Skip to Content
DocumentationRedisredis常见问题总结

Redis常见问题总结

数据库与缓存的一致性如何解决

一般来说,需要区分数据库的更新与redis的写入是否为同一个线程

数据库更新与redis写入在同一线程

  • 强一致性解决方案,利用redis的原子性,将缓存的操作用Lua脚本的方式写入,写入成功(包括有限次数内的失败重试)视为允许提交数据库事务,如果失败则回滚数据库事务
  • 最终一致性方案,利用可靠消息队列,在数据库提交后,利用消息队列,进行异步缓存更新,此时需要保证缓存更新一定成功

因为Redis本身不支持事务,所以在强一致性方案中,需要保证主程序不会出现其他异常导致数据库回滚,因为数据库回滚并不会影响redis回滚。 或者在数据库异常回滚后,手动操作redis回滚

数据库更新与redis写入不在一个线程

此时数据库操作完成后,不会主动更新redis,而是删除redis对应的key。在这种模式下,无论使用强一致性方案还是最终一致性方案,都需要借助分布式锁。因为不限制的话,可能会导致删除key之前,其他读线程读取到了数据库的旧值并且在删除key操作之后更新,所以需要借助分布式锁控制同步操作。因此不太适合读写频繁的场景。

监听数据库的方式

传统做法依赖于数据库本身事务与Redis操作,但因为Redis本身不支持事务,所以很难在技术层面上实现完全一致性。

此时更推荐主动监听数据库的方式,去同步更新。以MySQL为例子,可以使用canal,flink cdc等工具,监听数据库变化。 数据库存在变化,意味着数据库事务已经正常提交,此时在同步更新缓存,只需要保证Redis写入即可。这种方式属于最终一致性方案,存在一定的同步延时。

总结来看,由于redis本身不支持分布式事务,所以要保证数据库与缓存的一致性基本要依赖于程序自身逻辑。具体实现方式应该根据业务自身场景来设计。

缓存击穿

缓存击穿是指,针对某个访问非常频繁的热点数据的请求,无法在缓存中进行处理,紧接着,访问该数据的大量请求,一下子都发送到了后端数据库,导致了数据库压力激增,会影响数据库处理其他请求。 缓存击穿的情况,经常发生在热点数据过期失效时,如下图所示:

cache_break_down.png

为了避免缓存击穿给数据库带来的激增压力,我们的解决方法也比较直接,对于访问特别频繁的热点数据,我们就不设置过期时间了。这样一来,对热点数据的访问请求,都可以在缓存中进行处理,而 Redis 数万级别的高吞吐量可以很好地应对大量的并发请求访问。

当然也可以设置多级缓存,不同级缓存过期时间不一致。

缓存穿透

缓存穿透是指要访问的数据既不在 Redis 缓存中,也不在数据库中,导致请求在访问缓存时,发生缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据。此时,应用也无法从数据库中读取数据再写入缓存,来服务后续请求,这样一来,缓存也就成了“摆设”,如果应用持续有大量请求访问数据,就会同时给缓存和数据库带来巨大压力,如下图所示:

cache_penetration.png

一般来说,缓存穿透的发生有两种情况:

  • 业务层误操作:缓存中的数据和数据库中的数据被误删除了,所以缓存和数据库中都没有数据;
  • 恶意攻击:专门访问数据库中没有的数据。

提供三种解决方案

  1. 第一种方案是,缓存空值或缺省值。 在 Redis 中缓存一个空值或是和业务层协商确定的缺省值(例如,库存的缺省值可以设为 0)。紧接着,应用发送的后续请求再进行查询时,就可以直接从 Redis 中读取空值或缺省值,返回给业务应用了,避免了把大量请求发送给数据库处理,保持了数据库的正常运行。
  2. 第二种方案是,使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力。
  3. 最后一种方案是,在请求入口的前端进行请求检测。缓存穿透的一个原因是有大量的恶意请求访问不存在的数据,所以,一个有效的应对方案是在请求入口前端,对业务系统接收到的请求进行合法性检测,把恶意的请求(例如请求参数不合理、请求参数是非法值、请求字段不存在)直接过滤掉,不让它们访问后端缓存和数据库。这样一来,也就不会出现缓存穿透问题了。

缓存雪崩

缓存雪崩是指大量的应用请求无法在 Redis 缓存中进行处理,紧接着,应用将大量请求发送到数据库层,导致数据库层的压力激增。

一般来说原因有以下两种:

  • 缓存中有大量数据同时过期,导致大量请求无法得到处理。
  • 一致性hash环的集群特性导致的。集群中 某个主从节点挂掉了,请求分散到其他集群,但是量极大,把其他集群也都冲垮了。

同时过期的解决方案

  1. 尽量不要给key设置相同的过期时间
  2. 应用程序设计好一定的限流方案,比如服务降级,服务熔断等

实例宕机

  1. 如果是单节点模式下宕机,同样需要进行服务降级,避免大量请求打到数据库上
  2. 如果是集群中某个节点宕机(频繁访问导致),那么需要将一些热门key分散到不同的节点上,客户端访问时可以利用负载均衡的特性,进行一次重分发
Last updated on