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

关于 MySQL 和 Redis 一致性问题

  •  
  •   Dganzh · 2022-03-03 21:33:56 +08:00 · 2750 次点击
    这是一个创建于 756 天前的主题,其中的信息可能已经有所发展或是发生改变。

    MySQL 作为 DB ,Redis 作为缓存,两个系统会存在一致性问题,最近想到一个方案, 大家说说这种方案有什么优缺点?

    写过程:

    1. 先抢锁( lua 搞):redis.setnx(lock_key)
      1.1 失败则自旋直至抢到成功
      1.2 成功,根据更新数据库所需时间设置过期时间,然后进入 2
    2. 更新数据库:db.update()
    3. redis.set(data_key)
    4. redis.del(lock_key)

    读过程(根据业务下面 3 选 1 ):

    1. 能接受旧数据的业务,则直接读缓存,redis.get(data_key)。
    2. 要求读到最新数据的业务,则先读 redis.get(lock_key ),如果为空则 redis.get(data_key)返回;不为空则直接读数据库,然后再 redis.setnx(data_key),也就是如果这里存在 data_key 则不设置缓存了。
    3. 上面 2 的过程 redis.get(lock_key)和 redis.get(data_key)之间可能存在其他的写过程,且刚好执行到 2 ,这时候仍可能读到旧数据。所以,这里可以同样先抢锁,redis.setnx(lock_key),抢到锁再 redis.get(data_key),为空则回源数据库然后再设到 redis 。
    10 条回复    2022-03-04 14:31:42 +08:00
    fishCatcher
        1
    fishCatcher  
       2022-03-03 22:06:09 +08:00 via iPhone   ❤️ 1
    2 回写 redis 的时候如果 db 被更新,那你回写的就是脏数据,后面读到的都是脏数据,肯定是不对的。

    3 加锁会阻塞所有其他读写,toc 业务不建议用。

    这种一般都有现成的方案,不建议自己造轮子。
    bxb100
        2
    bxb100  
       2022-03-03 22:38:07 +08:00
    延时双删
    giiiiiithub
        3
    giiiiiithub  
       2022-03-03 23:05:43 +08:00   ❤️ 1
    现成方案,不要造轮子。

    非严格一致可以参考现成方案
    https://coolshell.cn/articles/17416.html
    bigbyto
        4
    bigbyto  
       2022-03-04 00:45:09 +08:00
    你这相当于把操作串行了,而且可靠性依赖 redis 的锁,但 redis 的锁并不可靠。A 获得锁,此时发生 gc stop the world ,锁过期,此时 B 获得锁成功。

    这问题的本质就是同时更新两个数据源,如果要求严格的一致性,就需要把所有的操作作为一个原子操作提交,这个实际上是很困难的,比如你可以引入 NPC 协议,这样系统复杂度会大大增加且引入了新的单点,只能在性能和一致性之间做 trade off
    Maboroshii
        5
    Maboroshii  
       2022-03-04 02:09:04 +08:00 via Android
    最终一致,不要管 mysql 了 纯 redis 操作
    Brian1900
        6
    Brian1900  
       2022-03-04 03:51:48 +08:00
    是不是 redis 没用对地方
    Tenlearn
        7
    Tenlearn  
       2022-03-04 09:50:09 +08:00
    @bxb100 延时双删实践上就是扯淡,除了串行没有任何方案能保证完全一致,还是要加过期时间
    leafre
        8
    leafre  
       2022-03-04 10:53:51 +08:00
    必须强一致性,用粗粒度锁,并发冲突概率极大,性能肯定差,实际生产肯定不这样使用。
    xuanbg
        9
    xuanbg  
       2022-03-04 12:50:03 +08:00
    对于缓存的作用,我发现有很多人其实走进了误区。我们在使用缓存的时候,一定要先明确一点:缓存是用来加速的,仅此而已!
    当你给缓存数据赋予更多的职责的时候,缓存也就不是缓存,而是内存数据库了。两个数据库如何保持一致,这是数据库层面的问题,不要在业务层面去解决。不然业务逻辑将会无比复杂,而且根本就没有完美解决方案。

    如果你 redis 里面的数据只是缓存,那么就要在享受高效读的好处时,就必须要接受缓存数据不够实时带来的一系列问题。
    如果你把 redis 当内存数据库使用,就要接受掉电丢数据的结果。又想读写快,又不想意外丢数据,你想啥呢!
    sunqb
        10
    sunqb  
       2022-03-04 14:31:42 +08:00
    为什么需要保证强一致性?你的业务如果有这个需求,就不应该上 mysql 了,直接 oracle 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   979 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 20:12 · PVG 04:12 · LAX 13:12 · JFK 16:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.