查看原文
其他

新入职的同事问我,为什么会出现数据库和缓存不一致的问题?

脚本之家 2022-09-23

The following article is from Hollis Author Hollis

 关注脚本之家”,与百万开发者在一起

作者 l Hollis来源 l Hollis(ID:hollischuang)
关于缓存,相信很多人都不陌生,我们通常会在数据库之上搭建一个缓存服务器,将一些高频的数据存储到缓存中,可以提升查询效率,从而提高响应速度以及并发度。


但是,与此同时也带来了一个问题,那就是如何保证缓存和数据库之间的数据一致性?


在讨论怎么做之前,我们先来看看为什么会出现缓存一致性的问题呢?这些问题是如何发生的呢?



非并发的情况

首先,我们在非并发的场景中,出现不一致的问题大家都能比较容易的理解,因为缓存的操作和数据库的操作是存在一定的时间差的


而且这两个操作是没办法保证原子性的,也就是说,是有可能一个操作成功,一个操作失败的。


所以,这就必然会存在不一致的情况。


但同时,因为我们的业务系统是开放给用户使用的,所以经常会出现各种各样的并发的场景,因为并发的存在,会使得数据一致性的问题更加的多。



并发的情况


对于数据的操作,无外乎就是读和写两种,那么就会同时存在"读读并发"、"读写并发"和"写写并发"。


对于两个读线程,即使发生并发,因为只是读的动作,所以不会有数据的变更,发生了并发的话也不会有数据不一致的情况。


接下来我们先来分析比较容易理解的"写写并发"的情况。


   写写并发

因为在数据库和缓存的操作过程中,可能存在"先写数据库,后删缓存"、"先写数据库,后更新缓存"、"先删缓存库,后写数据库"以及"先更新缓存库,后写数据库"这四种。


其中"删缓存"的这两种是把缓存清空,所以"写写并发"不会存在缓存和数据库不一致的情况。我们就看"更新缓存"的这两种:


先写数据库,后更新缓存:

  

                

先更新缓存,后写数据库:



以上两种情况,都是两个写线程并发之后,因为乱序的问题,导致最终缓存中的值是20,而数据库中的值是10,最终导致缓存和数据库中的值不一致的情况。


除了写写并发之外,还有一种比较容易被忽视的情况,那就是读写之间的并发也会导致数据库和缓存的不一致。


 读写并发

我们知道,当我们使用了缓存之后,一个读的线程在查询数据的过程是这样的:


1、查询缓存,如果缓存中有值,则直接返回 

2、查询数据库 

3、把数据库的查询结果更新到缓存中


所以,对于一个读线程来说,虽然不会写数据库,但是是会更新缓存的,所以,在一些特殊的并发场景中,就会导致数据不一致的情况。


读写并发的时序如下:



也就是说,假如一个读线程,在读缓存的时候没查到值,他就会去数据库中查询,但是如果自查询到结果之后,更新缓存之前,数据库被更新了,但是这个读线程是完全不知道的,那么就导致最终缓存会被重新用一个"旧值"覆盖掉。


这也就导致了缓存和数据库的不一致的现象


但是这种现象其实发生的概率比较低,因为一般一个读操作是很快的,数据库+缓存的读操作基本在十几毫秒左右就可以完成了。


而在这期间,更好另一个线程执行了一个比较耗时的写操作的概率确实比较低。


当然,根据墨菲定律,只要有可能发生的事情,就一定会发生。所以我们也要引起重视。



总结


我们在本文中介绍了数据库和缓存因为双写存在的不一致的情况,无论是单线程还是多线程,都是有可能会出现数据不一致的。


本文中还提到了在数据库和缓存的操作过程中,可能存在"先写数据库,后删缓存"、"先写数据库,后更新缓存"、"先删缓存库,后写数据库"以及"先更新缓存库,后写数据库"这四种。


那么,到底是应该删除缓存好呢,还是更新缓存好呢?到底应该先操作数据库呢还是先操作缓存呢?哪种方案更好呢?又该如何选择呢?


我们在后面的文章中再展开介绍。后面几篇文章会基于本文作为前提展开,所以大家要先能理解不一致问题出现在什么时候,才能知道要怎么做才能解决。


<END>

程序员专属T恤

商品直购链接 👇

  推荐阅读:

这是一件程序员才懂的T恤

突发状况,数据库表被锁,抓瞎了?
都是同样条件的mysql select语句,为什么读到的内容却不一样?

如何保证数据库和缓存双写一致性?

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存