Redis缓存回收及常见问题解决方案Redis缓存回收策略
Redis提供了多种内存回收策略,当内存使用达到maxmemory限制时,会根据配置的策略进行数据回收,主要策略包括:
- noeviction:当内存限制达到时,客户端尝试执行会让更多内存被使用的命令(大部分写入指令,DEL和几个例外除外)会返回错误。
- allkeys-lru:回收最少使用的键(LRU算法),适用于所有键,使得新添加的数据有空间存放。
- volatile-lru:回收最少使用的键(LRU算法),但仅限于在过期集合的键。
- allkeys-random:随机回收键,适用于所有键,使得新添加的数据有空间存放。
- volatile-random:随机回收键,但仅限于在过期集合的键。
- volatile-ttl:回收过期集合中的键,优先回收存活时间(TTL)较短的键。
- volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键。
- allkeys-lfu:从所有键中驱逐使用频率最少的键。
策略选择建议:
- allkeys-lru:适用于请求符合幂定律分布(部分子集元素被访问更多)的场景,若不确定选择何种策略,这是一个不错的选择。
- allkeys-random:适用于循环访问或请求分布均匀(所有元素被访问概率相近)的场景。
- volatile-ttl:适用于希望通过设置TTL值来决定哪些对象应被过期的场景。
回收进程工作原理:
- 客户端执行新命令添加数据。
- Redis检查内存使用情况,若超过maxmemory限制,则根据设定策略进行回收。
- 不断重复上述过程,保持内存使用在限制范围内。
缓存穿透、击穿、雪崩及预热解决方案
缓存穿透定义:查询一个一定不存在的数据,由于缓存不命中且不写入缓存,导致每次请求都需查询存储层,在流量大时可能压垮数据库。
解决方案:
- 布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,拦截不存在的数据请求。
- 缓存空结果:将查询返回的空结果(数据不存在或系统故障)进行缓存,设置较短过期时间(最长不超过五分钟)。
缓存击穿定义:针对设置了过期时间的热点key,在缓存过期时,大量并发请求同时访问该key,导致请求全部转发到数据库,可能压垮数据库。
解决方案:
- 分散过期时间:在原有失效时间基础上增加一个随机值(如1-5分钟随机),降低缓存过期时间重复率,避免集体失效。
- 加锁或队列:使用加锁或队列方式保证缓存的单线程(进程)写,避免失效时大量并发请求落到底层存储系统。
缓存雪崩定义:设置缓存时采用相同过期时间,导致缓存在某一时刻同时失效,请求全部转发到数据库,造成数据库瞬时压力过重。
解决方案:
- 互斥锁(mutex key):
在缓存失效时,先使用SETNX等操作设置一个mutex key。
若操作返回成功,再进行数据库加载数据并回设缓存;否则重试获取缓存。
- 提前使用互斥锁:
在value内部设置一个比实际缓存过期时间短的超时值(timeout1)。
当从缓存读取到timeout1发现过期时,延长timeout1并重新设置到缓存,再从数据库加载数据并设置到缓存。
- 永远不过期:
物理不过期:从Redis角度看,不设置过期时间。
逻辑过期:将过期时间存在key对应的value里,通过后台异步线程进行缓存构建。
缓存预热定义:系统启动前,提前将相关数据加载到缓存中,避免系统启动后因缓存未命中导致大量请求直接访问数据库。
解决方案:
- 手动加载:系统启动时,通过脚本或程序手动将热点数据加载到缓存。
- 定时任务:设置定时任务,在系统低峰期自动将热点数据加载到缓存。
总结
- 穿透:缓存不存在,数据库不存在,高并发,少量key。可通过布隆过滤器或缓存空结果解决。
- 击穿:缓存不存在,数据库存在,高并发,少量key。可通过分散过期时间或加锁解决。
- 雪崩:缓存不存在,数据库存在,高并发,大量key。可通过互斥锁、提前使用互斥锁或永远不过期策略解决。
- 预热:系统启动前提前加载热点数据到缓存,避免启动后大量请求直接访问数据库。