跳至主要內容

Redis的缓存问题

pptg大约 5 分钟

1. 缓存问题

Redis因为读写性能更强,往往用来环节数据库的压力,但是如果使用不当也会有很大的隐患

1.1 缓存穿透

在热点数据上,Redis用来缓存数据库的内容。缓存穿透是指当Redis和数据库中都没有目标数据时,此时的请求会不断尝试查询数据库而无法放入缓存,进而导致数据库性能下降甚至挂掉

解决方案:

  1. 接口鉴权:在接口层进行基本的验证,如鉴权、判断请求参数
  2. 空值缓存:将未查询到的请求设置为key-null,并设置较短的过期时间
  3. 布隆过滤器(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查询两种策略:

  1. 热点数据永不过期
  2. 逻辑过期:在value中存过期时间,当请求时如果过期则通过请求的参数重新构建Redis数据
  3. 互斥锁:基于DoubleCheck的思路,在第一次查询Redis后加锁,锁后进行第二次Redis查询和SQL查询

1.3 缓存雪崩

缓存雪崩指缓存大批量同时到过期时间,导致数据库查询数据量巨大

解决方案:

  1. 热点数据永不过期
  2. 设置随机的过期时间,防止同时过期
  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 同步策略

  1. 延时双删:先删除Redis、后写入数据库,随后再一定时间后再次删除Redis,防止脏数据在删除Redis和写入数据库之间再次被缓存
  2. 同步删除:更新数据库后,删除Redis
  3. 异步删除:更新数据库后,用mq通知Redis删除;也可以用数据库的binlog机制来触发mq

2.4 双写一致性

双写一致性指的是两个线程的数据库对同一key的value发生修改,发往Redis后无法判断请求的先后,进而导致缓存的数据不是最新版

解决方案:使用事务保证数据库和更新Redis的原子性