Redis八股小记
如果我要求自己什么都学会,我在学习过程中就会担心其他方面的知识,最后我什么都得不到。
与其贪多求全,不如脚踏实地,把已经接触过的内容学懂学透。唯有打下坚实的基础,才能在未来逐步拓展,不至于浮于表面。
1.为什么是先写库再删缓存来保证一致性?如果在删缓存之前服务挂了怎么办?
先删除缓存再写库会怎么样?
删除缓存后,还没写库时,其他线程过来读数据 → 缓存 miss → 去数据库读到旧值 → 把旧值写回缓存 → 缓存和数据库又不一致了。
假设线程1先执行删除缓存,之后要更新数据库。但是在这之间,插入了线程2,线程2查询缓存未命中,接着查询数据库,并写入缓存,此时线程1更新数据库,那么就发生了缓存与数据库数据不一致的情况了。而且这种情况发生的概率比较大,因为查询缓存写缓存是相当快的。
先写库再删缓存?
即使删缓存失败,最多缓存里是旧值,但数据库里是最新的。下一次缓存失效或被淘汰后,会自动拉新。
删缓存前服务挂了?
订阅BinLog,异步删缓存。
2.如果并发下多个线程同时写,怎么保证缓存和数据库一致?
分布式锁:给某个业务 ID 加锁,保证同一时间只有一个线程能操作,避免并发覆盖。适合金融、库存扣减等强一致场景。
延时双删:更新数据库 → 删除缓存 → 延时几百毫秒后再删一次缓存,避免读写并发时旧值被写回,降低不一致概率。
逻辑过期:
在缓存中维护逻辑过期时间,过期后后台线程异步重建缓存,对用户返回旧值即可。适合商品详情、商铺信息这类允许短时间脏数据的场景,允许短暂不一致。
3.缓存击穿、穿透、雪崩的区别?怎么解决?
穿透:查询不存在的数据,缓存和数据库中都没有此数据,所以请求会不断打到数据库,可能导致数据库瘫痪。
可以缓存null值,对不存在的数据key的value缓存为null,这种方法很大的弊端就是内存占用和数据不一致(如果下次真的要储存当前数据,会导致缓存与数据库数据不一致)。
更好的办法是布隆过滤器,首先初始化一个比较大的数组,里面存放的是二进制0或1,全部初始化为0,当一个key来了之后,经过3次Hash计算,模数组长度得到下标,并在原来的位置改为1。这样三个数组的位置就可以标明一个key的存在,查找的过程也是这样。当然布隆过滤器也会产生误判,误判率可以自己设置,一般是5%。这个误判率是必须存在的,否则就得增加数组长度,5%的误判率大部分项目也能接受。
击穿:对于设置了过期时间的key,在某个时间Redis缓存恰好过期,恰好这时有大量对这个key的请求打过来。发现缓存过期就会查询DB,导致压垮数据库。
一般有两种方式应对。
- 使用互斥锁,利用setnx设置互斥锁,当操作成功时再进行load db的操作,并写入缓存,否则重试获取缓存的方法。
- 逻辑过期:
- 将key写入Redis时,附带上一个
expireTime
字段,但不设置TTL。 - 查询时,从Redis中取出数据后判断时间是否过期
- 如果过期会返回旧数据,然后开新线程进行缓存重建,查询数据库将新数据写入Redis。
如果项目对强一致性要求较高就采用互斥锁,如果更多的是追求并发性就采用逻辑过期。
雪崩:大量key在同一时间过期,请求全部转发到DB,DB瞬间压力过重而雪崩。可以将缓存失效时间分开,比如可以在失效时间基础上加上随机值,比如1-5分钟随机。这样每个缓存过期时间的重复率就会降低,很难引发集体失效。
4.Redis 持久化方式有哪几种?优缺点?
Redis中提供了两种数据持久化的方式:RDB和AOF。
Redis DataBase和Append Only File。RDB是一个快照文件,会将Redis内存存储的数据写入磁盘内,当Redis实例宕机时,可以从RDB快照文件中恢复数据。
AOF是含义是追加文件,当Redis执行写命令时,都会存储到这个文件中。恢复数据时,只需要重新执行一遍里面的数据。
RDB是二进制文件,保存时体积小,所以恢复的比较快,但是可能会丢数据。
AOF恢复速度慢一些,但丢数据的风险小很多。可以在AOF文件中设置刷盘策略,比如每秒批量写入一次命令。
如果这篇文章对你有帮助,请点赞、评论、收藏,创作不易,你的支持是我创作的动力。