高并发下redis缓存常见问题

2023年2月1日08:25:17

redis作为缓存,与数据库一起给系统提供数据服务,redis不只是提供了查询访问的高性能响应,而且屏蔽了大量的对数据库的查询请求,避免了高并发下数据库被击溃的问题。

对于应用了缓存的数据,可以粗略的理解为在应用和数据中间加了一层。但是正所谓宝剑双锋,在增加缓存层后,解决了很多问题,但也不可避免的带来一些技术复杂度,尤其是在高并发的场景下。下面我们来一起看看有那些常见问题和对应的解决方案。

数据一致模式

缓存层和DB层的数据一致处理模式《 cache aside pattern 》分为两种:

读:数据查询的时候,先从redis中获取,没有则查询数据库,结果写入redis缓存,返回结果。

写:数据写入的时候,先写DB,再失效缓存:

具体如下图:

高并发下redis缓存常见问题

常见问题

缓存穿透

问题产生

当用户高并发大量请求不存在的key时,因为redis没有缓存该key的数据,所有查询会穿透缓存,直接指向数据库,数据库的连接数会急速上升,很快就会导致连接耗尽,数据库宕机。

解决方案

解决该问题的思路是:屏蔽大量查询连接数据库。大致可以有以下3中方案:

1,屏蔽少量IP密集性高并发访问,通过nginx配置 HttpLimitReqModul和HttpLimitZoneModule来限制ip在同一时间段的访问次数,以避免恶意攻击的缓存穿透。

2,缓存空值,对于提供缓存的数据,可以在数据库查询不到数据的情况下,在缓存中增加对应key的null值。这样大量的数据访问会直接从redis获取到null值,不会把压力集中到数据库层。(1,实际实现中,缓存空值会设置较短的过期时间,一般1-3分钟后就过期了。这样设置的原因是:这些数据在redis中缓存其实是没有业务价值的,避免长期占用空间;2,实际从缓存获取不到值后,在后面的实现中,一般会加锁(分布式锁或者本地锁),具体看缓存击穿的解决办法。)

3,采用布隆过滤器(Bloom Filter),基本思路就是开辟一块单独的空间存放所有可能的key值,在key值以内的,redis缓存中获取不到数据,才会查询数据库后再把结果写入redis。

缓存雪崩

问题产生

由于redis受限于内存空间,不能把所有的数据存放在缓存中,缓存的基本思想是存放高频查询的数据。所以,缓存一般是会设置过期时间,过期后,缓存数据会被清空。而每次缓存启动都会预热数据,这样,大量的数据可能会在同一时间被清空,而这个时候,如果这些数据存在大量的访问,压力会直接集中到数据库上面,造成数据宕机。

解决方案

解决思路是:避免大量数据同时过期。可以在大批量数据同时缓存的时候,缓存的过期时间不采用固定值,采用随机算法,给指定的固定值增加一个随机数时间长度。

缓存击穿

问题产生

热点数据有大并发的请求量在请求缓存的时候,由于缓存过期,大量的请求瞬时访问到数据,导致数据库宕机。

解决方案

基本思路就是通过锁的方式(缓存击穿的问题,本地锁就可以解决问题。),进程中同时只允许一个线程去读取数据库并更新缓存。

具体实现如下图:

高并发下redis缓存常见问题

缓存数据库双写不一致

所谓双写不一致,就是缓存和数据库的数据不一致。站在业务的角度来分析,一般没有要求缓存、DB数据一直严格一致的情况,大多数情况是可以允许最终一致的。在 cache aside pattern 中推荐的写模式,其实满足绝大部分要求的。如果要尽可能的提高一致性,可以将同步的删除缓存做一个调整,采用异步或者其他方式来重复尝试确保删除成,一般不建议占用当前请求的时间。(网上有说阿里开源的数据同步方案:alibaba/canal 中双一致方式是通过写和订阅数据库的binlog 的方式去异步实现删除缓存。有兴趣的可以了解一下:https://github.com/alibaba/canal)

小结

本文简单描述了redis缓存在高并发场景下的一些常见问题。曾经见过有项目把redis纯粹作为数据库使用,而同时存在的mysql就像是一个另类的备份,这种模式下,对数据的读写和使用和作为缓存使用区别很大,就不讨论了。

  • 作者:大斜千变
  • 原文链接:https://blog.csdn.net/kongxincai0/article/details/112123756
    更新时间:2023年2月1日08:25:17 ,共 1631 字。