参考文章地址:如何实现Redis和Mysql中数据双写一致性
【Redis】所以延迟双删有啥用
延时双删实现MySQL和Redis的数据一致性
在学习苍穹外卖的时候,为了保持数据的一致性,当时教的方法是在每次进行数据库的操作之前就先删除Redis中对应的数据。原以为这确实是个好方法,今看到这篇公众号文章,又有了更深刻的了解,随记下笔记。
问题原因:
如果只是在每次操作前进行Redis的删除操作,这样的方式不一定每次都奏效。如果遇到了网络拥塞,那么删除了Redis中的数据库,需要过一段时间才能更新MySQL中的数据,那么在这段时间内如果有查询操作,那么又将出现数据不一致的情况。1
解决
虽然苍穹外卖中推荐的是先删后更,但是有的文章也会推荐使用先更后删,采用Cache Aside(旁路缓存)。先更后删也会存在数据不一致性的问题,只是相对出现的概率性不高2。如果要彻底解决这个问题就需要加锁,而这样做就会导致效率变低,所以还是需要看具体的业务要求,选取合适的解决策略。所以,Redis最好适用于多读少些的场景下。
1、缓存延迟双删
针对先删后更进行改良。
- 删除缓存
- 更新数据库
- 睡眠一段时间
- 再删除缓存
根据业务需要设置休眠时间,目的是为了删除请求造成的脏读数据。只能提高相对的保持数据一致性的能力。因为在休眠期间还是可能存在数据不一致的可能。
为什么要设置休眠时间:因为线程是交替进行的,A删了缓存 想要更新数据库的时候,这次是B可能查到了旧的数据,然后还没更新,又到A去操作数据库,然后又删除缓存,B再去更新数据,那么又数据不一致了。通过设置休眠时间,就可以保证在一定时间后数据是一致的。虽然中间有一些脏读数据,比起整体应该是可以接收的。
再加强:还可以给缓存设置过期时间,双延迟删除+过期时间来保持最终数据的一致性。只是会增加时耗。
2、删除重试
如果不是加锁,其他的策略都有可能造成数据不一致性,毕竟考虑极端情况就是删除缓存操作失效了。所以引入删除重试机制。
有通过消息队列进行,但是会业务代码造成入侵。
1.更新数据库
2.数据库将操作信息写入binlog日志中
3.订阅程序提取出所需要的数据以及key(通过cancal监听和执行删除操作,以及重新操作。)
4.一段非业务代码,获取该消息
5.尝试删除缓存操作,如果删除失败 - > 6
6.将消息发送至消息队列
7.重新从消息队列中获得该数据,重新操作。
会增加复杂度,但是能更好的保障数据的一致性。
总结:
性能要求高,数据一致性要求没有那么高得时候,还是采用先更后删的策略。
数据一致性要求高,且性能有一定容忍度的时候,采取双延迟删除的策略。
数据一致性要求再高一些,采取删除重试的策略。
最极端就是加锁,但是一般不会采取。