常见问题
# 缓存雪崩
Redis中大批量key在同一时间同时失效导致所有请求都打到了MySQL。而MySQL扛不住导致大面积崩塌
解决方案
- 缓存数据的过期时间加上个随机值,防止同一时间大量数据过期现象发生
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中
- 设置热点数据永远不过期
- 使用快速失败的熔断策略,减少 DB 瞬间压力;
- 使用主从模式和集群模式来尽量保证缓存服务的高可用。
# 缓存穿透
缓存穿透是指缓存和数据库中都没有
的数据,比如ID默认>0,黑客一直 请求ID= -12的数据那么就会导致数据库压力过大,严重会击垮数据库。
解决方案
- 后端接口层增加 用户鉴权校验,参数做校验等
- 单个IP每秒访问次数超过阈值直接拉黑IP
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null 失效时间可以为15秒防止恶意攻击
- 用Redis提供的 Bloom Filter 特性
# 缓存击穿
大并发集中对这一个热点key进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库
解决方案:设置热点数据永远不过期或者加上互斥锁
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
缓存穿透,缓存击穿,缓存雪崩解决方案分析_zeb_perfect的专栏-CSDN博客 (opens new window)
redis缓存穿透,缓存击穿,缓存雪崩原因+解决方案 - 知乎 (zhihu.com) (opens new window)
# 双写一致性
缓存
跟数据库
均更新数据,如何保证数据一致性?
# 先更新数据库,再更新缓存
- 安全问题:线程A更新数据库->线程B更新数据库->线程B更新缓存->线程A更新缓存。
导致脏读
。 - 业务场景:读多写少场景,频繁更新数据库而缓存根本没用。更何况如果缓存是叠加计算后结果更
浪费性能
。
# 先删缓存,再更新数据库
同样会造成数据不一致的问题:
A 请求写来更新缓存->B 发现缓存不在去数据查询旧值后写入缓存->A 将数据写入数据库,此时缓存跟数据库不一致。
FaceBook的Cache Aside Pattern (opens new window)方案
- 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
- 命中:应用程序从cache中取数据,取到后返回。
- 更新:
先把数据存到数据库中,成功后,再让缓存失效
。
# 脑裂
脑裂是指因为网络原因,导致master节点、slave节点 和 sentinel集群处于不用的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点 此时存在两个不同的master节点就像一个大脑分裂成了两个。如果客户端还在基于原来的master节点继续写入数据那么新的master节点将无法同步这些数据,当网络问题解决后sentinel集群将原先的master节点降为slave节点,此时再从新的master中同步数据将造成大量的数据丢失。
解决方法
修改redis的配置文件
min-replicas-to-write 3 表示连接到master的最少slave数量
min-replicas-max-lag 10 表示slave连接到master的最大延迟时间
2
当我们上面两个条件都不满足时,那么master就会拒绝写请求,这样就可以减小数据同步后的数据丢失
# 事务
Redis支持事务,通过 MULTI (opens new window) (事务开始)、 DISCARD (opens new window) (事务取消)、 EXEC (opens new window) (执行事务)和 WATCH (opens new window) (监听key,当key被修改时会打断当前事务)四个命令来实现事务功能
# 执行的三个阶段
- 开启事务(multi)
- 命令入队(业务操作)
- 执行事务(exec)或取消事务(discard)
# 特点
- redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
- Redis事务失败时不支持回滚,而是会继续执行剩下的命令
# 热key
所谓热key问题就是,突然有几十万的请求去访问redis上的某个特定key,那么这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机引发雪崩。
针对热key的解决方案:
- 提前把热key打散到不同的服务器,降低压力
- 加入二级缓存,提前加载热key数据到内存中,如果redis宕机,走内存查询