V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhongpingjing
V2EX  ›  Java

你们引入缓存中间件,会去解决数据库缓存双写一致性的问题吗

  •  1
     
  •   zhongpingjing · 2021-06-03 16:57:23 +08:00 · 3805 次点击
    这是一个创建于 1029 天前的主题,其中的信息可能已经有所发展或是发生改变。
    看了一篇博文了解了一些相关的内容,了解到引起不一致性的原因:
    先删缓存,再更新 DB (这种策略应该没有人用?)
    先更新 DB,再删缓存(这种发生不一致性的概率虽然可以忽略不计,但是是否需要去防止?)
    先更新 DB,然后删除缓存失败(真的会发生删除缓存失败吗?网络原因?)
    DB 主从复制的延迟造成不一致
    。。。

    解决这个双写一致性问题,需要双删和重试。感觉这种方式代价有点大啊。
    在实际生产中,会这样去解决吗?有没有更好的办法?
    29 条回复    2021-06-15 17:55:02 +08:00
    wqhui
        1
    wqhui  
       2021-06-03 17:16:31 +08:00
    看业务是不是要求强一致性,还是说最终一致性也可以,强一致性的话代价肯定是大的了
    BBCCBB
        2
    BBCCBB  
       2021-06-03 17:17:47 +08:00
    没有解决.. 不能容忍的地方我们都查 db.

    只要两个地方没有事务. 总是有不一致的窗口存在.
    vindac
        3
    vindac  
       2021-06-03 17:17:56 +08:00
    我们都是缓存定时淘汰,有个服务去更新缓存。。
    zhongpingjing
        4
    zhongpingjing  
    OP
       2021-06-03 18:01:02 +08:00
    @vindac 意思是你们的业务服务自身不会去更新缓存是吗,那如果缓存过期了,会去查 DB 呢还是直接返回 null ?去查 DB 的话,查完会把结果放到缓存中吗?
    abigeater
        5
    abigeater  
       2021-06-03 18:02:58 +08:00
    业务操作对缓存操作不直接写到 DB
    服务定时将缓存写到 DB
    读取从缓存读取 没有数据从 DB 读取后写到缓存
    zhongpingjing
        6
    zhongpingjing  
    OP
       2021-06-03 18:04:55 +08:00
    @vindac 专门的更新缓存服务,那要如何定位到失效缓存,如何更新它啊,比如缓存 A 过期了,缓存服务就需要去 DB 拉取数据放到缓存中,如何发现缓存 A 失效了,并及时更新它
    raaaaaar
        7
    raaaaaar  
       2021-06-03 18:06:18 +08:00 via Android
    定时是个思想,大概可以参考下 cache 和内存的做法?没命中时再装载到 cache 里,满了就置换,根据修改位先写会再换什么的
    zhongpingjing
        8
    zhongpingjing  
    OP
       2021-06-03 18:06:34 +08:00
    @abigeater 这样有可能丢失数据
    zhongpingjing
        9
    zhongpingjing  
    OP
       2021-06-03 18:08:35 +08:00
    @raaaaaar 有点听不懂大佬在讲什么[笑哭]
    InkAndBanner
        10
    InkAndBanner  
       2021-06-03 18:54:54 +08:00
    @abigeater 从 DB 读 写到缓存里面不好吗?
    potatowish
        11
    potatowish  
       2021-06-03 18:58:29 +08:00 via iPhone   ❤️ 1
    1.先更新 DB,再删缓存
    2.订阅 binlog 日志去单独做更新,比如 canal
    fkname
        12
    fkname  
       2021-06-03 19:00:07 +08:00
    一般有缓存时间兜底
    lsp7572
        13
    lsp7572  
       2021-06-03 19:15:36 +08:00
    延时双删了解下
    zhch602
        14
    zhch602  
       2021-06-03 20:51:17 +08:00
    @lsp7572 别误导人了,延迟双删也就面试八股文用用,真正生产中使用就是找死
    iyaozhen
        15
    iyaozhen  
       2021-06-03 21:07:27 +08:00
    @zhch602 咦,为啥。比较简单有效
    Croxx
        16
    Croxx  
       2021-06-03 21:17:11 +08:00 via iPhone
    leafre
        17
    leafre  
       2021-06-03 22:00:51 +08:00
    删除缓存失败,那就补偿,直到成功为止,单机不一致解决
    主从不一致使用选择性读主解决
    ryd994
        18
    ryd994  
       2021-06-04 07:42:59 +08:00 via Android
    双删和后删有什么区别呢?
    如果你速度快,别人速度慢,你更新完了别人再去取数据。和你先更新再删缓存又有什么区别?
    如果你速度慢,别人速度快。别人把旧值取了,你再去把旧值再删一遍。

    提供的保证是基本一样的,就是缓存对 db 的延迟一致性。考虑到写入者挂掉的情况,这甚至不是最终一致性。

    你如果要求强一致,那只能用锁了。不那么蛋疼的办法就是:写一个中间件代理。对同一数据的请求由这同一台代理序列化。中间件可以在修改数据库的时候加锁。用本地锁实现了强一致。
    但是现在问题就转移到了如何保证对同一数据的请求总是由同一代理处理的问题。看起来是 hash 一下不难。但是你得考虑代理服务器上线离线的情况。
    bthulu
        19
    bthulu  
       2021-06-04 09:00:36 +08:00   ❤️ 1
    不管双删后删, 因网络问题, 都可能导致数据不一致.
    如果需要最终一致性, 操作 db 时, 同时在数据库中标记一下需要更新的缓存, 因与 db 操作在同一数据库, 可以保证这里的强一致性.
    然后再开定时任务从 db 中捞取需要更新缓存的数据, 去更新缓存, 更新成功后, 再清除此更新标记, 更新缓存失败, 不做处理, 等待下一次任务执行周期就行.
    abigeater
        20
    abigeater  
       2021-06-04 11:33:26 +08:00
    @InkAndBanner em..你这里是 从 DB 读,写到缓存吗 还是从 DB 读写到缓存 这个空格分的不够开不知道是哪个意思
    1.从 DB 读,写到缓存:我这用定时服务从缓存写到 DB 那如果从 DB 读 就会有个时间差 定时服务还没写进 DB 时被读取就会拿到旧数据 那数据就不一致了
    2.从 DB 读写缓存:就是这个意思,只是把从缓存写回到 DB 的压力减少
    abigeater
        21
    abigeater  
       2021-06-04 11:40:56 +08:00
    @zhongpingjing 不会丢吧 边界值可以直接写回 DB 后清缓存和删写回队列 其他的都可以走这个流程 一般 DB 崩都是瞬发的请求导致的 在定时服务写回 DB 可以是频繁的不间断的所以写回 DB 队列应该不会造成堵塞(可能,目前我还没遇过堵塞至少)
    lsp7572
        22
    lsp7572  
       2021-06-04 14:35:28 +08:00
    @zhch602 当然是视业务场景而定,你这一句话找死还没我四个字的信息量大好吧。。起码给个例子,我也能学习。。
    xuanbg
        23
    xuanbg  
       2021-06-04 15:00:44 +08:00
    大多数程序员可能没有意识到的是:缓存失效的时序其实并没有辣么重要。假设先读到数据,然后数据被删除,再用这个数据创建了新的数据。在业务上有问题吗?一点问题就没有呀。例如某个客户被删除了,原先基于这个客户的数据失效了吗?并没有啊,所以你们在纠结什么呢?

    所以,先删数据库再删缓存就行了。
    zhch602
        24
    zhch602  
       2021-06-04 16:07:06 +08:00
    @lsp7572
    @iyaozhen
    因为即使用了延迟双删还是有可能不一致,最关键的是 我没听说过哪个业务敢在线上让线程 sleep 个几百毫秒甚至更多
    至于资料,你们随便在 google 上搜延迟双删第一页就有文章,直接放个链接 https://www.cnblogs.com/zhouj-happy/p/12616906.html
    lsp7572
        25
    lsp7572  
       2021-06-04 17:34:19 +08:00
    @zhch602 你发的这文章只是提出了问题,没有解决问题。

    至于你说的 sleep 这个就是业务来决定的

    延时双删就是一定程度可以解决缓存一致性问题的解决方法
    lsp7572
        26
    lsp7572  
       2021-06-04 17:41:07 +08:00
    另外删除操作可以另起线程,这个是业务方可以考量的虽然会增加编码复杂度
    zhch602
        27
    zhch602  
       2021-06-04 22:31:55 +08:00 via Android
    @lsp7572 解决方法就是没有完美的解决方法,要视业务而定,要强一致只能加锁,最终一致的话延迟双删也是下下策
    JasonLaw
        28
    JasonLaw  
       2021-06-10 21:16:36 +08:00
    liian2019
        29
    liian2019  
       2021-06-15 17:55:02 +08:00
    @potatowish 这得单独整一套分布式缓存服务才行,太重了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3664 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 10:43 · PVG 18:43 · LAX 03:43 · JFK 06:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.