Redis常见问题总结:
- Redis常见问题总结
- Redis缓存预热
- Redis缓存雪崩
- Redis缓存击穿
- Redis缓存穿透
- Redis 中 key 的过期删除策略
- 数据删除策略
- Redis内存淘汰策略
- 一、Redis对过期数据的处理
- (一)相关配置
- (二)内存淘汰流程
- (三)动态改配置命令
- 二、内存淘汰策略的种类及特点
- (一)Redis 4.0以前的策略
- (二)Redis 4.0以后增加的策略
- 三、常见的淘汰算法
- (一)FIFO算法(先进先出)
- (二)LRU算法(最近最少使用)
- (三)LFU算法(最不常使用)
- (四)LRU和LFU的选择
- 总结
Redis常见问题总结
Redis缓存预热
“宕机”服务器启动后迅速宕机,指的是服务刚上线由于Redis中没有任何数据,导致大量请求访问数据库,主从之间数据吞吐量较大,数据同步操作频度较高。
而缓存预热解决的就是,在系统启动前,提前将相关的数据直接加载到缓存系统。避免用户在请求的时候,先查询数据库,再将数据库的查询到的数据缓存到Redis的问题。用户可以直接查询事先被预热的缓存数据。
解决方案:
- 准备工作:
- 日常例行统计数据访问记录,统计访问频度较高的热点数据。
- 将统计结果中的数据分类,根据级别,Redis优先加载级别较高的热点数据
- 实施:
- 使用脚本程序固定触发数据预热过程
- 如果条件允许,使用CDN(内容分发网络),效果会更好
Redis缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。【这里指的是同一批key的过期时间相同或者Redis服务挂掉】
解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
Redis缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击
常见的解决方案有两种:
- 互斥锁
- 逻辑过期
互斥锁:
逻辑过期:
Redis缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库
常见两种解决方案:
- 缓存空对象
- 布隆过滤器
Redis 中 key 的过期删除策略
数据删除策略
什么是过期数据?
Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
- XX :具有时效性的数据
- -1 :永久有效的数据
- -2 :已经过期的数据或被删除的数据或未定义的数据
注意:过期的数据真的删除了吗?不是的
删除策略比对
1. 定时删除 节约内存,无占用 不分时段占用CPU资源,频度高拿时间换空间
2. 惰性删除 内存占用严重延时执行,CPU利用率高拿空间换时间
3. 定期删除 内存定期随机清理每秒花费固定的CPU资源维护内存随机抽查,重点抽查
Redis的过期键的删除策略是指当Redis中的缓存的key过期了,Redis要如何处理。
Redis中提供了三种删除策略:
1.定时删除
当放入数据后,设置一个定时器,当定时器读秒完毕后,将对应的数据从dict中删除。
优点: 内存友好,数据一旦过期就会被删除
缺点: CPU不友好,定时器耗费CPU资源,并且频繁的执行清理操作也会耗费CPU资源。用时间换空间
2.惰性删除
当数据过期的时候,不做任何操作。当访问数据的时候,查看数据是否过期,如果过期返回null,并且将数据从内存中清除。如果没过期,就直接返回数据。
优点: CPU友好,数据等到过期并且被访问的时候,才会删除。
缺点: 内存不友好,会占用大量内存。用空间换时间
3.定期删除
定期删除是定时删除和惰性删除的折中方案。每隔一段时间对redisServer中的所有redisDb的expires依次进行随机抽取检查。
Redis中有一个server.hz定义了每秒钟执行定期删除的次数,每次执行的时间为250ms/server.hz。Redis中会维护一个current_db变量来标志当前检查的数据库。current_db++,当超过数据库的数量的时候,会重新从0开始。
定期检查就是执行一个循环,循环中的每轮操作会从current_db对应的数据库中随机依次取出w个key,查看其是否过期。如果过期就将其删除, 并且记录删除的key的个数。如果过期的key个数大于w25%,就会继续检查当前数据库,当过期的key小于w25%,会继续检查下一个数据库。当执行时间超过规定的最大执行时间的时候,会退出检查。一次检查中可以检查多个数据库,但是最多检查数量是redisServer中的数据库个数,也就是最多只能从当前位置检查一圈。
Redis内存淘汰策略
一、Redis对过期数据的处理
Redis的内存淘汰机制主要是用于在Redis用于缓存的内存不足时,处理需要新写入且需申请额外空间的数据。
(一)相关配置
- 在
redis.conf
中:- 配置
maxmemory <bytes>
,用于设置Redis的最大内存空间。若不设定该参数,默认无限制,通常会设定为物理内存的四分之三。 - 配置
maxmemory-policy noeviction
,用于设置淘汰策略,默认为noeviction
。
- 配置
(二)内存淘汰流程
- 客户端发起需要申请更多内存的命令(如
set
)。 - Redis检查内存使用情况,若已使用内存大于
maxmemory
,则根据用户配置的不同淘汰策略来淘汰内存(key),以换取一定的内存。 - 若上述步骤均无问题,则该命令执行成功。
(三)动态改配置命令
Redis支持动态改配置,无需重启。
- 设置最大内存:
config set maxmemory 100000
- 设置淘汰策略:
config set maxmemory-policy noeviction
二、内存淘汰策略的种类及特点
(一)Redis 4.0以前的策略
- noeviction:当内存使用超过配置时会返回错误,不会驱逐任何键。(默认选项,一般不选用)
- allkeys-lru:当内存不足以容纳新写入数据时,在整个键空间中,移除最近最少使用的key。(最常用)
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- allkeys-random:当内存不足以容纳新写入数据时,在整个键空间中,随机移除某个key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
(二)Redis 4.0以后增加的策略
- volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键。
- allkeys-lfu:从所有键中驱逐使用频率最少的键。
内存淘汰策略可通过配置文件修改,redis.conf
对应的配置项是maxmemory-policy
,修改对应的值即可。
三、常见的淘汰算法
(一)FIFO算法(先进先出)
- 思想
- 基于队列的先进先出原则,最先进入的数据会被最先淘汰掉,是最简单、最公平的思想。
- 实现
- 维护一个FIFO队列,按照时间顺序将各数据(已分配页面)链接起来组成队列,并将置换指针指向队列的队首。进行置换时,把置换指针所指的数据(页面)顺次换出,并把新加入的数据插到队尾即可。
- 缺点
- 会导致缺页率增加。随着分配页面的增加,被置换的内存页面往往是被频繁访问的,因此FIFO算法会使一些页面频繁地被替换和重新申请内存,从而导致缺页率增加。由于缺页率会随着分配页面的增加而增加,使得redis的开销也逐渐增加,所以这种算法已不再使用。
(二)LRU算法(最近最少使用)
- 思想
- 最近最少使用的会被优先淘汰。如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。当空间满时,最久没有访问的数据最先被淘汰掉。
- 实现
- 用双向链表(LinkedList)+哈希表(HashMap)实现(链表用来表示位置,哈希表用来存储和查找),在Java里有对应的数据结构
LinkedHashMap
。
- 用双向链表(LinkedList)+哈希表(HashMap)实现(链表用来表示位置,哈希表用来存储和查找),在Java里有对应的数据结构
- 缺点
- 在需要淘汰时,只是随机选取有限的key进行对比,排除掉访问时间最久的元素,不能选择整个候选元素的最优解,只是局部最优。默认随机选取的key的数目为5,在配置文件
redis.conf
中由maxmemory_samples
属性的值决定,采样数量越大越接近于标准LRU算法,但也会带来性能的消耗。 - 在Redis 3.0以后增加了LRU淘汰池,进一步提高了与标准LRU算法效果的相似度。淘汰池即维护的一个数组,数组大小等于抽样数量
maxmemory_samples
,在每一次淘汰时,新随机抽取的key和淘汰池中的key进行合并,然后淘汰掉最旧的key,将剩余较旧的前面5个key放入淘汰池中待下一次循环使用。假如maxmemory_samples = 5
,随机抽取5个元素,淘汰池中还有5个元素,相当于变相的maxmemory_samples = 10
,所以进一步提高了与LRU算法的相似度。
- 在需要淘汰时,只是随机选取有限的key进行对比,排除掉访问时间最久的元素,不能选择整个候选元素的最优解,只是局部最优。默认随机选取的key的数目为5,在配置文件
(三)LFU算法(最不常使用)
- 思想
- 如果一个数据在最近一段时间很少被访问到,那么在将来它被访问的可能性也很小。当空间满时,最小频率访问的数据最先被淘汰。
- 实现及问题解决
- 为每个key维护一个计数器,每次key被访问时,计数器增大,计数器越大,则认为访问越频繁。但存在以下问题:
- 可能存在某个key在开始一个小时内有100万的访问量,但之后一小时内访问量为0,而在第二个小时内另一个key的访问量达到20万,此时第二个小时内key1会优先于key2被淘汰,尽管key2在该小时内访问量更大。
- 当新加入的key,由于没有被访问过,初始计数器为0,若此时触发淘汰机制,会把最先添加的key最先淘汰掉。
- 解决方案:在LFU算法中维护了一个24bit的字段,被分成16 bits与8 bits两部分。第一部分(高16 bits)用来记录计数器的上次缩减时间,时间戳,单位精确到分钟。第二部分(低8 bits)用来记录计数器的当前数值,该数值反映访问频率,而非次数。
- 在
redis.conf
配置文件中,lfu-log-factor
用来调整计数器counter的增长速度,lfu-log-factor
越大,counter增长越慢。lfu-decay-time
是一个以分钟为单位的数值,用来调整counter的缩减速度。
- 为每个key维护一个计数器,每次key被访问时,计数器增大,计数器越大,则认为访问越频繁。但存在以下问题:
(四)LRU和LFU的选择
需要根据业务权衡到底是选择淘汰最近最少使用(LRU)还是选择最不经常使用(LFU)。总的来说,无论是LRU、LFU、TTL还是Random都是近似算法来实现的,在可靠性和性能上做了一定的平衡。在业务中应主动删除没有价值的数据,或者更新某些key的过期时间等来提高Redis的性能和空间,不能过分依赖于淘汰策略。
总结
Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时需要申请额外空间的数据;过期策略用于处理过期的缓存数据。要根据实际业务场景和需求合理选择内存淘汰策略,并结合主动的数据管理操作来优化Redis的性能和内存使用。