前言
数据量不断增大,如何利用最小的资源条件实现程序的最大性能,这问题基本所有程序猿都有思考的问题。好些经验尚浅的猿猿们估计都会说,加缓存呀,的确现在业内解决这个问题的方案是加缓存,但是加缓存也是需要区别对待的。如果没有一个完善并且值得推敲的缓存设计方案,因为加缓存引出的一个很严重的问题就出现了,数据一致性。就我经历的一个项目说,当时需要根据一个区域的大量节点进行查找计算出最短几条的连接路线,因为数据量大全部拿库是很慢的,因此加入了缓存,性能是提升尚来了,但是实际已经被占用的资源再下次计算中还会被囊括,就是因为缓存导致的。最后解决方案通过自动刷新和手动刷新来实现的。虽然设计不太好,但是因为非互联网项目,暂时可以满足需求。当对于标志比较高的那就不一定能满足了。
在知乎中 缓存与数据库一致性系列 这个文章中讲的很好,相信看过的都能有所得。 在文章中下图中的一句话很对,只有确定清楚了自己系统要达到的要求,才能针对性的制定对应方案。鱼我所欲也,熊掌亦我所欲,二者兼可得乎!没有最好得方案,有的只是最合适得方案。根据实际情况,扬长避短结合给种方案得优劣来选取实现才合理。
目的
数据和缓存强一致性
方案
要达到目的,可分为两步 最终一致性 和最终强一致性。在文章 缓存与数据库一致性系列 和 主从缓存一致性4中优化 有各种方案的分析,在此将自己比较认可的一种方案进行记录下,
第一步:实现最终一致性,次方案可以说是主从优化4方案中 第4种的优化版,利用消息队列将缓存刷新的操作异步化和串行化。
写流程容灾分析
- 写1.1 DEL缓存失败:没关系,后面会覆盖
- 写1.4 写MQ失败:没关系,Databus或Canal都会重试
- 消费MQ的:1.5 || 1.6 失败:没关系,重新消费即可
读流程容灾分析
- 读2.3 异步写MQ失败:没关系,缓存为空,是OK的,下次还读库就好了
第二步:第一步中方案已经实现了最终一致性,而最终强一致性和此的差别就是,时间差,借用原作者的 :“ 最终一致性方案” + “时间差” = “强一致性方案”,原主的方案是 对 变动数据加 标记锁 ,即是当进行数据修改操作时 填加修改标记,标记成功才进行实际修改操作,当进行读操作时,当获取到写的标记时,就强行从主库中获取数据。而达到立即一致性。
补充
- 在原博文中 缓存与数据库一致性系列 中 加缓存标记锁,这里使用 可自动失效的缓存 可进一步优化,将失效时间设定稍大于主从同步周期时间。
- 实际使用中,并不是所有的数据都适用缓存,而上述方案也不是适用所有的业务需求,一味追求高性能而增加了项目复杂度,项目成本,和开发难度和后期维护难度都是不可取的。
- 对于缓存方案,可根据数据类型和修改频率进行选择,例如对于系统配置相关数据,使用 永久缓存(启动时加载,未查询到查询数据库)+ 修改标记锁基本就可以满足。
后言
此文属主要内容并非原创,仅用于记录和分享