先更新缓存仍是先更新数据库

概览

说这个问题以前得看下几种缓存模式,能够先看下缓存模式(Cache Aside、Read Through、Write Through、Write Behind)这篇文章。html

先更新缓存,再更新数据库

考虑并发操做:线程A写,线程B读

先更新缓存再更新数据库1

  • 一、线程A发起一个写操做,第一步delete cache
  • 二、此时线程B发起一个读操做,cache miss
  • 三、线程B继续读数据库,读出来一个老数据
  • 四、而后老数据入cache
  • 五、线程A写入了最新的数据

这样之后每次从缓存中读到的都是老数据,数据不一致,不能知足咱们的需求。shell

既然这种状况下先删除缓存会有数据不一致的状况,那咱们来试试第一步不删除缓存而是直接更新缓存试试看。数据库

考虑并发操做:线程A写,线程B写

先更新缓存再更新数据库2

  • 一、线程A发起一个写操做,第一步set cache
  • 二、线程B发起一个写操做,第一步set cache
  • 三、线程B写入数据到数据库
  • 四、线程A写入数据到数据库

这样之后每次从缓存中读到的都是线程B设置数据,但数据库中存储的是线程A写入的数据,致使数据不一致。缓存

小结

可看到先操做缓存不管是先删除缓存仍是先更新缓存都会发生数据不一致的状况,因此不推荐两种作法。并发

先更新数据库,再更新缓存

考虑并发操做:线程A写,线程B读

先更新数据库再更新缓存1

  • 一、线程A发起一个写操做,第一步写入数据到数据库
  • 二、线程A第二步delete cache
  • 三、线程B发起一个读操做,cache miss
  • 四、线程B从数据库获取最新数据
  • 五、线程B同时set cache

注意咱们的更新是先更新数据库,成功后,让缓存失效。ide

一个是查询操做,一个是更新操做的并发,首先,没有了文章开始删除cache数据的操做了,而是先更新了数据库中的数据,此时,缓存依然有效,因此,并发的查询操做拿的是没有更新的数据,可是,更新操做立刻让缓存的失效了,后续的查询操做再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题,后续的查询操做一直都在取老的数据。post

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。为何不是写完数据库后更新缓存?你能够看一下Quora上的这个问答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕两个并发的写操做致使脏数据,这个问题咱们下面会说到。.net

考虑并发操做:线程A写,线程B写,线程C读

先更新数据库再更新缓存2

  • 一、线程A发起一个写操做,第一步写入数据到数据库
  • 二、线程B发起一个写操做,第一步写入数据到数据库
  • 三、线程B第二步delete cache
  • 四、线程C发起一个读操做,cache miss
  • 五、线程C从数据库获取最新数据
  • 六、线程C同时set cache
  • 七、线程A第二步delete cache

该状况下因为线程A、B最初都把数据写入了数据库,接着都有delete cache,此时若是有线程C来读数据,你会发现无论线程C的动做作任意顺序穿插在A、B动做之间,最后查询数据最差也就是在线程A、B删除cache以前获取到了旧数据,其他都会获取到新数据,并不会影响后来的请求获取到新数据。线程

为何最后是把缓存的数据删掉,而不是把更新的数据写到缓存里。这么作引起的问题是,若是A、B两个线程同时作数据更新,A先更新了数据库,B后更新数据库,则此时数据库里存的是B的数据。而更新缓存的时候,是B先更新了缓存,而A后更新了缓存,则缓存里是A的数据。这样缓存和数据库的数据会发生不一致。3d

小结

可看到:

  • 先操做数据库再删除缓存能有不错的结果(最差是操做未结束时会获取到旧数据,不会影响后续请求),因此最推荐这种作法。
  • 先操做数据库再更新缓存可能形成数据不一致的场景,不推荐这种作法。

参考

相关文章
相关标签/搜索