一、Redis缓存问题
- 缓存和数据库一致性问题
分布式环境下非常容易出现缓存和数据库一致性问题,如果想要缓存跟数据库强一致性,那就不要使用缓存。
我们只能通过采取合适的策略来降低缓存与数据库一致性问题的概率。
合适的策略包括合适的缓存更新策略、更新数据库后及时更新缓存、缓存失败时增加重试机制等。
二、缓存穿透、缓存击穿、缓存雪崩
(1)缓存穿透:指用户(黑客)不断请求缓存和数据库中都没有的数据。这样不断的攻击会导致数据库压力非常大,严重会击垮数据库。
解决办法:
1.在接口层增加校验,比如用户鉴权、参数做校验、不合法的校验直接return,比如id做基础校验,id<=0直接拦截。
2.高级用布隆过滤器(Bloom Filter):利用高效的数据结构和算法快速判断出你这个key是否在数据库中存在,不存在你return就好了,存在你就去查DB刷KV再return。
(2)缓存击穿:指一个Key非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个key的点上击穿了缓存。
解决办法:
设置热点数据永不过期,或者加上互斥锁就搞定了。
public static String getData(String key)throws InterruptedException {
//从Redis查询数据
String result = getDataByKV(key);
//参数校验
if (StringUtils.isBlank(result)) {
try {
//获得锁
if (reenLock.tryLock()) {
//去数据库查询
result = getDataByDB(key);
//校验
if (StringUtils.isNotBlank(result)) {
//插进缓存
setDataToKV(key, result);
}
} else {
//睡一会再拿
Thread.sleep(100L);
result = getData(key);
}
}finally {
//释放锁
reenLock.unlock();
}
}
return result;
}
(3)缓存雪崩:同一时间大面积的缓存失效,瞬间Redis跟没有一样,而且大并发的请求直接打到数据库上,数据库几乎是灾难性的。如果没有做熔断等策略,基本上就是瞬间挂一片的节奏。
解决办法:
1.在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效。
2.如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效。
3.或者设置热点数据永不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷新一下缓存就好了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。
三、Redis的内存回收机制
-
Redis的内存回收主要分为过期删除策略和内存淘汰策略两部分。
四、Redis的过期策略
策略 | 描述 |
定时过期 | 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。 该策略可以立即清除过期的数据,对内存很友好; 但是会占⽤用大量的CPU资源去处理理过期的数据,从而影响缓存的响应时间和吞吐量。 |
惰性过期 | 只有当访问一个key时,才会判断该key是否已过期,过期则清除。 该策略略可以最大化地节省CPU资源,却对内存非常不友好。 极端情况可能出现⼤量的过期key没有再次被访问,从而不会被清除,占⽤用大量内存。 |
定期过期 | 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。 该策略是前两者的一个折中⽅方案。 通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 (expires字典会保存所有设置了了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。) |
五、Redis淘汰策略
策略 | 描述 |
volatile-lru | 从已设置过期时间的KV集合中优先对最近最少使用(less recently used)的数据淘汰 |
volatile-ttl | 从已设置过期时间的KV集合中优先对剩余时间短(time to live)的数据淘汰 |
volatile-random | 从已设置过期时间的KV集合中随机选择数据淘汰 |
allkeys-lru | 从所有KV集合中优先对最近最少使用(less recently used)的数据淘汰 |
allkeys-random | 从所有KV集合中随便选择数据淘汰 |
noeviction | 不淘汰策略,若超过最大内存,返回错误信息(默认策略) |
- 备注:Redis 5默认使用noeviction淘汰策略,Redis 4.0加入了LFU(least frequency use)淘汰策略,包括volatile-lfu和allkeys-lfu,通过统计访问频率,将访问频率最少,即最不经常使用的KV淘汰。
六、Redis持久化
- Redis为了保证效率,数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化。
- Redis的持久策略有两种,一种是RDB,另一种是AOF。
(1)RDB:快照的形式直接把内存中的数据保存到一个dump文件中,定时保存,保存策略。
- RDB持久化策略原理:
1.Redis会生成一个子进程,子进程将内存中的数据写到磁盘上一个临时RDB文件。
2.当子进程完成写临时文件后,将原来在磁盘中的二进制文件dump.rdb替换掉。
这样的好处是可以copy-on-write(写时复制)。
- RDB优缺点:
优点:
非常适合于备份(比如:每个小时备份一次数据),即使遇上问题,也可以随时将数据还原到不同的版本。RDB非常适合灾难恢复。
缺点:
如果需要尽量避免在服务器故障时丢失数据,那么RDB无法全面恢复。
(2)AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。
- AOF原理:
每一个写命令都通过write函数追加到appendonly.aof中。
- AOF优缺点:
优点:
让Redis变成非常持久,可以设置不同的Fsync策略,AOF默认策略是每秒钟Fsync一次。(在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据)。
缺点:
AOF文件体积通常要大于RDB文件的体积。根据所使用的Fsync策略,AOF的速度可能会慢于RDB。
备注:
1.Redis默认是快照RDB的持久化。
2.当Redis重启的时候,他会优先使用AOF文件还原数据集,因为AOF文件保存的数据通常比RDB文件保存的数据集更加完整。(前提是开启了AOF配置)
3.关于如何选择哪种持久化策略,可依据以下情景:
(1)如果你可以承受数分钟内的数据丢失,那么可以只使用RDB持久化。定时生成RDB快照非常便于进行数据备份,并且RDB恢复数据集的速度要比AOF恢复的速度快。
(2)如果不能承受数分钟内的数据丢失,可以使用AOF,AOF将Redis执行的每一条命令追加到磁盘中,处理巨大的写入会降低Redis的性能。
(3)建议是将两者一起用,用AOF保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同程度的备份,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。
Redis讲解目录: