Skip to content
← 返回Redis列表
数据与原子性

Redis:数据结构服务、缓存与部署边界 · 缓存异常至延伸阅读 ​

返回分篇目录

缓存异常 ​

缓存雪崩 ​

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案 ​

  • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  • 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
  • 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

缓存穿透 ​

缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉

  • 解决方案
    • 接口层增加校验,如用户鉴权校验,id 做基础校验,id<=0 的直接拦截;
    • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null,缓存有效时间可以设置短点,如 30 秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个 id 暴力攻击
    • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力

缓存击穿 ​

缓存击穿是指在缓存中不存在的数据请求(通常是缓存过期或从未缓存)时,导致大量请求同时访问数据库,造成数据库负载骤增。例如,当多个用户同时请求一个已经过期的缓存数据,系统直接访问数据库,可能会导致数据库的瞬时压力激增,影响整体性能

  • 解决方案
    • 互斥锁 :在处理请求时,对即将过期的缓存加锁,只有第一个请求能够去数据库查询并更新缓存,其他请求则等待,直至缓存更新完成
    • 预热缓存 :在系统启动时或者特定时间段,提前将热点数据加载到缓存中,避免在高并发情况下直接访问数据库
    • 使用空对象缓存 :对于某些请求结果为空的数据,可以在缓存中存储一个特定的标记(如空对象),在一定时间内不再访问数据库,避免重复的数据库查询
    • 随机过期时间 :为缓存设置随机的过期时间,避免大量缓存数据同时过期,降低缓存击穿的风险
    • 监控和报警 :实施监控机制,及时发现数据库负载异常,并进行报警处理,快速响应可能的缓存击穿情况

Redis 与 Mysql 同步机制 ​

Mysql 同步到 Redis ​

  • 在 redis 数据库设置缓存时间,当该条数据缓存时间过期之后自动释放,去数据库进行重新查询,但这样的话,我们放在缓存中的数据对数据的一致性要求不是很高才能放入缓存当中

    缓存量大但又不常变化的数据,比如商品信息的评论

  • 基于 binlog 使用 mysql_udf_redis,将数据库中的数据同步到 Redis

    无论 MySQL 还是 Redis,自身都带有数据同步的机制,像比较常用的 MySQL 的 Master/Slave 模式,就是由 Slave 端分析 Master 的 binlog 来实现的,这样的数据其实还是一个异步过程,只不过当服务器都在同一内网时,异步延迟需要实际测量;同一内网也不能保证为零或可忽略。

    那么理论上我们也可以用同样方式,分析 MySQL 的 binlog 文件并将数据插入 Redis。但是这需要对 binlog 文件以及 MySQL 有非常深入的理解,同时由于 binlog 存在 Statement/Row/Mixedlevel 多种形式,分析 binlog 实现同步的工作量是非常大的

  • 通过 MySQL 自动同步刷新 Redis

    当我们在业务层有数据查询需求时,先到 Redis 缓存中查询,如果查不到,再到 MySQL 数据库中查询,同时将查到的数据更新到 Redis 里;当我们在业务层有修改插入数据需求时,直接向 MySQL 发起请求,同时更新 Redis 缓存。 就是 MySQL 的 CRUD 发生后自动地更新到 Redis 里,这需要通过 MySQL UDF 来实现。具体来说,我们把更新 Redis 的逻辑放到 MySQL 中去做,即定义一个触发器 Trigger,监听 CRUD 这些操作,当操作发生后,调用对应的 UDF 函数,远程写回 Redis,所以业务逻辑只需要负责更新 MySQL 就行了,剩下的交给 MySQL UDF 去完成。

  • 在我们的实际开发当中往往采用如下方式实现实现 Mysql 和 Redis 数据同步:当我们在 MySQL 数据库中进行增删改的时候,我们在增删改的 service 层将缓存中的数据清除,这个时候用户在此请求的时候我们缓存中没有数据了,直接去数据库中查询,查询回来之后将缓存中的数据放缓存当中,这个时候回填结果仍可能因并发写入或旧读取而陈旧,需按业务定义版本、失效事件或其他协调机制。

Redis 同步到 MySql ​

redis 读取速度快,也没有必要把所有的数据都放到 redis 里面,redis 里面只放使用频繁,用户操作量较大的数据,或者用户近期使用的数据

  • 不是很重要可以设置定时器,在晚上用户访问少时候同步

延伸阅读 ​

具体锁释放与缓存读写顺序见 Redis 数据结构、缓存与锁实践。

继续阅读