知识点

缓存与数据库一致性

2019-01-19  本文已影响124人  知止9528

究竟先操作缓存,还是数据库?

读操作

(1)尝试从缓存get数据,结果没有命中;
(2)从数据库获取数据,读从库,读写分离;
(3)把数据set到缓存,未来能够命中缓存;

写操作
有两个方案:
(1)先操作数据库,再操作缓存;

(2)先操作缓存,再操作数据库;
并且,希望保证两个操作的原子性,要么同时成功,要么同时失败。


当原子性被破坏的时候,分别会发生什么?

一、先操作数据库,再操作缓存

(1)先操作数据库,成功;
(2)再操作缓存(delete或者set),也成功;

但如果这两个动作原子性被破坏:

情况一:

第一步成功,第二步失败,会导致,数据库里是新数据,而缓存里是旧数据,业务无法接受。


二、先操作缓存,再操作数据库

(1)先操作缓存(delete或者set),成功;
(2)再操作数据库,也成功;

如果第一步就失败,也可以返回调用方50X,不会出现数据不一致。

这里又分了两种情况:

(1)操作缓存使用set

(2)操作缓存使用delete

使用set的情况:
第一步成功,第二步失败,会导致,缓存里是set后的数据,数据库里是之前的数据,数据不一致,业务无法接受。

使用delete的情况:
第一步成功,第二步失败,会导致,缓存里没有数据,数据库里是之前的数据.乍一看是没问题,认真看就有问题

存在的一些问题

当然,先删除缓存,再操作数据库,在高并发读写下,理论上也会有数据不一致的情况发生

1.写请求删除了缓存
2.读请求读了缓存
3.读请求查询DB
4.写请求更新DB
5.读请求把旧值写入缓存

即读请求读取到的数据库的值是旧值,然后读请求又把旧值设置进去了

还有就是先把缓存删除了,然后再去操作数据库,如果数据库失败回滚了,而缓存并不能回滚。在以缓存为主的系统,这种方案是不允许的.

这里的问题是,读请求并不知道它获取到的值是旧值,所以我们的解决思路是怎么来让知道?

引入时间戳
假设A为读线程,B为写线程
线程A查询DB前记录当前系统时间戳T1,写入缓存前拿到key时间戳T2,如果存在,则
做比对,如果T2>T1,则说明拿到的是旧值,则不写入缓存

示例如下:
场景1:线程B先拿到锁

缓存数据不存在,线程A记下当前时间戳T1,然后查DB
线程B更新DB,提交事务
线程B获取锁成功
线程B更新key时间戳T2
线程B删除缓存中的旧值
线程B释放锁
线程A获取锁成功
线程A获取key时间戳T2
线程A判断到T2>T1,则不会把旧值写入缓存

场景2:线程A先拿到锁

缓存数据不存在,线程A记下当前时间戳T1,然后查DB
线程B更新DB,提交事务
线程A获取锁成功
线程A获取key时间戳T2
线程A判断T2<T1(或T2不存在),则把旧值写入缓存
线程A释放锁
线程B获取锁成功
线程B更新key时间戳T2
线程B删除缓存中的旧值
可以看到,即使线程A存在写入旧值可能,最终也会很快被清理掉

上一篇下一篇

猜你喜欢

热点阅读