当前位置: 首页 > article >正文

缓存数据一致性保证通用方案

欢迎关注公众号 【11来了】 ,持续 中间件源码、系统设计、面试进阶相关内容

在我后台回复 「资料」 可领取编程高频电子书
在我后台回复「面试」可领取硬核面试笔记

文章导读地址:点击查看文章导读!

感谢你的关注!

缓存数据一致性保证通用方案

在引入缓存的情况下,由于数据源有多个,那么就需要考虑数据一致性的问题,接下来会说一些数据一致性的解决方案,在实际情况中可以根据实现成本以及对于一致性的要求级别来选择不同的方案来实现

不过即使花费再高的实现成本也无法实现绝对的一致性,因为毕竟是存在了冗余数据,如果要保证绝对一致性,那么直接就在数据库操作就可以了

方案一:旁路缓存策略

image-20241007151451332

对于缓存数据的一致性,使用通用方案就可以保证大部分场景下的数据一致性,基于实现成本和数据一致性的考虑,旁路缓存策略 是一种比较通用的缓存一致性更新策略,流程为:

  • 读场景先从缓存读取数据,如果命中直接返回;如果未命中,则去数据库中读取
  • 写场景下先更新数据库中的数据,之后再去失效对应的缓存

使用缓存的目的是提升系统性能,但同时也失去了一定的数据一致性,因此使用缓存的场景一定是可以容忍短暂的数据不一致问题的,那么因此也就没有必要为了保证比较强的数据一致性,去投入较大的实现成本

在旁路缓存策略中,在极端情况下(读写操作时序错乱时)也会发生数据不一致的问题。如下(不过这种属于极其小概率事件,了解即可):

线程 A(写操作)线程 B(读操作)
读取缓存未命中
读取数据库旧值
更新数据库数据
失效缓存
将数据库旧值放入缓存(脏数据)

方案二:延时双删

image-20241007151411096

基于上边的问题,也有优化方案,可以减少发生这种事件的概率,比如 延时双删 ,即两次删除缓存,如下:

  • 第一次删除缓存是为了更快的达到最终一致性效果
  • 第二次会延时一段时间后再次去删除缓存,就是为了删除可能存在的脏数据

其次需要考虑延时时间的设置: 脏数据来源于读操作,读操作的耗时最多就是去读取数据库从节点上的旧数据,那么这里的延时时间需要保证大于读操作时间 + 数据库主从同步延时时间

虽然这种方案实现起来简单,但也存在不足:

  • 延迟时间是预估的,并不一定准确

  • 延迟等待第二次删除缓存会阻塞操作,存在性能消耗,可以使用异步线程来完成第二次删除

不过通过延时双删已经可以保证比较好的数据一致性了

方案三:基于 binlog 实现缓存更新

image-20241007150850593

除了延时双删,还存在其他的缓存更新策略,如 基于 binlog 实现缓存更新

阿里巴巴开源了 Canal 就是监听 binlog 来完成缓存更新,工作原理:

  • Canal 模拟 MySQL 的从库,向主库发送数据同步请求
  • MySQL 主库向 Canal 发送数据同步的 binlog
  • Canal Server 解析 binlog 并存储
  • 应用创建 Canal 客户端与 Canal Server 通信获取对应 binlog,完成对应业务操作

这里直接通过客户端和 Canal Server 通信存在性能问题,同一时刻只能一个客户端和 Canal Server 通信,单节点难以承受较大数据规模的缓存更新任务

因此从 Canal 1.1.1 版本之后,Canal Server 支持将 binlog 投递至 MQ,通过 MQ 可以实现多个客户端去消费,完成大量数据的缓存更新任务

不过针对互联网大多数场景来说,完全没有必要使用 Canal 来完成缓存数据的更新,通过缓存旁路策略完全可以满足数据一致性需要,再引入 Canal 会导致架构复杂,并需要维护 Canal 的可用性,实现成本较高

总结

综上,介绍了 3 种保证缓存一致性的解决方案,在真正使用场景中,需要从业务场景对数据不一致时间、实现成本、维护成本等多个方面进行评估,选择适合的方案

不过既然使用缓存,肯定就没办法保证绝对的一致性

比如在 Linux 内核中,使用了 PageCache(在内存中) 来优化 IO 性能,所有的 IO 操作,数据并不是直接写入到磁盘中,而是先放入到了 PageCache 中,再统一时间将 PageCache 的数据刷入到磁盘中,以此来提升磁盘 IO 的效率

那么在服务器异常关机的情况下,丢失数据的原因就是数据没有及时的从 PageCache 中刷入到磁盘中,可以发现在操作系统层面上也会存在缓存数据丢失的问题,那么在软件层面上就更不可避免地会出现数据不一致的情况了


http://www.kler.cn/news/341317.html

相关文章:

  • Linux下Nodejs应用service配置
  • LeetCode讲解篇之377. 组合总和 Ⅳ
  • 矩阵式键盘接口设计(用单片机读取4x4矩阵式键盘的键号,并将其显示在数码管上)(Proteus 与Keil uVision联合仿真)
  • 【网络安全】账户安全随笔
  • Vue82 路由器的两种工作模式 以及 node express 部署前端
  • C盘一红就卡顿到不行?为什么呢?
  • Python爬虫使用示例-古诗词摘录
  • Apache DolphinScheduler社区9月进展记录
  • 鸿蒙OS 开机动画流程
  • C++:visual studio运行时找不到.dll文件
  • 概率论详细介绍
  • 【北京迅为】《STM32MP157开发板嵌入式开发指南》-第十九章 Linux 工具之make 工具和 makefile 文件
  • easyexcel多sheet导出(唯一能用)
  • PL/SQL
  • vue3实现登录获取token并自动刷新token进行JWT认证
  • 分治算法(7)_归并排序_计算右侧小于当前元素的个数
  • SpringBoot技术在服装生产管理中的实践
  • 【JDK17 | 1】Java 17 深入剖析:新特性与变革
  • 22-微服务项目部署
  • 芋道前端utils文件夹