Redis 常见问题
1. 缓存穿透
缓存穿透:key中对应的缓存数据不存在,导致去请求数据库,造成数据库的压力倍增的情况。
解决方案:
- 采用布隆过滤器(BloomFilter)进行数据拦截,在查询的时候先去 BloomFilter 去查询 key 是否存在,如果不存在就直接返回,存在再走查 缓存 -> 查 DB。
- 如果请求的数据为空,我们可以将空值也进行一次缓存(过期时间设置短点),这样再请求的时候就会返回null。
- 后端接口层增加 用户鉴权校验,参数做校验等。
- 单个IP每秒访问次数超过阈值直接拉黑IP,关进小黑屋1天。
2. 缓存击穿
缓存击穿:redis过期后的一瞬间,有大量用户请求同一个缓存数据,导致这些请求都去请求数据库,造成数据库压力倍增的情况,针对一个key而言。
解决方案:
- 使用互斥锁(mutex key),就是一个key过期时,多个请求过来允许其中一个请求去操作数据库,其他请求等待第一个请求成功返回结果后再请求。
- 设置热点数据永远不过期。
3. 缓存雪崩
缓存雪崩:缓存服务器宕机或者大量缓存集中某个时间段失效,导致请求全部去到数据库,造成数据库压力倍增的情况,这个是针对多个key而言。
解决方案:
- 缓存数据的过期时间加上个随机值,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
- 设置热点数据永远不过期。
- 加锁排队,这种有点像上面缓存击穿的解决方式,但是这种请求量太大,比如5000个请求过来,4999个都需要等待,这必然是指标不治本,不仅用户体验性差,分布式环境下就更加复杂,因此在高并发场景下很少使用
- 最好的解决方法,是使用缓存标记,判断该标记是否过期,过期则去请求数据库,而缓存数据的过期时间要设置的比缓存标记的长,这样当一个请求去操作数据库的时候,其他请求拿的是上一次缓存数据
4. 双写一致性
即缓存跟数据库均更新数据,如何保证数据一致性?
遵循 Cache Aside Pattern
- 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
- 命中:应用程序从cache中取数据,取到后返回。
- 更新:先把数据存到数据库中,成功后,再让缓存失效。
5. 脑裂
脑裂是指因为网络原因,导致master节点、slave节点 和 sentinel集群处于不用的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。
此时存在两个不同的master节点就像一个大脑分裂成了两个。
集群脑裂问题中,如果客户端还在基于原来的master节点继续写入数据那么新的master节点将无法同步这些数据,当网络问题解决后sentinel集群将原先的master节点降为slave节点,此时再从新的master中同步数据将造成大量的数据丢失。
Redis处理方案是redis的配置文件中存在两个参数
min-replicas-to-write: 3 表示连接到master的最少slave数量
min-replicas-max-lag: 10 表示slave连接到master的最大延迟时间
如果连接到master的slave数量 < 第一个参数(3) 且 ping的延迟时间 <= 第二个参数(10)那么master就会拒绝写请求,
配置了这两个参数后如果发生了集群脑裂则原先的master节点接收到客户端的写入请求会拒绝就可以减少数据同步之后的数据丢失。
6. 事务
步骤:开始事务 -> 命令入队 -> 执行事务
- redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
- Redis事务没有
隔离级别
的概念:批量操作在发送EXEC
命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。 - Redis
不保证原子性
:Redis中单条命令是原子性执行的,但事务不保证原子性。 - Redis编译型错误事务中所有代码均不执行,指令使用错误。运行时异常是错误命令导致异常,其他命令可正常执行。
watch
指令类似于乐观锁
,在事务提交时,如果watch
监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC
执行事务时,事务队列将不会被执行。
7. 正确的Redis开发步骤
上线前: Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
上线时: 本地 ehcache 缓存 + Hystrix 限流 + 降级,避免MySQL扛不住。
上线后: Redis 持久化采用 RDB + AOF 来保证断点后自动从磁盘上加载数据,快速恢复缓存数据。