分布式系统中,缓存和数据库数据一致性问题总结
分布式系统中,缓存和数据库数据一致性问题总结
文章无格式见谅
首先缓存的更新主要分两个机制(被动更新和主动更新缓存)
日常使用中会存在三者:
调用方(用户) 缓存 数据提供方(数据库)
被动更新
主动更新:数据库更新完会直接更新缓存,来保证数据库和缓存都是最新的数据
这样会出现几种情况,让我们一一解答
先更新缓存,再更新数据库(数据不一致风险比较大,一般不使用)
先更新数据库,再更新缓存(一般也不使用):如果缓存值是经过长时间计算去更新的话,这样会浪费系统的资源和性能,如果缓存值没人去查询的话,那么就更浪费资源了
先删除缓存,再更新数据库(面试面的最多(双删),但不推荐使用):
1. 首先一个更新请求过来了,操作流程:先删除缓存-----更新数据库----再删除缓存 1.1 (会造成的问题)如果两个请求同时打过来(一个更新一个查询),先删除(这时缓存没数据),理想的情况下是先更新,然后再查询这样缓存就变成了新的数据 1.2 但是这个是理想的情况,因为大概率数据库的操作都是读比写快。这会导致还没更新完数据的时候缓存已经是旧的数据,这样就需要第二次删除缓存,也就是所谓的双删。 1.3 那么我们经常听到的延迟双删是什么呢,就是当更新完缓存的时候过一段时间再去删除缓存。那么这个时间我们怎么控制呢,没有一个准确的值,不同系统不同的时间。这样会造成系统的吞吐量下降。 1.4 补充一点 之前有面试会问到,延迟双删有这几步,那么其中某一步失败了怎么办??? (1)删除缓存(刚开始就失败,就当请求没来过) (2)更新数据库(更新失败那么事务就回滚) (3)第二次删除缓存(主要问题点是在这个步骤):如果前面两步的操作没办法回滚的时候,那么这个时候最便宜的做法就是《硬着头皮往前走,重试重试再重试》。 (3.1)还可以借助消息队列,来重发处理失败的消息,如果继续失败,那么就把他丢到死信队列,通知人工介入等等。(但是这样会使我们的代码高度耦合),(可以借助canal或者类似于数据库binlog等机制来处理),重新写一个方法来一直读取我们失败的来不断的去删除,这样解耦会更好一点。
先更新数据库,再删除缓存(最常用的)
这也就是cache-aside模式:主要原理是系统大概率的操作都是读比写快
说到这里就会有人不太理解:
有一种情况:两个请求,一个读A,一个更新B。前提:缓存没数据 1. A查缓存—缓存没数据—读数据库—(旧值) 2. B更新数据库—新值 3. B删除缓存 4. A将数据(旧值)写入缓存 上述这种情况是存在的,但是回到了最根本的一点—>没有绝对完美的解决方法 如果遇到了上述读比写还慢的情况,那么就再删除一次,(延迟双删)
还有额外的两种方法(中小项目一般不用)
1.Read/Write Through :直接操作缓存,不存在缓存是旧值的情况
具体操作就是当程序启动的时候,将数据库的数据(字典表,订单信息等等)放到缓存中,不能等程序启动之后再去放(启动之后意味着可以接收请求)
2.Write Behind :直接把缓存当作主数据库,一切都操作缓存,写完直接返回
需要数据进行持久化的时候,需要介入异步或者队列来操作数据库 (缺点:缓存崩了,那么数据就丢了), (优点:减低了写操作的时间,增大了系统的吞吐量)