前言概括:
在满足实时性的条件下,不存在两者完全保存一致的方案,只有最终一致性方案。
图解说明:
- 时序图,描述请求的先后调用顺序;
- 红色的线是请求A,黑色的线是请求B;
- 红色的文字,是MySQL和Redis最终不一致的数据;
- 数据是从10更新为11。
1.先写MySQL,再写Redis
请求A、B都是先写MySQL,然后再写Redis,在高并发情况下,如果请求A在写Redis出现卡顿,请求B已经依次完成数据的更新,就会出现图中问题。
不过这里有个前提,就是对于读请求,先去读Redis,如果没有,再去读DB,但是读请求不会再回写Redis。也就是说,读请求不会更新Redis。
2.先写MySQL,再删除Redis
针对这种情况, 第一次查询,请求B查询的数据是10,但是MySQL的数据是11,只存在这一次不一致的情况,对于不是强一致性要求的业务,可以容忍。
当请求B进行第二次查询时,因为没有命中Redis,会重新查一次DB,然后再回写到Redis。
3.先写MySQL,通过Binlog,异步更新Redis
此方案,会保证MySQL和Redis的最终一致性,但是如果中途请求B需要查询数据,如果缓存无数据,就直接查DB;如果缓存有数据,查询的数据也会存在不一致的情况。
所以这个方案,是实现最终一致性的终极解决方案,但是不能保证实时性。
4.讨论方案比较
(1)先写MySQL,再写Redis
- 对于并发量、一致性要求不高的项目,很多就是这么用的;
- 当Redis瞬间不可用的情况,需要报警出来,然后线下处理。
(2)先写MySQL,再删除Redis
- 比较推荐这种方式,删除Redis如果失败,可以再多重试几次,否则报警出来;
- 这种方案,是实时性中最好的方案,在一些高并发场景中,推荐使用。
(3)先写MySQL,通过Binlog,异步更新Redis
- 对于异地容灾、数据汇总、建议用这种方式,比如binlog + kafka,数据的一致性也可以达到秒级;
- 纯粹的高并发场景,不建议用这种方案,比如抢购、秒杀等。
5.个人结论
- 实时一致性方案:采用“先写MySQL,再删除Redis的策略”,这种情况虽然也会存在两者不一致,但是需要满足的条件有点苛刻,所以是满足实时性条件下,能尽量满足一致性的最优解。
- 最终一致性方案:采用“先写MySQL,通过Binlog,异步更新Redis”,可以通过Binlog,结合消息队列异步更新Redis,是最终一致性的最优解。