设为首页 收藏本站
查看: 802|回复: 0

[经验分享] redis存在大量脏页问题的追查记录

[复制链接]

尚未签到

发表于 2018-11-4 12:06:25 | 显示全部楼层 |阅读模式
case现场
  线上发现一台机器内存负载很重,top后发现一个redis进程占了大量的内存,TOP内容如下:
27190   root    20   0  18.6g   18g  600 S  0.3     59.2    926:17.83   redis-server  发现redis占了18.6G的物理内存。由于redis只是用于cache一些程序数据,觉得很不可思议,执行redis的info命令,发现实际数据占用只有112M,如下:
# Memoryused_memory:118140384used_memory_human:112.67Mused_memory_rss:19903766528used_memory_peak:17871578336used_memory_peak_human:16.64Gused_memory_lua:31744mem_fragmentation_ratio:168.48mem_allocator:libc  于是用了pmap -x 27190 查看redis进程的内存映像信息,结果如下:
27190:   ./redis-server ../redis.conf  
Address             Kbytes      RSS     Dirty           Mode        Mapping0000000000400000    548         184         0           r-x--       redis-server0000000000689000    16          16          16          rw---       redis-server000000000068d000    80          80          80          rw---       [ anon ]0000000001eb6000    132         132         132         rw---       [ anon ]0000000001ed7000    19436648    19435752    19435752    rw---       [ anon ]00007f5862cb2000    4           0           0           -----       [ anon ]
  发现存在大量的内存脏页。现在内存负载高的问题已经比较清晰了,是由于redis的脏页占用了大量的内存引起的。可是,redis为什么会存在那么多的脏页呢?
case 分析
  看了下linux脏页的定义:
脏页是linux内核中的概念,因为硬盘的读写速度远赶不上内存的速度,系统就把读写比较频繁的数据事先放到内存中,以提高读写速度,这就叫高速缓存,linux是以页作为高速缓存的单位,当进程修改了高速缓存里的数据时,该页就被内核标记为脏页,内核将会在合适的时间把脏页的数据写到磁盘中去,以保持高速缓存中的数据和磁盘中的数据是一致的。  也就是说,脏页是因为内存中的很多数据没来得及更新到磁盘导致的。看了下linux系统的脏页flush机制:
  http://blog.chinaunix.net/uid-17196076-id-2817733.html 发现跟内存flush的可以进行设置(/proc/sys/vm底下)
dirty_background_bytes/dirty_background_ratio:  
    - 当系统的脏页到达一定值(bytes或者比例)后,启动后台进程把脏页刷到磁盘中,此时不影响内存的读写(当bytes设置存在时,ratio是自动计算的)
  

  
dirty_bytes/dirty_ratio:
  
    - 当系统的脏页到达一定值(bytes或者比例)后,启动进程把脏页刷到磁盘中,此时内存的写可能会被阻塞(当bytes设置存在时,ratio是自动计算的)
  

  
dirty_expire_centisecs:
  
    - 当内存和磁盘中的数据不一致存在多久以后(单位为百分之一秒),才会被定义为dirty,并在下一次的flush中被清理。不一致以磁盘中文件的inode时间戳为准
  

  
dirty_writeback_centisecs:
  
    - 系统每隔一段时间,会把dirty flush到磁盘中(单位为百分之一秒)
  查看当前系统的flush配置,发现没问题,dirty_background_ratio为10%,dirty_ratio为20%,dirty_writeback_centisecs为5s,dirty_expire_centisecs为30s,可是为啥redis的脏页没有被flush到磁盘中呢?
  一般脏页是要把内存中的数据flush到磁盘中,那么会不会是redis的持久化导致了脏页呢?查看下redis关于这些方面的配置:
rdb持久化已经被关闭# save 900 1# save 300 10# save 60 10000# append持久化也被关闭appendonly no# 最大内存设置、内存替换策略都是默认值# maxmemory # maxmemory-policy volatile-lru  如上所示,发现redis自身已经完全关闭持久化,只是作为cache使用,而且对于最大内存使用默认值(代表没有限制),内存的淘汰机制是volatile-lru。翻看redis的文档,查看对应的淘汰机制:
volatile-lru:      从已设置过期时间的数据集(server.db.expires)中挑选最近最少使用的数据淘汰(默认值)volatile-ttl:      从已设置过期时间的数据集(server.db.expires)中挑选将要过期的数据淘汰volatile-random:   从已设置过期时间的数据集(server.db.expires)中任意选择数据淘汰allkeys-lru:       从数据集(server.db.dict)中挑选最近最少使用的数据淘汰allkeys-random:    从数据集(server.db.dict)中任意选择数据淘汰no-enviction:      禁止驱逐数据  而在当前的使用环境中,程序对redis的使用是当做cache,并且会对数据设置expire超时时间,到期后等待redis进行删除的。那么脏页的原因,是不是因为过期数据清理机制的问题呢(比如清理不及时等)?因此,需要查看redis在对过期数据进行删除时采取的策略,参考信息如下:
  Redis中的内存释放与过期键删除 redis 过期键的清除
  redis过期键删除机制:
惰性删除:  
    -  expire的键到期后,不会自动删除,不过在每次读取该键时进行检查,检查该键是否已经过期,如果过期,则进行删除动作。这样可以保证删除操作只会在非做不可的情况下进行
  

  
定期删除:
  
    - 每隔一段时间执行一次cron删除操作,从每个db的expire-keys中随机找出一定数量的key(默认是20个),检查key是否超时。如果已经超时,则进行删除
  
    - 通过限制删除操作执行的时长和频率,并以此来减少删除操作对 CPU 时间的影响。
  

  
大于maxmemory的自动删除:
  
    - 每次client和server进行command交互时,server都会检查maxmemory的使用情况
  
    - 如果当前的内存已经超过了max-memory,那么则进行清理,直到内存占用在maxmemory线以下
  
    - 清理的策略基于淘汰机制(LRU,TTL,RANDOM等)
  redis对于过期键删除使用的是 惰性删除 + 定期删除 + 大于maxmemory的自动清除 的策略。
case 定位
  通过以上的分析,问题已经比较明确了,原因如下:

  •   由于某种原因,redis使用的内存越来越大(可能是由于惰性删除,导致expire的数据越积越多,或者其它原因,具体原因取决于redis内部的实现)
  •   redis由于只当做cache,并没有实际读写文件,因此操作系统并不会帮它flush到磁盘中(因为没有地方可以flush)
  •   由于redis没有设置maxmemory,因此默认的是机器的内存大小,只有当redis自身使用的内存达到机器内存大小时,redis才会自身进行清理(volatile-lru机制)
  •   因此当前的redis的内存越来越大,而且脏页数据越来越多(大部分可能都是已经过期的数据)
case解决
  为了解决这个问题,比较方便且合理的方法就是:

  •   惰性删除有时候并不是很靠谱,特别对于一些log类型的数据,在写入redis后就不管了,虽然设置了超时时间,但是没有效果
  •   定期删除对于expire-keys也不是很靠谱,存在随机性,也可能过期很久的数据没有删除
  •   相对而言,比较合理的方式是基于使用情况设置redis的maxmemory大小,用于让redis实现自身的数据清理机制,确保把mem限制在maxmemory设定范围内



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-630638-1-1.html 上篇帖子: redis讲解 下篇帖子: redis-cluster 之cluster-require-full-coverage
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表