生产环境的问题:春节前后,redis内存占用率持续飙升
从业务端排查,并没有大量的新增key缓存,但是redis监控上能看到增加了几百万的key。
因为key数量达到了6000万,怀疑是过期key不能及时释放内存导致。
粗暴验证:
在低峰时段执行keys xxxx*命令遍历key,几秒后kill掉命令,发现redis key数量减少了,内存占用也降低了,说明问题原因和猜测的一致。
延展问题:
Redis里面如果有大量的key,怎样才能高效的找出过期的key并将其删除呢,难道是遍历每一个key吗(keys命令确实能触发内存释放)?假如同一时期过期的key非常多,Redis会不会因为一直处理过期事件,而导致读写指令的卡顿?
答:
keys *命令确实会阻塞其他命令的执行,导致慢查询。
那Redis是怎么解决的呢?
Redis4.0新增了lazy free特性,从根本上解决Big Key(主要指定元素较多集合类型Key)删除的风险。lazy free可译为惰性删除或延迟释放;当删除键的时候,redis提供异步延时释放key内存的功能,把key释放操作放在bio(Background I/O)单独的子线程(客户端读写操作使用的是另一个线程,除了CPU可能有争抢,两者互不影响)处理中,减少删除big key对redis主线程的阻塞。有效地避免删除big key带来的性能和可用性问题。
Redis使用惰性删除+定期删除相结合的方式处理过期的key。
惰性删除
所谓懒惰删除就是在客户端访问一个key的时候,redis会对key的过期时间进行检查,如果过期了就立即删除。
优点:不会占用太多的额外CPU资源,只在访问key的时候检查过期时间并删除。
缺点:如果一个过期的key长时间没有被访问,那这个key就会一直存留在内存之中,占用内存资源。
定期删除
定期删除的原理是,Redis会将所有设置了过期时间的key放入一个字典中,然后每隔一段时间从字典中随机取一些key检查过期时间并删除已过期的key。
Redis默认每秒进行10次过期扫描:
从过期字典中随机20个key
删除这20个key中已过期的
如果超过25%的key过期,则重复第一步
同时,为了保证不出现循环过度的情况,Redis还设置了扫描的时间上限,默认不会超过25ms。
既然是随机访问key,能保证所有的过期key都被访问到吗?
redis删除过期key的算法_深入理解Redis的内存回收机制_许吴倩的博客-CSDN博客
参考:
定期删除功能源码
https://github.com/redis/redis/blob/23325c135f08365d1b7d4bf4fb1c9187fc7374b9/src/expire.c
定期删除功能源码解读
Redis源码剖析之数据过期(expire) - xindoo - 博客园
回到生产问题
让过期key及时释放内存的正确姿势
hz参数
The frequency at which Redis background tasks are performed. A higher value results in higher CPU consumption but smaller latency. We recommend that you do not specify a value larger than 100.
Redis 后台任务的执行频率。 较高的值会导致较高的 CPU 消耗但较小的延迟。 建议您不要指定大于 100 的值。
hz值每次调整增加30或40,观察CPU使用率、内存使用率、key数量变化情况、P99执行时延
因为缓存key非常多,约6000万左右,逐步增大hz到250左右,才有比较明显的效果。
修改后,cpu使用率仍然2%,P99执行时延保持不变,每分钟key过期数不再增加,说明再加大hz,已经没什么用了。
此时,key总数量和内存使用率已经在稳步下降了,问题得到解决!