美文网首页知识点
缓存与数据库一致性

缓存与数据库一致性

作者: 知止9528 | 来源:发表于2019-01-19 14:45 被阅读124次

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

    读操作

    (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存在写入旧值可能,最终也会很快被清理掉

    相关文章

      网友评论

        本文标题:缓存与数据库一致性

        本文链接:https://www.haomeiwen.com/subject/igfzdqtx.html