0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Redis缓存更新一致性的方式

jf_ro2CN3Fa 来源:博客园 作者:Finley 2022-11-21 10:40 次阅读

当执行写操作后,需要保证从缓存读取到的数据与数据库中持久化的数据是一致的,因此需要对缓存进行更新。

因为涉及到数据库和缓存两步操作,难以保证更新的原子性。

在设计更新策略时,我们需要考虑多个方面的问题:

对系统吞吐量的影响:比如更新缓存策略产生的数据库负载小于删除缓存策略的负载

并发安全性:并发读写时某些异常操作顺序可能造成数据不一致,如缓存中长期保存过时数据

更新失败的影响:若某个操作失败,如何对业务影响降到最小

检测和修复故障的难度: 操作失败导致的错误会在日志留下详细的记录容易检测和修复。并发问题导致的数据错误没有明显的痕迹难以发现,且在流量高峰期更容易产生并发错误产生的业务风险较大。

更新缓存有两种方式:

删除失效缓存: 读取时会因为未命中缓存而从数据库中读取新的数据并更新到缓存中

更新缓存: 直接将新的数据写入缓存覆盖过期数据

更新缓存和更新数据库有两种顺序:

先数据库后缓存

先缓存后数据库

两两组合共有四种更新策略,现在我们逐一进行分析。

并发问题通常由于后开始的线程却先完成操作导致,我们把这种现象称为“抢跑”。下面我们逐一分析四种策略中“抢跑”带来的错误。

先更新数据库,再删除缓存

若数据库更新成功,删除缓存操作失败,则此后读到的都是缓存中过期的数据,造成不一致问题。

可能存在读写线程竞争导致的并发错误:

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

视频教程:https://doc.iocoder.cn/video/

先更新数据库,再更新缓存

同删除缓存策略一样,若数据库更新成功缓存更新失败则会造成数据不一致问题。

该策略同样存在读写线程竞争导致数据不一致的问题:

ec4a5aa0-68b7-11ed-8abf-dac502259ad0.png

也可能因为两个写线程竞争导致并发错误:

ec6d143c-68b7-11ed-8abf-dac502259ad0.png

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://github.com/YunaiV/yudao-cloud

视频教程:https://doc.iocoder.cn/video/

先删除缓存,再更新数据库

可能发生的并发错误:

ec97c696-68b7-11ed-8abf-dac502259ad0.png

先更新缓存,再更新数据库

若缓存更新成功数据库更新失败, 则此后读到的都是未持久化的数据。因为缓存中的数据是易失的,这种状态非常危险。

因为数据库因为键约束导致写入失败的可能性较高,所以这种策略风险较大。

可能发生的并发错误:

ecd69df8-68b7-11ed-8abf-dac502259ad0.png

两个写线程竞争也会导致数据不一致:

ecf7b934-68b7-11ed-8abf-dac502259ad0.png

解决方案

使用 CAS

CAS (Check-And-Set 或 Compare-And-Swap)是一种常见的保证并发安全的手段。CAS 当且仅当客户端最后一次取值后该 key 没有被其他客户端修改的情况下,才允许当前客户端将新值写入。

funcCAS(oldVal,newVal){
ifcache.get()==oldVal{
cache.set(newVal)
}
}

时间 线程A 线程B 数据库 缓存
0 v0 v0
1 更新数据库为 v1 v1 v0
2 更新数据库为 v2 v2 v0
3 执行 CAS 操作:当且仅当缓存中为 v0 时将 v2 写入缓存 v2 v2
4 执行 CAS 操作:当且仅当缓存中为 v0 时将v1写入缓存。当前缓存为 v2 故放弃写缓存 v2 v2

由上图可见,CAS 可以有效的避免并发错误的发生。

目前一些兼容 Redis 协议的中间件已经提供了 CAS 命令的支持,比如阿里的 Tair 以及腾讯的 Tendis。

Redis 官方提供了 Watch + 事务的方法来支持 CAS, 或者使用 redis 中 lua 脚本原子性执行的特点来实现 CAS。不过由于代码较为复杂,这两种方案都不常见。

使用分布式锁

CAS 假设发生并发问题的概率不大, 所以 CAS 也被称为乐观锁。那么悲观锁能否解决我们的问题呢?

还是以「先更新数据库,再更新缓存」方案中两个写线程竞争为例, 我们要求任何线程在写入或读取数据库前都需要获取排它锁。

时间 线程A 线程B 数据库 缓存
0 v0 v0
1 获取排它锁 v0 v0
2 更新数据库为 v1 v1 v0
3 更新缓存为 v1 v1 v1
4 等待排它锁 v1 v1
5 释放排它锁 v1 v1
6 获得排它锁 v1 v1
7 更新数据库为 v2 v2 v1
8 更新缓存为 v2 v2 v2
9 释放排它锁 v2 v2

分布式锁同样可以解决并发问题,只是成本可能略高。

异步更新

阿里开源了 MySQL 数据库binlog的增量订阅和消费组件 - canal。canal interwetten与威廉的赔率体系 从库获得主库的 binlog 更新,然后将更新数据写入 MQ 或直接进行消费。

我们可以让API服务器只负责写入数据库,另一个线程订阅数据库 binlog 增量进行缓存更新。

因为 binlog 是有序的,因此可以避免两个写线程竞争。但我们仍然需要解决读写线程竞争的问题:

ed2a5a56-68b7-11ed-8abf-dac502259ad0.png

这里同样可以 CAS 解千愁:

ed8614a4-68b7-11ed-8abf-dac502259ad0.png

延时双删

使用删除缓存策略时读线程先开始却后写缓存会导致不一致,那么我们在读线程结束后再次清除缓存是不是就可以解除错误状态了?延时双删就是写线程等待一段时间“确保”读线程都结束后再次删除缓存,以此清除可能的错误缓存数据。

eda9ebb8-68b7-11ed-8abf-dac502259ad0.png

理论上我们无法给出一个时间来“确保”读线程都结束,所以仍有存在并发问题的可能。但是延时双删实现成本很低而且极大的减少了并发问题出现的概率,不失为一种简单实用的手段。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • CAS
    CAS
    +关注

    关注

    0

    文章

    34

    浏览量

    15204
  • MYSQL数据库
    +关注

    关注

    0

    文章

    96

    浏览量

    9389
  • Redis
    +关注

    关注

    0

    文章

    374

    浏览量

    10871

原文标题:讲讲 Redis 缓存更新一致性

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    介绍ARM存储一致性模型的相关知识

    今天要说的这个是存储一致性(memory consistency),不要跟前面讲过缓存一致性(cache coherence)混淆了。
    的头像 发表于 02-14 09:19 1853次阅读

    如何解决数据库与缓存一致性

    数据库压力。当乘客购买成功之后,数据库发生了变化,需要及时更新缓存中的数据,以便于其他乘客能从缓存中及时获取最新车票信息。这就是缓存一致性
    的头像 发表于 09-25 15:25 1106次阅读
    如何解决数据库与<b class='flag-5'>缓存</b><b class='flag-5'>一致性</b>

    Redis缓存和MySQL数据不一致原因和解决方案

    高并发架构系列:Redis缓存和MySQL数据一致性方案详解
    发表于 03-27 15:55

    一致性规划研究

    针对一致性规划的高度求解复杂度,分析主流一致性规划器的求解策略,给出影响一致性规划器性能的主要因素:启发信息的有效,信念状态表示方法的紧凑
    发表于 04-06 08:43 12次下载

    加速器一致性接口

    提供异步缓存一致性直接访问PS的入口。处理器可以标记ACP上的传输为一致性或非一致性。PL端的AXI主机通过ARUSERS[1:0]指示是否为一致性
    发表于 11-17 15:04 3675次阅读

    基于轨迹标签的谣言一致性维护算法

    针对发布/订阅系统中缓存副本一致性维护问题,首先,对原有基于谣言的一致性维护算法进行改进,提出种基于轨迹标签的谣言一致性维护算法。该算法通
    发表于 12-17 11:35 0次下载
    基于轨迹标签的谣言<b class='flag-5'>一致性</b>维护算法

    Cache一致性协议优化研究

    问题的由来.总结了多核时代高速缓存一致性协议设计的关键问题,综述了近年来学术界对一致性的研究.从程序访存行为模式、目录组织结构、一致性粒度、一致性
    发表于 12-30 15:04 0次下载
    Cache<b class='flag-5'>一致性</b>协议优化研究

    自主驾驶系统将使用缓存一致性互连IP和非一致性互连IP

    代ASIL B(D)自主驾驶系统将使用符合ISO 26262标准的缓存一致性互连IP和非一致性互连IP来实现。 美国加利福尼亚州坎贝尔2019年4月26日消息—Arteris IP
    的头像 发表于 05-09 17:13 3221次阅读

    管理基于Cortex®-M7的MCU的高速缓存一致性

    本文档概述了不同场景下的高速缓存一致性问题,并就如何管理或避免高速缓存一致性问题提供了些方法建议。
    发表于 04-01 10:12 5次下载
    管理基于Cortex®-M7的MCU的高速<b class='flag-5'>缓存</b><b class='flag-5'>一致性</b>

    介绍下cpu缓存一致性(MESI协议)

    之前介绍了java并发包的cas原理和java内存模型,这篇我们介绍下cpu缓存一致性原理,可以帮助我们更好的理解cas的底层原理。
    的头像 发表于 06-09 16:01 4661次阅读
    介绍下cpu<b class='flag-5'>缓存</b><b class='flag-5'>一致性</b>(MESI协议)

    管理基于Cortex-M7的MCU的高速缓存一致性

    电子发烧友网站提供《管理基于Cortex-M7的MCU的高速缓存一致性.pdf》资料免费下载
    发表于 09-25 10:11 0次下载
    管理基于Cortex-M7的MCU的高速<b class='flag-5'>缓存</b><b class='flag-5'>一致性</b>

    如何保证缓存一致性

    “ 本文的参考文章是2022年HOT 34上Intel Rob Blakenship关于CXL缓存一致性篇介绍。”
    的头像 发表于 10-19 17:42 1093次阅读
    如何保证<b class='flag-5'>缓存</b><b class='flag-5'>一致性</b>

    redis与mysql如何保持数据一致性

    Redis和MySQL是两个常用的数据库系统,它们都有自己的特点和用途。在某些场景下,我们可能需要将Redis和MySQL进行结合使用,并保持数据的一致性
    的头像 发表于 11-16 11:27 927次阅读

    Redis缓存与Mysql如何保证一致性

    基本流程就是客户端A请求,先去删除缓存,然后将数据写入数据库,此时客户端B查询先去查询缓存缓存没有返回,去查数据库,此时还没有完成主从同步,拿到是从库的旧数据,然后将旧数据进行缓存
    的头像 发表于 12-02 14:23 922次阅读
    <b class='flag-5'>Redis</b><b class='flag-5'>缓存</b>与Mysql如何保证<b class='flag-5'>一致性</b>?

    异构计算下缓存一致性的重要

    在众多回复中,李博杰同学的回答被认为质量最高。他首先将缓存一致性分为两个主要场景:是主机内CPU与设备间的一致性;二是跨主机的一致性
    的头像 发表于 10-24 17:00 509次阅读
    异构计算下<b class='flag-5'>缓存</b><b class='flag-5'>一致性</b>的重要<b class='flag-5'>性</b>