过期策略
数据删除策略的目标
在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或内存泄露 。
定时删除
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
优点:节约内存,到时就删除,快速释放掉不必要的内存占用
缺点: CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量
总结:用处理器性能换取存储空间(拿时间换空间)
Redis 默认会每秒进行十次过期扫描,过期扫描不会遍历过期字典中所有的 key,而是
采用了一种简单的贪心策略。
1、从过期字典中随机 20 个 key;
2、删除这 20 个 key 中已经过期的 key;
3、如果过期的 key 比率超过 1/4,那就重复步骤 1;
同时,为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms。
设想一个大型的 Redis 实例中所有的 key 在同一时间过期了,会出现怎样的结果?
毫无疑问,Redis 会持续扫描过期字典 (循环多次),直到过期字典中过期的 key 变得稀
疏,才会停止 (循环次数明显下降)。这就会导致线上读写请求出现明显的卡顿现象。导致这
种卡顿的另外一种原因是内存管理器需要频繁回收内存页,这也会产生一定的 CPU 消耗。
也许你会争辩说“扫描不是有 25ms 的时间上限了么,怎么会导致卡顿呢”?这里打个
比方,假如有 101 个客户端同时将请求发过来了,然后前 100 个请求的执行时间都是
25ms,那么第 101 个指令需要等待多久才能执行?2500ms,这个就是客户端的卡顿时间,
是由服务器不间断的小卡顿积少成多导致的。
所以业务开发人员一定要注意过期时间,如果有大批量的 key 过期,要给过期时间设置
一个随机范围,而不能全部在同一时间过期。
# 在目标过期时间上增加一天的随机时间
redis.expire_at(key, random.randint(86400) + expire_ts)
在一些活动系统中,因为活动是一期一会,下一期活动举办时,前面几期的很多数据都
可以丢弃了,所以需要给相关的活动数据设置一个过期时间,以减少不必要的 Redis 内存占
用。如果不加注意,你可能会将过期时间设置为活动结束时间再增加一个常量的冗余时间,
如果参与活动的人数太多,就会导致大量的 key 同时过期。
掌阅服务端在开发过程中就曾出现过多次因为大量 key 同时过期导致的卡顿报警现象,
通过将过期时间随机化总是能很好地解决了这个问题。
惰性删除
数据到达过期时间,不做处理。等下次访问该数据时
如果未过期,返回数据
发现已过期,删除,返回不存在
优点:节约CPU性能,发现必须删除的时候才删除
缺点:内存压力很大,出现长期占用内存的数据
总结:用存储空间换取处理器性能(拿时间换空间)
定期删除
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
特点1: CPU性能占用设置有峰值,检测频度可自定义设置
特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理
总结:周期性抽查存储空间(随机抽查,重点抽查)
Redis启动服务器初始化时,读取配置server.hz的值,默认为10
每秒钟执行server.hz次serverCron()
activeExpireCycle()对每个expires[*]逐一进行检测,每次执行250ms/server.hz
对某个expires[*]检测时,随机挑选W个key检测
如果key超时,删除key
如果一轮中删除的key的数量>W*25%,循环该过程
如果一轮中删除的key的数量≤W*25%,检查下一个expires[*], 0-15循环
W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
参数current_db用于记录activeExpireCycle() 进入哪个expires[*] 执行
如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行
等看书老钱的Redis深度历险,在回来补充一下。