过期回收策略
Redis的key到达过期时间就会被回收,可以想象如果同一时间太多key过期,那么这个回收任务会忙不过来,回收会占据线程的处理时间,如果回收太过于频繁,会导致线上读写卡顿。那么Redis是如何处理这个问题呢
过期的key的集合
Redis中所有设置了TTL的key,都会放入到一个专门的字典中,检测过期的策略有:
- 定时遍历字典中的所有时间
- 惰性删除(访问的时候,发现过期就删除)
前者是集中处理,后者是零散处理。
定时扫描策略
Redis默认每秒做10次过期扫描,过期扫描不遍历过期字典的所有key,而是使用了如下规则
1. 从过期字典中随机选20key
2. 删除20key中已过期的
3. 如果过期key比例超过1/4,那么重复第一步,否则结束
同时为了每次遍历不会出现死循环,限制了循环最多占用25ms。
可以想象,如果同一时间大量key同时过期(雪崩),上面这个循环会打满25ms,然后下一次循环的时候又占满25ms。由于1s进行10次回收,那么1s就会有250ms在进行回收工作。为了解决这个问题,通常的做法是TTL在指定的时候,需要增加一个随机数。
从库的过期策略
从库的过期策略是被动的过期,是由于主库发生的过期可以被动地同步到从库。
淘汰
内存使用量如果超过了物理内存限制,会和磁盘开始进行交换,这个是Redis系统不允许的,所以在内存使用量超过阈值时,Redis会启动淘汰机制。注意,如果使用了淘汰机制,大概率Redis是用于缓存目的
Redis有很多淘汰方案,可配置,有下面几种:
- noeviciton不淘汰,默认策略,这样可以保证数据不丢失,但线上业务不可用
- volatile-lru:对于设置了过期时间的key,使用lru算法
- volatile-ttl:对设置了过期时间的,根据过期时间淘汰,离过期时间越近的先淘汰
- volatile-random:对设置了过期时间的,随机淘汰
- allkeys-lru:对所有key,使用lru算法淘汰
- allkeys-random:对所有key,随机淘汰
LRU
基本原理是可以通过hashmap和一个双向链表实现一个LRU算法
- hash存放的是key和value对应的链表节点
- 链表中存放的是value
- setkey会进入到链表的头,如果超过了限制,会将尾部去掉
- 被查询的数据,会被提到链表的头
Redis的近似LRU算法
LRU会使用大量的内存,Redis中没有使用标准的LRU算法,而是采用了近似LRU算法(其实可以理解为一个策略),具体做法是
- 随机采样5个key,淘汰掉最旧的 (这里需要看配置是淘汰策略是allkey还是volatile)
- 如果淘汰后还是超过MaxMemeory,则继续采样淘汰