数据库与缓存双写一致性问题

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

流程图

image.png

造成双写不一致问题描述

比如有两个写请求并发过来修改同一个数据信息,分别由线程A和线程B进行处理。
线程A先处理写请求1,线程B后处理写请求2。
(1)线程A处理写请求1,先更新对应的数据表的值;
(2)线程B处理写请求2,先更新对应的数据表的值;
(3)线程A由于网络原因,在处理更新缓存的时候,有一定时间的阻塞;
(4)线程B早于线程A更新了数据的缓存值;(最新的数据值)
(5)线程A晚于完成B更新了数据的缓存值,此时,缓存的值就又被调整为了旧值。

出现了 数据库(新值) 与 缓存(旧值) 数据不一致的问题。

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

流程图

image.png

造成双写不一致问题描述

线程A,接收一个写请求,
(1)首先去更新数据表的数据,数据更新成功;
(2)在进行下一步删除缓存的操作时,因为一些原因,导致删除缓存的步骤失败,删除不成功;
(3)此时,数据库存储的数据值为更新后的新值,缓存存储的数据值还是更新前的旧值,数据不一致。

出现了 数据库(新值) 与 缓存(旧值) 数据不一致的问题。

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

这种方式下的双写方案,在一定程度上可以保证数据库与缓存的双写一致性。
删除缓存后,即使在更新数据库的操作失败了,那下一次读取缓存的时候,仍然读取的是数据库存储的旧值,更新到缓存的数据也是旧值。此时保证了数据库(旧值)与缓存(旧值)里的数据的一致性。

流程图

image.png

造成双写不一致问题描述

在并发读写请求的场景下,这种方式就无法保证 数据库与缓存双写的一致性。
(1)线程A接收一个写请求,线程B接收一个读请求。
(2)线程A先执行删除缓存的操作;
(3)删除缓存成功后,线程B开始读取数据,首先就会先从缓存当中读取,发现未命中数据;
(4)此时线程B就从数据库读取数据值(旧值),然后将该旧值更新至缓存中;
(5)线程A更新数据表,将数据更新为新值。

此时,就出现了数据库(新值)与缓存(旧值)不一致的问题。

解决数据库与缓存双写不一致的方案

实现数据库与缓存更新读取操作的异步串行化。

流程图

image.png

流程解释

(1) 并发接收对一条数据的读写请求。然后可以根据这条数据的字段信息,进行类似hash取值的操作,目的是生成唯一标识,能够路由到固定的jvm内存队列。
(2) 首先,线程A的写操作,先处理删除缓存的操作,将删除缓存操作和更新数据表操作压入内存队列当中(删除缓存中数据旧值)。
(3) 线程B的读操作开始读取缓存,因为缓存旧值已经被删除,所以读取缓存未命中数据。
(4) 此时,会将线程B要读取数据库获取数据值然后更新缓存的操作放入到内存队列当中。
(5) 等待线程A更新数据库的数据值(更新为新值)。更新缓存的操作才会执行(两个操作为串行化,更新缓存的操作必须等待更新数据库操作执行完毕)。

这个流程下来,可以保证,数据库和缓存里的数据在读写并发的情况下,是能够保持一致的。