面试必问的缓存与数据库的双写一致性问题!

是个程序,只要使用了缓存 Redis 之类的就会面临双写一致性问题。很多程序员都会栽倒在这个问题上。

面试必问的缓存与数据库的双写一致性问题!

因为,不管你怎么回答,都看起来不是很完美。

首先,我们面临的是,你是先写缓存还是先写数据库。假设,我们是先写缓存,然后再写数据库。那么,当缓存写成功了,数据库写失败了,就出现了不一致。

你可能会说,当数据库写失败后,我再把缓存给删除了。那你能帮助,你删除缓存一定成功吗?还不说,你缓存写成功后,在还未更新数据库的时候,就可能有查询请求来读缓存。

那我们改进一下更新策略。我们先删除缓存,然后再更新数据库,更新成功后,再删除数据库。

这种方法,如果是串行的话,逻辑上是没问题的。但是,串行肯定会导致服务压力过大,甚至崩掉。如果不串行执行,那么在你删除缓存,还未更新数据库的情况下,其他线程将 DB 中的旧数据读到了缓存中。然后你更新 DB,在这个空档期,读缓存读的都是旧数据,直到你执行了后面的再次删除缓存操作。

现在你是不是,感觉先写缓存,再写数据库,这种方案行不通。那么我们再来看看第二种方法,先写 DB,在写缓存。

如果 DB 写失败了,那么就不用写缓存了。但是,如果 DB 写成功了,缓存写失败了呢?数据又不一致了。

并发情况呢?一个请求A做查询操作,一个请求B做更新操作。那么一定有下面的情形产生:

请求A去查缓存时,缓存刚好失效。所以 A 去查数据库,得到一个值;请求B将新值写入数据库,请求B删除缓存,请求A将查到的旧值写入缓存。

但是,如果我说 Facebook 采用的就是这种方案,你会不会显得很惊讶。

Facebook 虽然也知道这个问题。这个 case 理论上虽然会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

最重要的是,就算发生这样的事情,对 Facebook 的一些业务影响不大。在业务和策略上采用双平衡,才是架构的真正艺术。

除此之外,我前面说的:Cache Aside Pattern 缓存旁路模式、Read through Pattern 缓存通读模式、Write through Pattern 缓存通写模式、Write behind caching Pattern 写后缓存模式都不完美。

还是那句话,如果非要保证强一致性,那就采用 2PC、3PC 或是 Paxos 协议保证一致性。

没有一项技术是完美的,任何一项技术都存在驳论。架构更是一门艺术,就看你如何平衡!

猜你喜欢

转载自blog.51cto.com/15127565/2664937