Redis的缓存问题
大约 5 分钟
1. 缓存问题
Redis因为读写性能更强,往往用来环节数据库的压力,但是如果使用不当也会有很大的隐患
1.1 缓存穿透
在热点数据上,Redis用来缓存数据库的内容。缓存穿透是指当Redis和数据库中都没有目标数据时,此时的请求会不断尝试查询数据库而无法放入缓存,进而导致数据库性能下降甚至挂掉
解决方案:
- 接口鉴权:在接口层进行基本的验证,如鉴权、判断请求参数
- 空值缓存:将未查询到的请求设置为
key-null
,并设置较短的过期时间 - 布隆过滤器(BloomFilter):空值缓存如果存在很多空值的话内存压力还是蛮大的,布隆过滤器解决了这个问题,它其实也是
key-null
的思想,不过使用多个hash将key映射到bitmap中,极大减小了空间的占用
布隆过滤器由一系列Hash函数、一个固定大小的二进制向量或位图构成,支持 增加、查询,但在 不可删除。上图是一个长度为10的位数组,他的增、查、删过程如下:
- 增加:
baidu.com
经过3个不同的Hash函数计算,分别映射到了2、5、7上;同时,google.com
映射到了2、6、9上 - 查询:存在一定的误差,同样的输入经过Hash的计算后,如果对应的位上都为
1
则判定为存在;否则判定为不存在,因为可能有的位置被其他的key填充为1了,但所有位置都被填充的概率较低。所以对于查询的结果,不存在是可信的,存在是有一定误差的 - 删除:当两个
key
的映射存在相同的位时,此时如果删除其中一个key,将导致另外一个key也会不存在
1.2 缓存击穿
缓存击穿指缓存中没有数据,但数据库中有数据(比如说缓存过期了),这时如果大规模的请求同时涌入,将导致数据进入缓存之前就有大量的数据库请求。
解决方案主要有防止数据过期、限制SQL查询两种策略:
- 热点数据永不过期
- 逻辑过期:在value中存过期时间,当请求时如果过期则通过请求的参数重新构建Redis数据
- 互斥锁:基于DoubleCheck的思路,在第一次查询Redis后加锁,锁后进行第二次Redis查询和SQL查询
1.3 缓存雪崩
缓存雪崩指缓存大批量同时到过期时间,导致数据库查询数据量巨大
解决方案:
- 热点数据永不过期
- 设置随机的过期时间,防止同时过期
- 分布式缓存热点数据
1.4 缓存污染
缓存污染是指大量低频访问的缓存数据存活,消耗内存空间,如果内存临近限定值,则会触发Redis的淘汰策略,影响Redis性能。
Redis中存在8中缓存淘汰策略:
- 不淘汰
- noeviction:新写入的数据直接返回错误
Expire
数据参与淘汰- 随机:volatile-random,随机删除
- ttl:volatile-ttl,越早过期的数据随机的权重越大
- lru:volatile-lru,随机选取一个N大小的候选集,在候选集中删除最久未被使用的数据
- lfu:volatile-lfu,随机选取一个N大小的候选集,在候选集中删除访问次数最小的数据,但lfu只有8bit大小,最多记录到255次,如果N中很多数据都到了255,此时则退化为
lru
- 全部数据参与淘汰
- 随机:allkeys-random,随机删除
- lru:allkeys-lru,随机选取一个N大小的候选集,在候选集中删除最久未被使用的数据
- lfu:allkeys-lfu,随机选取一个N大小的候选集,在候选集中删除访问次数最小的数据,但lfu只有8bit大小,最多记录到255次,如果N中很多数据都到了255,此时则退化为
lru
对于lfu退化的问题,Redis也设置了lfu-log-factor(访问次数越大越不容易递增)、lfu-decay-time(访问次数根据最后一次访问时间衰减)等方案,防止快速到达255
2. 数据库和Redis的数据一致性
2.1 如何导致数据不一致
数据修改时,由于是并发情况,写入数据库和删除Redis的先后顺序难以保证,这进而导致了数据的不一致性
- 先删除Redis、后写入数据库:在线程A删除Redis后到写入数据库前,线程B再次查询,这将导致数据库的脏数据被再次写入Redis
- 先写入数据库、后删除Redis:在线程A写入数据库后被中断,此时Redis未被删除,一直存在脏数据
2.2 数据一致性
数据一致性包括以下三种等级:
- 强一致性:即立即响应修改的内容,查询和修改永远一致,这种情况不应使用缓存
- 弱一致性:不承诺修改后立即同步到Redis,也无法保证以后一定能同步
- 最终一致性:弱一致性的特例,保证一定时间后数据能达到一致性
2.3 同步策略
- 延时双删:先删除Redis、后写入数据库,随后再一定时间后再次删除Redis,防止脏数据在删除Redis和写入数据库之间再次被缓存
- 同步删除:更新数据库后,删除Redis
- 异步删除:更新数据库后,用mq通知Redis删除;也可以用数据库的binlog机制来触发mq
2.4 双写一致性
双写一致性指的是两个线程的数据库对同一key的value发生修改,发往Redis后无法判断请求的先后,进而导致缓存的数据不是最新版
解决方案:使用事务保证数据库和更新Redis的原子性