缓存及其不一致

ops/2024/11/19 11:53:10/

        在实际开发过程中,一般都会遇到缓存,像本地缓存(直接在程序里搞个map也可以,但是可能会随着数据的增长出现OOM,建议使用正经的本地缓存框架,因为自己实现淘汰策略啥的挺费劲的)、分布式缓存,简单记录以下自己对这两方面的理解。

        本地缓存就是将数据缓存在当前应用程序本地,它速度快,易于管理,但是它无法跨多节点共享,在集群环境中会出现不一致的问题。多个本地缓存之间的数据可能不一致。

        分布式缓存就是将数据存储到多个节点的内存中,这些节点可以再不同服务器甚至不同的地理位置上。分布式缓存可以支持多个程序共享数据,提高系统的可伸缩性和可用性,但是成本比较高,考虑的地方也比较多,如数据一致性和故障恢复等问题。

        从上面不难看出来缓存有个共性,那就是缓存的一致性问题,在分布式的系统中,只能在CA中进行平衡,根据具体得到业务甚至数据特性进行平衡。

      本地缓存

        首先在选择上,现在的缓存框架多的很,Caffeine、Guava等,但是spring cache默认是支持Caffeine的

        启用本地缓存呢,就是为了减少网络连接(数据库、redis这都要的),有些数据频繁访问,而且这些数据在其他应用模块中不会使用或很少使用且这些数据很少会有所变动,如行政区划、行业信息、纳税人识别号以及一些系统内的数据。这种就适合放到本地内存里。

        但是本地内存有个问题,就是在集群环境里,多份数据的存储会造成内存的浪费,这点需要考虑到,但是世上安有两全法,只能是在,两者之间做权衡,选性能还是选成本。

        一旦要涉及到本地内存的一致性问题,那这问题就麻烦了,解决办法也不是没有,但一到实际中就等于是脱裤子放屁了,比如:数据库或者redis里弄个版本号,用本地缓存之前先看看版本号是否一致,这就和使用本地缓存的初衷不一致了啊,我就是想减少数据库访问或者redis连接的,结果现在还要再去访问,我为了什么呢?干嘛不直接用redis啊?

        所以本人认为一旦要考虑本地缓存的一致性问题了,那就说明你这个缓存没必要要了。

      分布式缓存

        分布式缓存解决了各个应用之间的一致性的问题,但它还是会存在与数据库的一致性问题。

        而这个一致性问题的解决方式有很多,具体要根据业务以及数据的特性来确定。

        先说说数据不一致产生的原因,拿redis举例,在逻辑中有两个操作,更新数据库与刷新缓存(有时候缓存信息很多,比如我存了一件商品的信息,里面有商品价格,有商品的库存,如果是更新的话,两个线程并发,首先都先后获取到了缓存的信息,都进行了修改,然后保存的时候一次提交,那这时候肯定有一个是没更新进去的),这两个操作都有可能失败,但是数据库可以开启事务,失败了就回滚,但是redis可没有回滚这一说,所以要想保证数据和缓存的一致性,大家就要想尽办法来解决这个问题。

        ①开启事务

         如果要求强一致性,缓存更新失败可以让数据库事务回滚。先执行逻辑更新数据库,然后刷新缓存。这样会有三种情况:1、都成功,皆大欢喜;2、数据库刷新成功,缓存刷新失败,数据库回滚,缓存未变,数据库也未变;3、数据库失败,直接回滚;

       ②刷新数据,操作缓存

        系统并发没那么高或者对一致性要求不高的可以采用更新缓存,删除的缓存可以消除并发操作缓存导致的数据不一致的问题。这种情况下如果更新缓存失败,可以根据业务的需求来确定是否采取重试机制或者采用MQ消息进行异步修改策略。

        ③延迟双删

        具体步骤是:删除缓存,刷新数据库,延迟1-2秒,删除数据库。

        刷新缓存失败的可能性比删除的可能性大很多,所以采用删除缓存的方式。并发高的情况下还要考虑到刷新缓存也可能会导致数据不一致的问题,所以采用删除的方式。第二次的删除是防止在第一次删除缓存,数据还未落到数据库的时候有线程查缓存没查到直接拿的数据库,之后又将数据缓存;虽然这第二次删除可能会导致删除掉正确数据,但是顶多会发生一次cache miss。

        总的来说,针对分布式缓存与数据库的一致性问题,要根据具体情况来进行分析,并发量大且要求一致性高的数据就延迟双删,至于之后可能产生的缓存击穿问题,那就是另外的问题了,缓存击穿有缓存击穿的解决方案。如果并发量不大的情况下,直接刷数据库然后再刷缓存(删或者刷看具体情况),如果失败了可以采用重试机制或者异步MQ消息重试的方式进行解决。

        没有啥完美方案,只能找最适合业务的方案

                 


http://www.ppmy.cn/ops/134961.html

相关文章

Acwing342

这个代码实现了一种结合 连通块分解、拓扑排序 和 Dijkstra 算法 的复杂图的最短路径计算方法,适用于含有两类边的图结构:普通边(在连通块内)和特殊边(跨连通块)。 以下是详细的代码讲解,逐步解…

排序算法——快速排序

目录 一、快速排序的原理 二、快速排序的过程 三、代码的实现 四、代码的优化 总结 一、快速排序的原理 快速排序的思想是分治法,将一个大问题分割成几个小问题解决,首先选择一个数作为分水岭,然后让比该数大的都在它的右边&#xff0c…

【数据结构初阶】栈和队列的建立

栈 概念和结构 栈是一种特殊的线性表,它只允许一端进行插入和删除数据操作,这一端被称为栈顶,则另一端被称为栈底,而栈内的数据遵循后进后出,先进后出的原则 入栈:栈的插入操作被称为进栈、入栈、压栈&a…

blockchain实现遇到的问题

区块链分叉 v1114 : 基于python socket 创建TCP server,以中心化的形式暂时实现区块链的状态同步 C:\Users\vin0sen>nc 192.168.137.1 9000 Enter a new data: 111 {"index": 1, "timestamp": "2024-11-14 15:28:53.173112", &quo…

Shell脚本4 -- 数学运算

声明: 本文的学习内容来源于B站up主“泷羽sec”视频【shell (3)脚本参数传递与数学运算】的公开分享,所有内容仅限于网络安全技术的交流学习,不涉及任何侵犯版权或其他侵权意图。如有任何侵权问题,请联系本…

2025年入门深度学习或人工智能,该学PyTorch还是TensorFlow?

随着2025应用人工智能和深度学习技术的举世泛气,还在迷茫于该选择哪个深度学习框架吗?PyTorch和TensorFlow是并立于深度学习世界两座巨塔,但是越来越多人发现,在2025年,PyTorch似乎比TensorFlow更为流行和被接受。下面…

区块链智能合约开发:全面解析与实践指南

随着区块链技术的不断发展,智能合约作为其中的核心组成部分,已经在多个领域展现出了巨大的潜力。智能合约不仅是去中心化应用(DApp)和去中心化金融(DeFi)的基础,也是推动区块链技术应用广泛发展…

跨平台WPF框架Avalonia教程 十一

控件类型 如果您想创建自己的控件,Avalonia中有三个主要的控件类型。首先要做的是选择最适合您使用场景的控件类型。 用户控件(User Controls)​ UserControl是创建控件的最简单方法。这种类型的控件最适合特定于应用程序的“视图”或“页面”。UserControl的创建…