ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Redis 缓存一致性问题

2022-03-31 23:33:59  阅读:175  来源: 互联网

标签:缓存 删除 数据库 旧值 Redis 更新 线程 一致性


场景

抢券,下单,库存扣减,使用redis库存扣减,然后在更新数据库,结果就导致redis库存扣减成功了,数据库更新失败或者redis下面的代码执行异常导致事务回滚,但是redis却没有回滚,就会导致,数据库库存数量没扣减,但是redis库存扣减了。

分析

需要从两个方面分析 要想保证缓存和数据库「实时」一致性 就需要解决两个问题 1 异常情况, 2 并发情况

更新方案

  1. 先更新缓存,后更新数据库
  2. 先更新数据库,后更新缓存

异常情况下都会导致 缓存脏数据

并发情况下 也会出问题 如下例子

  1. 线程 A 更新数据库(X = 1)
  2. 线程 B 更新数据库(X = 2)
  3. 线程 B 更新缓存(X = 2)
  4. 线程 A 更新缓存(X = 1)

最终 X 的值在缓存中是 1,在数据库中是 2,发生不一致

并发问题 通常解决方案
分布式锁」两个线程要修改「同一条」数据,每个线程在改之前,先去申请分布式锁,拿到锁的线程才允许更新数据库和缓存,拿不到锁的线程,返回失败,等待下次重试。

删除方案

  1. 先删除缓存,后更新数据库
  2. 先更新数据库,后删除缓存

先删除缓存,后更新数据库,第二步操作失败,数据库没有更新成功,那下次读缓存发现不存在,则从数据库中读取,并重建缓存,此时数据库和缓存依旧保持一致。
但如果是先更新数据库,后删除缓存,第二步操作失败,数据库是最新值,缓存中是旧值,发生不一致。所以,这个方案依旧存在问题。

并发情况下 问题

  1. 线程 A 要更新 X = 2(原值 X = 1)
  2. 线程 A 先删除缓存
  3. 线程 B 读缓存,发现不存在,从数据库中读取到旧值(X = 1)
  4. 线程 A 将新值写入数据库(X = 2)
  5. 线程 B 将旧值写入缓存(X = 1)

最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),发生不一致。

依旧是 2 个线程并发「读写」数据:

  1. 缓存中 X 不存在(数据库 X = 1)
    2.线程 A 读取数据库,得到旧值(X = 1)
  2. 线程 B 更新数据库(X = 2)
  3. 线程 B 删除缓存
  4. 线程 A 将旧值写入缓存(X = 1)

最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),也发生不一致。
这种情况「理论」来说是可能发生的,但是 概率「很低」因为它必须满足 3 个条件:

  1. 缓存刚好已失效
  2. 读请求 + 写请求并发
  3. 更新数据库 + 删除缓存的时间(步骤 3-4),要比读数据库 + 写缓存时间短(步骤 2 和 5)

如何保证两步都执行成功?

保证第二步成功执行,就是解决问题的关键。
答案是:异步重试

把重试请求写到「消息队列」中,然后由专门的消费者来重试,直到成功

或者另外一个方案就是 订阅数据库变更日志,再操作缓存。 例如阿里的 canal

至此,我们可以得出结论,想要保证数据库和缓存一致性,推荐采用「先更新数据库,再删除缓存」方案,并配合「消息队列」或「订阅变更日志」的方式来做。

主从库延迟和延迟双删问题

  1. 线程 A 更新主库 X = 2(原值 X = 1)
  2. 线程 A 删除缓存
  3. 线程 B 查询缓存,没有命中,查询「从库」得到旧值(从库 X = 1)
  4. 从库「同步」完成(主从库 X = 2)
  5. 线程 B 将「旧值」写入缓存(X = 1)

最终 X 的值在缓存中是 1(旧值),在主从库中是 2(新值),也发生不一致。
在线程 A 删除缓存、更新完数据库之后,先「休眠一会」,再「删除」一次缓存。(可以 线程 A 可以生成一条「延时消息」,写到消息队列中,消费者延时「删除」缓存。)

可以做到强一致吗?

要想做到强一致,最常见的方案是 2PC、3PC、Paxos、Raft 这类一致性协议,但它们的性能往往比较差,而且这些方案也比较复杂,还要考虑各种容错问题。

这时我们换个角度思考一下,我们引入缓存的目的是什么
没错,性能

一旦我们决定使用缓存,那必然要面临一致性问题。性能和一致性就像天平的两端,无法做到都满足要求
虽然我们可以通过加「分布锁」的方式来实现,但我们也要付出相应的代价,甚至很可能会超过引入缓存带来的性能提升。
所以,既然决定使用缓存,就必须容忍「一致性」问题,我们只能尽可能地去降低问题出现的概率。

参考 :
How to solve the database and cache consistency problem

> [缓存和数据库一致性问题](http://kaito-kidd.com/2021/09/08/how-to-keep-cache-and-consistency-of-db/)

标签:缓存,删除,数据库,旧值,Redis,更新,线程,一致性
来源: https://www.cnblogs.com/aaaak/p/16084735.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有