如何保证数据库和缓存的一致性
10 分钟阅读
•
1311 字
+
723 词
删除缓存的方法:
读取数据的逻辑:
Cache Aside
(先更新数据库再删除缓存)
Cache Aside 是最基本的缓存模式,在这个模式下,业务代码就是==
把缓存看成是和数据库一样的独立的数据源
==,然后业务代码控制怎么写入缓存,怎么写入数据库。一般来说,都是
优先写入数据库
的。
不管是先写数据库还是先写缓存,
Cache Aside 都不能解决数据一致性问题。
- 去缓存查数据
- 如果缓存查询数据失败,就查询数据库
- 数据库获取到数据,再更新缓存
- 再返回
-
先删除缓存再更新数据库
- 可能会造成数据不一致 ,假设线程1先删除缓存,然后在更新数据库的过程中 发生了网络延迟 。这个时候还有线程2进行读操作,首先由于缓存已经被删了,所以缓存未命中,然后读数据库,将旧的数据更新到了缓存。线程1的网络好了,然后更新数据库。但是这个时候缓存和数据库的数据发生了不一致。
- 解决办法:使用 延迟双删 ,写操作 更新了数据库后停顿一个时间再进行删除缓存 。这样子最多其他线程在这个时间段里读到的是脏数据。
-
先更新数据库再删除缓存(cache aside)
- 假如在删除缓存时失败,那就用重试机制。
- 在高并发的时候,重试最好的方法是异步,比如发送消息给mq中间件,实现异步解耦。
参考答案:
-
原理
:
- 读:== 先从缓存中读取数据,如果没有就再去数据库里面读数据,然后把数据放回缓存中,如果缓存中可以找到数据就直接返回数据; ==
- 写:== 更新数据的时候先把数据持久化到数据库(先),然后再让缓存失效(后)。 ==
- 问题 :假如有两个操作一个更新一个查询,第一个操作先更新数据库,还没来及删除缓存,查询操作可能拿到的就是旧的数据;更新操作马上让缓存失效了,所以后续的查询可以保证数据的一致性;还有的问题就是有一个是读操作没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,也会造成脏数据, 出现了缓存中数据和数据库数据不一致。
- 可行性 :出现上述问题的概率其实非常低,需要同时达成读缓存时缓存失效并且有并发写的操作。数据库读写要比缓存慢得多,所以读操作在写操作之前进入数据库,并且在写操作之后更新, 概率比较低 。
- 解决方法依旧使用 延迟双删
- 原理 : Read/Write Through原理是把更新数据库(Repository)的操作由缓存代理,应用认为后端是一个单一的存储,而存储自己维护自己的缓存。
- Read Through :就是在 查询操作中更新缓存 ,也就是说,当缓存失效的时候,== Cache Aside 策略是由 调用方负责 ==把数据加载入缓存,而==Read Through则 用缓存服务自己来加载 ,从而对调用方是透明的==。
- Write Through :当有 数据更新的时候,如果没有命中缓存,直接更新数据库 ,然后返回。如果命中了缓存,则更新缓存,然后再由缓存自己更新数据库(这是一个同步操作)。
- 原理 : 在更新数据的时候,只更新缓存,不更新数据库,而缓存会异步地批量更新数据库或者缓存过期的时候再刷到数据库。 这个设计的好处就是 让数据的I/O操作非常快 ,带来的问题是,数据 不是强一致性的 ,而且可能会丢。
- 第二步失效问题 :这种可能性极小,缓存删除只是标记一下无效的软删除,可以看作不耗时间。如果会出问题,一般程序在写数据库那里就没有完成:故意在写完数据库后,休眠很长时间再来删除缓存。