让数据库和缓存数据保持一致的三种策略

如何保证缓存和数据库的一致性,这算得上是个老生常谈的话题啦,看到好多技术新人在写更新缓存数据代码 , 采用了非常复杂甚至“诡异”的方案,甚为不解 。一、背景目前随着缓存架构方案越来越成熟化,通常做法是引入「缓存」来提高读性能 , 架构模型就变成了这样:

让数据库和缓存数据保持一致的三种策略

文章插图
图片
先来看一下什么时候创建缓存 , 前端请求的读操作先从缓存中查询数据,如果没有命中数据,则查询数据库,从数据库查询成功后 , 返回结果,同时更新缓存,方便下次操作 。
在数据不发生变更的情况下,这种方式没有问题,如果数据发生了更新操作,就必须要考虑如何操作缓存,保证一致性 。
如何保证缓存和数据库的一致性,这算得上是个老生常谈的话题啦,看到好多技术新人在写更新缓存数据代码 , 采用了非常复杂甚至“诡异”的方案,甚为不解 。
今天就一起花点儿时间来聊聊吧~
二、缓存和数据库数据一致性问题(1)先更新缓存,后更新数据库如果缓存更新成功了,但数据库更新失败 , 那么此时缓存中是最新值,但数据库中是「旧值」 。
虽然此时读请求可以命中缓存,拿到正确的值 , 但是,一旦缓存「失效」,就会从数据库中读取到「旧值」,重建缓存也是这个旧值 。
这时用户会发现自己之前修改的数据又「变回去」了 , 对业务造成影响 。
(2)先更新数据库,后更新缓存如果数据库更新成功了,但缓存更新失败,那么此时数据库中是最新值,缓存中是「旧值」 。
之后的读请求读到的都是旧数据 , 只有当缓存「失效」后,才能从数据库中得到正确的值 。
这时用户会发现,自己刚刚修改了数据,但却看不到变更,一段时间过后,数据才变更过来 , 对业务也会有影响 。
可见,上面两种情况,无论谁先谁后 , 但凡后者发生异常,就会对业务造成影响 。那怎么解决这个问题呢?
三、缓存更新Design Pattern介绍几个也许有效的套路给大家吧~ 希望有帮助 。
(1)Cache Aside Pattern
让数据库和缓存数据保持一致的三种策略

文章插图
图片
让数据库和缓存数据保持一致的三种策略

文章插图
图片
如上图所示,一个是查询操作 , 一个是更新操作的并发 。
首先,没有了删除cache数据的操作了,而是先更新了数据库中的数据 , 此时,缓存依然有效 , 所以,并发的查询操作拿的是没有更新的数据,但是 , 更新操作马上让缓存的失效了,后续的查询操作再把数据从数据库中拉出来 。而不会像文章开头的那个逻辑产生的问题,后续的查询操作一直都在取旧数据 。
那么,是不是Cache Aside这个就不会有并发问题了?
不是的 。
比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作 , 写完数据库后,让缓存失效 , 然后,之前的那个读操作再把老的数据放进去 , 所以,会造成脏数据 。
(2)Read/Write Through Pattern
  • Read Through
Read Through 套路就是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的 。
  • Write Through
Write Through 套路和Read Through相仿,不过是在更新数据时发生 。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回 。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)
操作逻辑如下图所示:
让数据库和缓存数据保持一致的三种策略

文章插图
图片
(3)Write Behind Caching Pattern基本逻辑如下:
让数据库和缓存数据保持一致的三种策略

文章插图
图片
Write Behind 又叫 Write Back 。
简单说就是 , 在更新数据的时候,只更新缓存 , 不更新数据库,而我们的缓存会异步地批量更新数据库 。这个设计的好处就是让数据的I/O操作飞快无比(直接操作内存的嘛 ) , 因为异步,write backg还可以合并对同一个数据的多次操作 , 所以性能的提高是相当可观的 。


推荐阅读