redis存在大量脏页问题的追查记录
from:https://www.zybuluo.com/SailorXiao/note/136014
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,如下:
# Memory used_memory:118140384 used_memory_human:112.67M used_memory_rss:19903766528 used_memory_peak:17871578336 used_memory_peak_human:16.64G used_memory_lua:31744 mem_fragmentation_ratio:168.48 mem_allocator:libc
于是用了pmap -x 27190 查看redis进程的内存映像信息,结果如下:
27190: ./redis-server ../redis.conf Address Kbytes RSS Dirty Mode Mapping 0000000000400000 548 184 0 r-x-- redis-server 0000000000689000 16 16 16 rw--- redis-server 000000000068d000 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 <bytes> # maxmemory-policy volatile-lru
如上所示,发现redis自身已经完全关闭持久化,只是作为cache使用,而且对于最大内存使用默认值(代表没有限制),内存的淘汰机制是volatile-lru。翻看redis的文档,查看对应的淘汰机制:
volatile-lru: 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰(默认值) volatile-ttl: 从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 volatile-random: 从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 allkeys-lru: 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 allkeys-random: 从数据集(server.db[i].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设定范围内
redis存在大量脏页问题的追查记录的更多相关文章
- Innodb刷脏页技术深度挖掘
DBA某数据库集群每日17:00左右会出现一个性能陡降的现象,在10~20秒内主库出现大量慢查询.这些查询本身没有性能问题,也没有任何关联,可以认为是由于数据库系统负载较重,由于并发导致的慢查询.通过 ...
- 【MySQL 读书笔记】SQL 刷脏页可能造成数据库抖动
开始今天读书笔记之前我觉得需要回顾一下当我们在更新一条数据的时候做了什么. 因为 WAL 技术的存在,所以当我们执行一条更新语句的时候是先写日志,后写磁盘的.当我们在内存中写入了 redolog 之后 ...
- MySQL:刷脏页
1. 脏页,干净页 当内存数据页和磁盘数据页上的内容不一致时,我们称这个内存页为脏页: 内存数据写入磁盘后,内存页上的数据和磁盘页上的数据就一致了,我们称这个内存页为干净页. 2. 刷脏页的时机 2. ...
- Checkpoint--查看各DB上的脏页
可以使用sys.dm_os_buffer_descriptors来看数据页在buffer pool中的状态,其中is_modified来标示数据页是否为脏页 --------------------- ...
- Mysql的刷脏页问题
平时的工作中,不知道你有没有遇到过这样的场景,一条 SQL 语句,正常执行的时候特别快,但是有时也不知道怎么回事,它就会变得特别慢,并且这样的场景很难复现,它不只随机,而且持续时间还很短. 当内存数据 ...
- MySQL中InnoDB脏页刷新机制Checkpoint
我们知道InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页.既然有重做日志保证数据持久性,查询时也可以直接从缓冲池页中 ...
- InnoDB Redo Flush及脏页刷新机制深入分析
概要: 我们知道InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页.既然有重做日志保证数据持久性,查询时也可以直接从缓 ...
- 面试题:了解MySQL的Flush-List吗?顺便说一下脏页的落盘机制!(文末送书)
Hi,大家好!我是白日梦! 今天我要跟你分享的MySQL话题是:"了解Flush-List吗?顺便说一下脏页的落盘机制!(文末送书)" 本文是MySQL专题的第 8 篇,共110篇 ...
- 【技巧】easyUI的datagrid,如何在翻页以后仍能记录被选中的行
easyUI的datagrid在复选框多选时,如何在翻页以后仍能记录被选中的行: 注意datagrid中需要配置idField属性,一般为数据的主键
随机推荐
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
Android中View的绘制过程 onMeasure方法简述 附有自定义View例子 Android中View的绘制过程 当Activity获得焦点时,它将被要求绘制自己的布局,Android fr ...
- Android Studio git 版本回退到最新的版本
1.场景 1.1 最新三次的提交 分别是:定义了一个变量k = 10 . 定义了一个变量 j = 6 . 定义了一个变量 i = 5 ; 本地仓库 和 远程仓库保持一致 1.2 我添加了一行代码 ...
- iOS定时器、延迟执行
1.通用方式(并不是实时调用并且会卡顿): // 一般用于更新一些非界面上的数据 [NSTimer scheduledTimerWithTimeInterval:时间间隔 target:self se ...
- 如何正确使用Cocoapods
➠更多技术干货请戳:听云博客 一.介绍Cocoapods Cocoapods是引入为项目引入新血液的接口,只有引入了新血液,功能才可以多样化,进而满足不同的消费群体.使用Cocoapods可以方便日后 ...
- 深入.net(继承)
继承.多态,学习目标: 1.能够熟记定义和概念 2.能够理解其内部意义 3.能够运用到自己的编码实现中 如何进行代码的复用: ----- 自己写好了一段代码,理想状态(永远都不要再写了,直接用) -- ...
- 【代码笔记】iOS-改变导航条标题的颜色为红色
一,效果图. 二,代码. RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional se ...
- iOS学习19之OC类的扩展
为一个类扩展功能:1.子类化:2.修改源代码:3.定义协议:4.Category:类目 1.Category 1> Category的作用 Category:也叫分类,类目,是为没有源代码的类扩 ...
- request,session,application
JSP 的3个内置对象request,session,application,其实都有一个作用域,这些对象内部有一个Map成员用于存放数据,比如session对象的setAttribute(key,v ...
- partproble在RHEL 6下无法更新分区信息
在RHEL5.x版本下面,在添加磁盘分区等操作后,一直使用partproble命令使内核重新读取分区表信息,从而不用重新启动.但是最近在RHEL 6(Red Hat Enterprise Linux ...
- 查看ORACLE的实际执行计划
ORACLE的执行计划分为预估执行计划和实际执行计划.其中,你用Toad.PL/SQL Developer.SQL Developer.EXPLAIN PLAN FOR或者SET ATUOTRACE ...