分布式场景下,缓存与数据库的一致性是必须要绕过去的一道坎。而强一致性同步成本太高,如果追求强一致,那么没必要用缓存了,直接用mysql即可。通常考虑的,都是最终一致性。我们要做的,就是把这个最终一致性的时间降到最低。
从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。
方案一:先同步数据库,再删除缓存。
失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
命中:应用程序从cache中取数据,取到后返回。
更新:先把数据存到数据库中,成功后,再让缓存失效。
看看更新操作有没有问题。
如果数据库同步成功,缓存操作失败,则让事务回滚,最终还是一致的。
如果数据库同步失败, 缓存也不会再被操作,数据是一致的。
方案二:监听binlog日志
监听binlog日志,当监听到的数据变化时,让缓存失效或更新。
实现方法:业务服务,同步数据完成后,删除缓存。定义好缓存类与数据库表的映射关系,并定义为缓存同步类型,当监听到某一表变化时,调用该同步类型对应的实现类,把数据同步到redis中。
监听biglog的工具:
(1)mysql-binlog-connector这个jar,可以实现监听binlog。
https://github.com/shyiko/mysql-binlog-connector-java
(2)阿里开源的Canal(比较活跃的)
https://github.com/alibaba/canal
(3)zendesk开源的Maxwell
(4)Yelp开源的mysql_streamer
方案三:先删缓存,再同步数据库
先删除缓存,成功后,再同步数据库。如果缓存删除成功,数据库同步失败,则下次从数据库读。
此方案存在的最大问题:
1、线程A删除缓存,还没有来得及同步数据库时
2、线程B查询缓存,发现没有,则从数据库查询到旧值,并同步到缓存
3、线程A同步新值到数据库
这样,缓存和数据库就不一致了。
方案四:先更新缓存,再同步数据库
此方案绝对不能用,如果缓存更新成功,数据库操作失败,那就完了。
总结
方案一和方案二可以在实际中使用,其中,如果有强一致性的需求,可以使用方案二。
来源:CSDN
作者:昨天与今天
链接:https://blog.csdn.net/longvs/article/details/103928134