新入职的同事问我,为什么会出现数据库和缓存不一致的问题?
The following article is from Hollis Author Hollis
但是,与此同时也带来了一个问题,那就是如何保证缓存和数据库之间的数据一致性?
在讨论怎么做之前,我们先来看看为什么会出现缓存一致性的问题呢?这些问题是如何发生的呢?
非并发的情况
首先,我们在非并发的场景中,出现不一致的问题大家都能比较容易的理解,因为缓存的操作和数据库的操作是存在一定的时间差的。
而且这两个操作是没办法保证原子性的,也就是说,是有可能一个操作成功,一个操作失败的。
所以,这就必然会存在不一致的情况。
但同时,因为我们的业务系统是开放给用户使用的,所以经常会出现各种各样的并发的场景,因为并发的存在,会使得数据一致性的问题更加的多。
对于数据的操作,无外乎就是读和写两种,那么就会同时存在"读读并发"、"读写并发"和"写写并发"。
对于两个读线程,即使发生并发,因为只是读的动作,所以不会有数据的变更,发生了并发的话也不会有数据不一致的情况。
接下来我们先来分析比较容易理解的"写写并发"的情况。
写写并发
因为在数据库和缓存的操作过程中,可能存在"先写数据库,后删缓存"、"先写数据库,后更新缓存"、"先删缓存库,后写数据库"以及"先更新缓存库,后写数据库"这四种。
其中"删缓存"的这两种是把缓存清空,所以"写写并发"不会存在缓存和数据库不一致的情况。我们就看"更新缓存"的这两种:
先写数据库,后更新缓存:
先更新缓存,后写数据库:
以上两种情况,都是两个写线程并发之后,因为乱序的问题,导致最终缓存中的值是20,而数据库中的值是10,最终导致缓存和数据库中的值不一致的情况。
除了写写并发之外,还有一种比较容易被忽视的情况,那就是读写之间的并发也会导致数据库和缓存的不一致。
读写并发
我们知道,当我们使用了缓存之后,一个读的线程在查询数据的过程是这样的:
1、查询缓存,如果缓存中有值,则直接返回
2、查询数据库
3、把数据库的查询结果更新到缓存中
所以,对于一个读线程来说,虽然不会写数据库,但是是会更新缓存的,所以,在一些特殊的并发场景中,就会导致数据不一致的情况。
读写并发的时序如下:
也就是说,假如一个读线程,在读缓存的时候没查到值,他就会去数据库中查询,但是如果自查询到结果之后,更新缓存之前,数据库被更新了,但是这个读线程是完全不知道的,那么就导致最终缓存会被重新用一个"旧值"覆盖掉。
这也就导致了缓存和数据库的不一致的现象。
但是这种现象其实发生的概率比较低,因为一般一个读操作是很快的,数据库+缓存的读操作基本在十几毫秒左右就可以完成了。
而在这期间,更好另一个线程执行了一个比较耗时的写操作的概率确实比较低。
当然,根据墨菲定律,只要有可能发生的事情,就一定会发生。所以我们也要引起重视。
我们在本文中介绍了数据库和缓存因为双写存在的不一致的情况,无论是单线程还是多线程,都是有可能会出现数据不一致的。
本文中还提到了在数据库和缓存的操作过程中,可能存在"先写数据库,后删缓存"、"先写数据库,后更新缓存"、"先删缓存库,后写数据库"以及"先更新缓存库,后写数据库"这四种。
那么,到底是应该删除缓存好呢,还是更新缓存好呢?到底应该先操作数据库呢还是先操作缓存呢?哪种方案更好呢?又该如何选择呢?
我们在后面的文章中再展开介绍。后面几篇文章会基于本文作为前提展开,所以大家要先能理解不一致问题出现在什么时候,才能知道要怎么做才能解决。
<END>
程序员专属T恤
推荐阅读: