参考:
https://juejin.cn/post/7074395743213060133
个人学习总结及分享,和而不同,相帮共勉
1、问题:
- 为什么会出现数据双写一致性问题?
首先介绍下redis与mysql的双写工作场景
假设数据库有a=1,缓存a=1,现有两个线程A、B,A线程更新a=2,B线程更新a=3
具体操作时间线如下,最终导致缓存中a=2,而数据库中a=3,数据存在明显不一致的情况。
根本原因,是多线程并发操作导致数据库和缓存数据未及时更新
2、是更新缓存or删除缓存,是先操作缓存还是先操作数据库?
根据问题,衍生出三种更新策略:
先更新数据库,再更新缓存 or 先更新缓存,再更新数据库,这种情况,由于是更新操作,由于多线程操作,并不能保证执行缓存操作时,线程的修改数据的先后顺序,若缓存未设置过期时间很容易产生脏数据,且更新缓存势必引入不必要的更新逻辑(),否则通常不予考虑。
先删除缓存,再更新数据库,假设两个线程A,B,数据库、缓存初始值a=1,A写操作更新a=,2的值,B读操作。具体操作时间线如下图,最终导致数据不一致,只能等待下次数据更新或者缓存过期时间到,才更新缓存,保证数据最终一致性。
- 先更新数据库,再删除缓存,(业界普遍使用的方案)假设两个线程A,B,数据库、缓存初始值a=1,A写操作更新a=,2的值,B读操作。具体操作时间线如下图,最终导致数据不一致,但是数据不一致仅限于线程A还未删除缓存a前,间隔时间短。
3、成熟的技术方案?
核心方案:"延时双删”,通俗来讲就是删两次缓存,第一次,先更新数据库,在删除缓存。第二次,等待一段时间再删除一次缓存。
但是延时双删会存在一些问题,比如,删除失败如何解决,由于设置等待时间导致应用吞吐量下降如何解决?
延时双删变体:
缓存删除重试机制,将删除失败的key放入mq,从mq中将删除失败的key拿出来重新删除,并不断重复这个操作,直至key删除成功;
binlog异步删除机制(cancal),上述删除重试机制,虽然能解决删除失败的问题,但是会导致重试机制代码会与业务代码耦合性大大提高,该重试机制会导致业务代码存在大量从代码。失败重试机制作为公共功能,理应从业务代码抽离,单独处理。
基于以上考虑,业界推出基于binlog的异步删除策略,具体业务逻辑如下: