0、背景
WRITE : 数据写入Mysql 和 Redis缓存,
READ:先从 Redis 缓存中取数据,拿不到再从Mysql中加载,更新到Redis
一、传统方案的缺点
1.1 删除场景
1.1.1 先删 Redis 再删 Mysql (不一致场景如下图)
线程1:删除Redis,
线程2:查询不到,去查Mysql,然后设置到 Redis中去
线程1:删除Mysql数据
此时 Redis 与 Mysql的数据不一致。
1.1.2 先删 Mysql 再删 Redis (不一致场景如下图)
线程1:缓存过期,查询不到,去查Mysql
线程2:删除 Mysql 数据
线程2:删除 Redis 数据
线程1:Mysql 的数据设置到 Redis 中去
此时 Redis 与 Mysql的数据不一致。
update的场景和上述是一模一样的。 (并且上述都是指考虑单机,还没考虑Redis集群和Mysql集群的主从同步问题)
二、监听 binlog方案
上图第三阶段,接收Mysql的binlog变更消息,可以参考阿里的 Canal,接入 Kafka
2.1、缓存失效方案
脏数据定义:Redis中取到的数据,不是Mysql-Master中的数据
2.1.1 同步失效(写Mysql后同步失效Redis缓存)
脏数据生成时间窗口 | 脏数据存在时间窗口 | |
Redis有数据 | T0 - T2(小于Mysql主从延迟耗时) | T0 - T2 期间生成的,那么脏数据存在时间是生成那刻 —— 下次更新前。 |
Redis没有数据 | T1 - T2 (Mysql主从延迟耗时) | 1、若是 T1 - T0 期间生成的,那么脏数据存在时间 T1 - T0 2、若是 T0 - T2 期间生成的,那么脏数据存在时间是生成那刻 —— 下次更新前。 |
2.1.2 接收到Binlog的MQ消息后,失效Redis缓存
脏数据生成时间窗口 | 脏数据存在时间窗口 | |
Redis存在数据 | 无,存在缓存不重新生成 | T1 - T0 |
Redis不存在数据 | T1 - T2(Mysql主从延迟耗时) | 写入Redis那一刻 —— T0 (Mysql主从延迟耗时 + binlog消息发送与拉取耗时) |
![](https://i-blog.csdnimg.cn/blog_migrate/ac86c24c1a304d3fecfd35b73c00c03c.png)
2.1.3 同步失效 + 接收到Binlog的MQ消息后,失效Redis缓存
脏数据生成时间窗口 | 脏数据存在时间窗口 | |
Redis存在数据 | T0 - T2 因为 T1 - T0 期间有缓存,所以不重新生成,那么 肯定是 T0 - T2 期间生成的 | T1 - T5 |
Redis不存在数据 | T1 - T2(Mysql主从延迟耗时) | 1、若是 T1 - T0 期间生成的,那么脏数据存在时间 T1 - T0 2、若是 T0 - T2 期间生成的,T0 - T5 |
2.1.4 三种方案对比
方案 | 脏数据生成时机窗口 | 脏数据可能存在的时间窗口 | 改造复杂度 |
方案1:同步失效 | MySQL 主从延迟期间 | 可能很长,无法容忍 生成那刻 —— 下次更新前。 | 中,所有更新操作同步失效 |
方案2:监听binlog失效缓存 | MySQL 主从延迟期间 | Mysql主从延迟耗时 + binlog消息发送与拉取耗时 | 简单,接入Binlog统一失效 |
方案3:同步失效缓存 + 监听binlog失效缓存 | MySQL 主从延迟期间 | Mysql主从延迟耗时 + binlog消息发送与拉取耗时 | 复杂,接入DTS统一失效 + 所有更新操作同步失效 |
建议:直接方案2 ,方案3 如果存在太多update场景的话,入口太多。