如何保证redis和MySQL数据的一致性
首先说结论:无法保证100%的数据强一致性,只要是双写,悲观角度看待一定会出现数据不一致问题,这个就好比一个人又要向左看又要向右看一样,是不可能的,除非你有影分身
介绍4种思路
1 先更新数据库,再更新缓存
适用场景:服务器停机状态下可以使用这种方法
在不停机的情况下
可能出现的问题:1 MySQL更新成功 redis失败
举例:先更新了MySQL,假设数量从100改为99,然后回写进redis的时候发生了异常,导致MySQL内是99,redis内还是100,下次读取操作会命中redis,将100返回导致数据不一致
可能出现的问题:2 多线程环境下,每个线程的执行消耗时间不一致,导致出现问题
举例:线程A在更新数据库的时候B线程也要更新同一个数据此时发生了如下情况
现在要把数据从100改为99
A线程带着99正在走路去更新的路上...
需求紧急变更,要改为98
B线程火速坐飞机带着98杀进来更新
B线程更新完了,回写进redis,任务完成
A线程终于走到站了,把更新的数据写入MySQL
A线程回写进redis,任务完成
B表示???更新了个寂寞,白干了
不推荐

2 先更新缓存,再更新数据库
不推荐 因为一般会把数据库最为最终解释数据,不会把缓存数据当作最终数据
正常逻辑:
A更新redis
A更新MySQL
B更新redis
B更新MySQL
多线程情况下可能会出现这个问题
数据库内数据为100
A更新redis ==>99
B更新redis ===>98
B更新数据库 ===>98
A更新数据库 ===>99
一顿操作完成后
redis => 98
MySQL => 99
导致不一致
不推荐

3 先删除缓存,在更新数据库
可能出现的问题 1 A线程删除redis,正在更新MySQL,B来读取MySQL
此时A正在更新,B读取到了旧的数据,然后将旧数据写入redis,导致A线程在MySQL中更新完后发现redis里面有脏数据,就无动于衷,不管了
解决方案:延时双删
实现方式:
先删除redis里面的值,然后正常执行业务逻辑,执行完成后再去删除redis里面的缓存
此时,按照上面问题1 的逻辑,A先删除redis,去更新MySQL,此时B来查找,redis无,找MySQL,找到,返回并写入redis,B虽然返回的是旧数据,但是B完事了,此时A对MySQL的操作完成了,再去删除redis里面的缓存,就把B才写入的旧数据又干掉了,这样,当C线程再来查询的时候,redis里面没有,去MySQL里面查询到的就是A更新完的最新的数据,然后写入redis,就达成了redis和MySQL数据一致
但是上述实现方式又有一个问题
问题1
A处理业务逻辑的时间一定要大于B读取数据并写入redis的时间,那A要干活多久呢?
方法1
在业务程序运行的时候,统部下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。(我觉得不行,不要犯经验主义的错误)
方法2
新启动一个后台监控程序,比如后面要讲解的WatchDog监控程序,会加时(我觉得可)
问题2
这种同步淘汰策略,吞吐量降低怎么办?

说明:起一个异步线程删除redis里面的数据
结论:能用,比较麻烦,建议看4

4 先更新数据库,再删除缓存
逻辑顺序介绍:
A线程更新了MySQL,B突然来查询,发现有,然后就返回了旧值,B的事情干完了,A更新MySQL完成后,去把redis缓存删除,这样,C线程再来读取的时候,redis里没有,就去MySQL内读取到了最新值,并写入了redis
结论:这样是比较稳妥的方案,建议使用
但是还有最后一个问题:数据的最终一致性
解决方案:消息中间件



小总结:

