Redis

89 阅读4分钟

1. 缓存失效

1.1 缓存穿透

  • 原理:每次访问都是一个数据库和缓存中都不存在的数据,所以每次的请求都会直接直接访问到数据库中。
  • 解决方案:
    • 接口效验:可以在最外层做一层身份效验或数据校验等。比如商品的ID都是正整数,则可以对非正整数直接过滤等等。
    • 缓存空值:当缓存和数据库中都没有值时,将空值写入到缓存中,然后设置较短的过期时间。
    • 布隆过滤器:

1.2 缓存击穿

  • 原理:一个热点key,在缓存过期的一瞬间,同时有大量的请求过来,这时大量的请求都会直接进入到数据库中,造成数据库的压力剧增。
  • 解决方案:
    • 增加互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并到数据库执行数据库查询操作,其他线程就会拿不到锁就会陷入等待,直到第一个线程将数据写入到缓存后,直接走缓存。
    • 逻辑过期:直接将缓存设置为不过期,然后增加一个逻辑过期字段。
      • 当热点key过期后,就增加互斥锁只让重新开启一个线程去更新缓存,在更新之前的其它请求得到的是旧数据,更新完成后释放锁。

image.png

1.3 缓存雪崩

  • 原理:大量热点key同时过期了,导致缓存同一时刻全部失效,造成数据库压力剧增,引起雪崩。
    • 过期时间打散:给缓存的过期时间打上一个随机值,让每个key的过期时间分布开来,让大量的热点key不会同时失效。
    • 增加互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并到数据库执行数据库查询操作,其他线程就会拿不到锁就会陷入等待,直到第一个线程将数据写入到缓存后,直接走缓存。
    • 多级缓存:给业务添加多级缓存。
    • 给缓存业务添加降级限流策略。
  • 与缓存击穿的区别:缓存击穿是一个热点 key,缓存雪崩是一组热点 key。

互斥锁

使用setnx:将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作

2. String结构和Hash结构

image.png

3. 缓存更新策略

image.png

  • 低一致性需求:使用内存淘汰机制。例如商品类型的查询缓存,很少发生变化。
  • 高一致性需求:主动更新,并以超时上出作为兜底方案。例如店铺详情查询缓存。

3.1 主动更新缓存时有三个问题需要考虑:

  1. 删除缓存还是更新缓存?
    • 更新缓存:每次跟新数据都更新缓存。缺点:当写多读少时,无效操作会增加。
    • 删除缓存:更新数据库时让缓存失效,查询时再更新数据。适合读多写少
  2. 如何保证缓存与数据库的操作同时成功或失败
    • 单体系统:将缓存与数据库操作放在一个事务。
    • 分布式系统:利用TCC等分布式事务方案。
  3. 先删除缓存还是操作数据库
    • 先删除缓存,再操作数据库。
    • 先操作数据库,再删除缓存。(更好)

为什么先操作数据库,再删除缓存更好?

  • 因为先操作数据库,再删除缓存出现缓存不一致的概率更低。更新时删除缓存,没命中缓存时写入缓存,一个线程查,一个线程写。
  • 想要出现缓存不一致的条件如下:
    1. 缓存正好失效。
    2. 两个线程需要并行执行。
    3. 当其中一个线程在查询数据库后写入缓存的极小时间内,另一个线程跟新了数据库,并删除缓存。
  • 解决办法:超时剔除。 image.png

4. Redis 事务是否具备原子性?

  • Redis事务执行过程中,如果一个命令执行出错,那么就返回错误,然后还是会接着继续执行下面的命令,之前执行成功的也不会回滚。
  • Redis在执行事务之前会检查命令不存在或者是命令参数不对,如果出现这种情况就会直接返回错误,当时一些程序员的逻辑错误是不支持,例如对 String 类型的数据库键执行对 HashMap 类型的操作!