????????欢迎光临,终于等到你啦????????
????我是苏泽,一位对技术充满热情的探索者和分享者。????????
????持续更新的专栏Redis实战与进阶
本专栏讲解Redis从原理到实践
这是苏泽的个人主页可以看到我其他的内容哦????????
努力的苏泽http://suzee.blog.****.net
本期我们重点讲一个后端的必考面试题 也是开发中常遇到的问题--数据一致性问题
上一期我们讲到了 SpringBoot如何结合Redis做一个缓存 实现我们减少对数据库压力的一个目的。还不知道怎么做的小伙伴可以先回顾一下前文http://t.****img.cn/tshRD
下面正文
目录
本期我们重点讲一个后端的必考面试题 也是开发中常遇到的问题--数据一致性问题
上一期我们讲到了 SpringBoot如何结合Redis做一个缓存 实现我们减少对数据库压力的一个目的。还不知道怎么做的小伙伴可以先回顾一下前文http://t.****img.cn/tshRD
下面正文
只要使用Redis缓存
就必然存在缓存和DB数据一致性问题。若数据不一致,则业务应用从缓存读取的数据就不是最新数据,可能导致严重错误。如将商品库存缓存在Redis,若价格不对,则下单时就可能出错,这是难以接受的。
什么是缓存和DB的数据一致性?
所以 这里我们先讨论更新的策略 一共是这么几种情况
先删缓存 再更数据库 查缓存未命中则查询数据库 写入缓存 如下
该策略可能导致数据不一致的问题。例如,线程1更新数据前先删除缓存,这时候线程2查询该缓存,发现不存在,则去DB中获取,得到旧值放入缓存,然后线程1更新数据库。这时候就出现了缓存与数据库不一致的问题。
如图就导致了数据不一致 缓存是10 数据库却是20
所以就有另一种解决方案 先更新数据库 再删缓存
线程1更新了数据库,线程2也更新数据库,这时候由于某种原因,线程2首先更新了缓存,线程1后续更新。 这样就会导致了缓存脏数据的问题,因为目前数据库中存储的是线程2更新后的数据,而缓存存储的是线程1更新的旧数据。
如何解决?
延时双删
1、何为延时双删
它的实现思路是
需要注意的点
延迟的时间如何确定?
总结
只要使用Redis缓存
就必然存在缓存和DB数据一致性问题。若数据不一致,则业务应用从缓存读取的数据就不是最新数据,可能导致严重错误。如将商品库存缓存在Redis,若价格不对,则下单时就可能出错,这是难以接受的。
什么是缓存和DB的数据一致性?
一致性
包含如下情况:
- 缓存有数据 缓存的数据值需和DB相同
- 缓存无数据 DB是最新值
不符合这两种情况的,都属于缓存和DB数据不一致。
所以 这里我们先讨论更新的策略 一共是这么几种情况
先删缓存 再更数据库 查缓存未命中则查询数据库 写入缓存 如下
该策略可能导致数据不一致的问题。例如,线程1更新数据前先删除缓存,这时候线程2查询该缓存,发现不存在,则去DB中获取,得到旧值放入缓存,然后线程1更新数据库。这时候就出现了缓存与数据库不一致的问题。
如图就导致了数据不一致 缓存是10 数据库却是20
所以就有另一种解决方案 先更新数据库 再删缓存
线程1更新了数据库,线程2也更新数据库,这时候由于某种原因,线程2首先更新了缓存,线程1后续更新。 这样就会导致了缓存脏数据的问题,因为目前数据库中存储的是线程2更新后的数据,而缓存存储的是线程1更新的旧数据。
如何解决?
延时双删
1、何为延时双删
延迟双删(Delay Double Delete)是一种在数据更新或删除时为了保证数据一致性而采取的策略。这种策略通常用于解决数据在缓存和数据库中不一致的问题。
具体来说,在某些场景下,我们需要先更新或删除数据库中的数据,然后再更新或删除缓存中的数据,以保证数据的一致性。。
它的实现思路是
在删除缓存之后,让当前线程休眠一段时间,然后再次删除缓存。这个时间段是为了给数据库操作足够的时间来完成,确保数据已经持久化到数据库中。通过延迟双删,可以防止在休眠期间有其他线程读取到旧的缓存数据,从而保证数据的一致性。
值得注意的是,不管哪种方案,都避免不了Redis存在脏数据的问题,只能减轻这个问题,要想彻底解决,得要用到同步锁和对应的业务逻辑层面解决。
RedisUtils.del(key);// 先删除缓存
updateDB(user);// 更新db中的数据
Thread.sleep(N);// 延迟一段时间,在删除该缓存key
RedisUtils.del(key);// 先删除缓存
需要注意的点
上述中(延迟N秒)的时间要大于一次写操作的时间。原因:如果延迟时间小于写入redis的时间,会导致请求1清除了缓存,但是请求2缓存还未写入的尴尬。。。
延迟的时间如何确定?
在业务程序运行时,统计业务逻辑执行读数据和写缓存的操作时间,以此为基础来进行估算。因为这个方案会在第一次删除缓存值后,延迟一段时间再次进行删除,所以称为“延迟双删”。
总结
把几种方案进行总结会得到这样的结论
-
Cache-Aside(旁路缓存)
- 应用程序直接与缓存和数据库对话。
- 优点:适用于读多的应用场景,具有缓存失效弹性,可以直接访问数据库。
- 缺点:可能导致缓存与数据库不一致。
-
Read-Through Cache(读取穿透缓存)
- 缓存与数据库保持一致,当缓存丢失时,从数据库加载数据并填充缓存。
- 优点:适用于读量较大的工作负载,可以预热缓存。
- 缺点:第一次请求数据时会导致缓存丢失,额外的数据加载成本。
-
Write-Through Cache(写入穿透缓存)
- 先将数据写入缓存,再写入数据库,保持缓存与数据库一致。
- 优点:结合了读取穿透缓存的好处,提供数据一致性保证。
- 缺点:引入额外的写延迟。
-
Write-Around(写入绕过缓存)
- 数据直接写入数据库,只有读取的数据才能进入缓存。
- 优点:适用于只写一次、读取次数较少或从不读的情况。
- 缺点:可能导致缓存未命中,读取性能较低。
-
Write-Back(写入回写缓存)
- 应用程序将数据写入缓存,缓存立即确认,并在延迟一段时间后写入数据库。
- 优点:提高写性能,适用于写工作量大的工作负载,对混合工作负载有效。
- 缺点:可能导致数据丢失,对数据库故障具有一定弹性。
根据具体的应用场景和需求,可以选择合适的缓存策略或结合多种策略来提高系统性能和数据一致性。