持久化数据&缓存数据双写一致性

2023-11-09

背景

缓存中数据更新一般有两个入口,

  • 数据缓存过期,数据在访问时发现缓存中无数据时重新查库然后更新至缓存;
    • 场景和问题等同于缓存查询,相关solution参考“缓存数据查询的注意事项”;
  • 缓存未过期,数据库数据有变动主动更新至缓存;
    • 比较常见的场景;
    • 也即为双写的概念:有新版本的数据需要同时写入持久化层和缓存层中;

问题点

数据一致性

  • 是指数据库持久化数据与高速缓存库中缓存数据的不一致;
  • 一般倾向于数据库为准,也就是说不一致是指缓存数据的时效性保障性低于持久化数据;
  • 不一致也即为缓存数据为脏数据;

脏数据的概念

  • 缓存的更新过程可能会出现时间上或长或短的时效性过期情况(或者说无法避免);
  • 长期或者在缓存单位生命周期中是脏数据的情况是无法接受的;
  • 但是如果在实现级出现短暂的脏数据,通过兜底或者其他策略完整最终一致性是解决方向;

缓存的过期时间

  • 常见缓存过期时间的处理策略是
    • 设置指定的过期时间:经过过期时间之后在数据查询过程中绑定缓存初始化的流程实现更新;
    • 不设置过期时间:长期有效,只有主动发起才会执行缓存的数据更新;
  • 关于数据一致性
    • 上述的设置过期时间的策略也是对数据一致问题的一种强行兜底方案;
      • 只是兜底,并不能依赖,缓存单位生命周期长度的脏数据无法接受;
    • 需要将脏数据的存在时间范围放在可控范围内;
      • 可控范围:业务可接受的范围;

问题解析

排除因素

  • 在分析一致性问题时我们排除使用过期时间兜底实现一致性的策略;
    • 首先,只是最终兜底,并且业务不可接受;
    • 以不过期的使用背景来分析,问题更明显;

问题来源

  • 无事务:缓存的操作不被包含在事务控制中;
    • 典型的情况:缓存更新失败,但是不影响数据库事务的成功提交,导致不一致;
  • 并发:因为无事务,无法保证双写的一致性;
    • 并发导致的时序问题导致数据覆盖,可能出现有效数据是旧数据的情况;
  • 多数据源:如果拿着一份数据分别向持久层和缓存执行执行更新,在时序无法保证时会出现交叉覆盖的情况;
  • 互斥:缓存的初始化(查询数据+set缓存)需要保证原子性,并且初始化和缓存的淘汰需要互斥,否则并发下交叉执行会有旧数据覆盖情况;

多数据源的描述实际也是因为无事务+并发造成的;

解决方向

  • 唯一数据源:
    • 以持久化数据为准,由缓存依赖获取持久化数据来实现更新;
  • 事务、并发:
    • 虽无法将缓存操作加入事务,但是需要保证缓存的操作在数据库事务提交之后,保证缓存更新依赖的数据有效性;
    • 快速兜底:在出现异常情况时通过重试机制快速兜底或预警通知;
  • 保证互斥:
    • 缓存的清楚和初始化使用锁保证互斥;

方案

更新缓存

更新缓存or淘汰缓存(删除缓存)
根据上述的通过保证唯一数据源来避免问题,应该采用淘汰缓存,然后缓存通过从持久化层获取数据完成初始化;

代表方案

如果采用更新缓存,无论更新缓存和更新数据库前后顺序怎么排列;
即无论是“更新缓存+更新数据库”还是“更新数据库+更新缓存”都会有以下问题;

问题:

  • 并发:
    • 如果多线程执行时序发生交叉,即ABBA或者BAAB时,持久层由于有数据库事务的保证,数据最终是有效的,但是缓存可能会发生旧数据覆盖新数据的情况;
  • 事务:
    • “更新数据库+更新缓存”,假如“更新缓存“的环节失败,但是“更新数据库”事务已经提交,导致不一致,这个问题需要我们关注,包括后续方案也需要关注这个问题;
    • “更新缓存+更新数据库”,这个就会有致命问题,即新数据已经更新至缓存,但是“更新数据库”时失败了,反而导致缓存数据是未持久化的数据;

结论

使用淘汰缓存,而不是更新缓存;

“淘汰缓存+更新数据库”

问题:

  • 事务+并发:
    • 因为两个环节的操作无事务统一管理,没有原子性;
      • 则可能在淘汰缓存之后、更新数据库事务提交之前;由其他线程访问缓存数据,触发缓存刷新流程,此时持久层数据仍是旧数据,而且被刷新至缓存,导致不一致;

结论:

  • 无法保证一致性,隐患很大;

“更新数据库+淘汰缓存”

细节

  • 保证“淘汰缓存”在“更新数据”事务提交之后,即:保证持久层有新数据之后再执行缓存淘汰;

问题:

  • 事务+兜底:
    • 此时,如果持久层事务提交之后,淘汰缓存因为各种原因失败,导致不一致;

一个细节问题(关于缓存操作的互斥):

- 如果不考虑上述缓存操作异常情况;
- 更新缓存时如果缓存未超时,此场景是无问题的;
-  场景:如果缓存数据是有效期的或者其他操作,导致在执行“更新数据库”之前 原缓存数据过期,此时有其它线程访问缓存数据并触发了缓存初始化流程:
    A_查询持久化数据+B_将查询结果set至缓存;那么就会有一个细节上的问题了:
    - 如果上述两个环节A&B都在当前线程的C_淘汰缓存这个环节之前完成,那就不会有问题,最后结果是再次淘汰缓存,保证了一致性;
    - 如果上述两个环节A&B交叉在C之间,即执行时序是A-C-B,那么上述的C_淘汰缓存操作就无意义了,还是会导致不一致;
- 解决方案:
    - 因为缓存的初始化是有同步锁控制的,如果将缓存淘汰操作也加入同一个同步锁控制内,就可以避免上述A-C-B的问题;并且不会造成额外影响;

结论:

  • 逻辑上和可行性个人认为是最靠谱的一个;
    • 通过消息支持重试机制进行兜底;
    • 通过同步锁与缓存初始化互斥保证逻辑完整;
  • 逻辑上是符合常理的;并且数据持久化完成之后“更新缓存”操作失败的兜底机制和保证缓存的互斥性是需要关注的核心问题;

数据库主从同步延迟的问题

问题描述:

上述的数据持久化都是在讲的概念,实际上如果存在读写分离,从库从主库同步数据需要一定的时间,即使在保证上述逻辑正常执行的情况下,
也需要考虑,当新数据成功持久化至主库之后、从库同步数据之前,有访问数据的请求落在从库上,读到的还是旧数据,仍然会造成脏数据;

解决思路:

订阅数据库binlog日志,在数据完成同步的时候重新发起“淘汰缓存”操作,并辅以消息重试机制保证执行;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

持久化数据&缓存数据双写一致性 的相关文章

  • 后端开发缓存篇之缓存及缓存模式的介绍

    一 什么是缓存 缓存 简单说就是为了节约对原始资源重复获取的开销 而将结果数据副本存放起来以供获取的方式 二 什么时候使用缓存 1 以 幂等和安全的方式对资源的获取操作 2 缓存数据必须是重复获取的 缓存能生效的本质是空间换时间 缓存的命中
  • 老话新谈之缓存一致性

    前言 缓存一致性常见的更新策略也比较多 如先更新数据库再更新缓存 先删缓存再更新数据库等等 我在理解的时候有些混乱 所以这个文章提供了一些理解上的技巧去理解缓存一致性 为什么会有缓存一致性的问题 缓存与数据库是两套中间件 存在网络抖动之类的
  • redis篇

    一 数据库分类 1 关系型数据库SQL Oracle 不开源收费 高帅富 SQL Server 不开源收费 微软自家的产品 DB2 不开源收费 IBM 的产品 Sybase 不开源收费 微软的小基友 关系破裂后家境惨淡 MySQL 大家都在
  • 第十二章_Redis单线程 VS 多线程

    Redis为什么选择单线程 是什么 这种问法其实并不严谨 为啥这么说呢 Redis的版本很多3 x 4 x 6 x 版本不同架构也是不同的 不限定版本问是否单线程也不太严谨 1 版本3 x 最早版本 也就是大家口口相传的redis是单线程
  • Redis介绍、安装、基础命令

    目录 引言 一 关系数据库和非关系数据库 1 1 关系型数据库 1 2 非关系型数据库 1 3 关系型数据库与非关系型数据库区别 数据存储方式不同 扩展方式不同 对事务性的支持不同 非关系型数据库产生背景 二 Redis简介 2 1 Red
  • 5. 一线大厂高并发缓存架构实战与性能优化

    分布式缓存技术Redis 1 冷热数据分离 2 缓存设计 2 1 缓存击穿 失效 2 2 缓存穿透 2 3 缓存雪崩 3 大V直播带货导致线上商品系统崩溃原因分析 4 突发性热点缓存重建导致系统压力暴增问题 5 缓存数据库双写不一致问题 6
  • Redis热点数据处理

    1 概念 热点数据就是访问量特别大的数据 2 热点数据引起的问题 流量集中 达到物理网卡上限 请求过多 缓存分片服务被打垮 redis作为一个单线程的结构 所有的请求到来后都会去排队 当请求量远大于自身处理能力时 后面的请求会陷入等待 超时
  • 4、动态代理的缓存机制

    1 背景 上一节大致介绍了Proxy动态代理的原理 从几个疑问上面分析 这一节介绍一下动态代理的缓存机制 网上的资源比较少 可以怀着下面几个问题阅读源码 为什么要缓存 缓存的内容是什么 哪里调用的缓存 缓存的实现机制 缓存的过期机制 2 属
  • Peterson 的算法能满足饥饿问题吗?

    我一直在搜索有关的信息彼得森算法但遇到的参考资料表明它不能解决饥饿问题 而只能解决僵局 这是真的 如果是这样 有人可以详细说明为什么不吗 彼得森算法 flag 0 0 flag 1 0 turn P0 flag 0 1 turn 1 whi
  • Android 双向滚动

    这更多的是一个大众答案而不是一个问题 我只是不知道如何发布它 版主如果你能告诉我是否有这样的事情 这个问题被问死了 然后我需要做类似的事情 所以我解决了这个问题 这篇文章的答案是如何在android中创建3x3双向滚动视图 下面是如何创建双
  • Redis数据类型

    文章目录 Redis介绍 RedisObject的结构 1 type 2 enconding 3 lru 4 refcount 5 prt Redis源码结构
  • Redis交互速度慢,频繁处理时经常报错 RedisSystemException: RedisException: Connection closed

    Redis交互速度很慢 达到几十到一百毫秒一次 且压力测试下经常报错 org springframework data redis RedisSystemException Redis exception nested exception
  • 【Redis】Redis 配置文件

    1 概述 相同文件 Redis redis 配置 配置文件 redis conf 自定义目录 myredis redis conf 4 1 Units单位 配置大小单位 开头定义了一些基本的度量单位 只支持bytes 不支持bit 大小写不
  • 如何将文件添加到解决方案文件夹?

    我正在使用以下脚本回答here https stackoverflow com questions 47628034 how to create a solution folder for a solution in visual stud
  • 自动包含 Visual Studio 中 vcproj 项目中文件夹的所有 .cpp/.h 文件

    有没有办法设置 vcproj 项目文件夹 以便根据项目的路径自动包含所有 cpp 或 h 文件 换句话说 当使用 Windows 资源管理器将文件添加到我的项目的文件夹时 是否可以将这些新文件自动添加到 Visual Studio 内的 v
  • Redis生产环境最佳实践

    欢迎关注公众号 通过文章导读关注 11来了 及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景 中间件系列笔记 和 编程高频电子书 文章导读地址 点击查看文
  • Visual Studio 解决方案中建议的项目数量

    我们正在开始开发新的应用程序 其中包括大约 12 名开发人员在 MS Visual Studio 中使用 C 开发的 30 50 个项目 我正在致力于应用程序模块的组件化 以支持架构并实现并行工作 我们争论 我们应该有多少种解决方案 有人声
  • 适用于 IE6.0 的 HTML5

    您知道有什么方法可以将此 HTML 代码优化为 IE6 或 7 或 8 而不添加anyHTML 元素 或者 IE 正在跳过所有 HTML5 元素 如果我只想使用 CSS 格式化元素 我不想使用其他功能 document createElem
  • Git 和 Visual Studio 项目参考

    好吧 我的问题的简短版本是 当您的项目在多个解决方案之间共享时 在 Git 中处理项目引用的最佳方法是什么 我的 Git 存储库应该如何组织 长版本是 我们是一个小型开发团队 5 名开发人员 目前我们使用 TFS 作为我们的源代码控制和构建
  • 如何打开 Visual Studio 解决方案而不加载其所有项目?

    我在 Visual Studio 中有一个大型解决方案 其中有一些 Android Windows Phone 和 iOS Xamarin 项目 不幸的是 Visual Studiohangs 几个小时 当它尝试加载整个解决方案时 只要我通

随机推荐

  • 关于K-means的通俗理解

    机器学习通俗理解系列 关于knn的通俗理解 文章目录 前言 一 什么是K means 二 什么原理 三 重点 1 K值的选定 2 样本之间的距离 四 优缺点 五 优化进阶 总结 前言 刚学习机器学习的时候免不了百度 问什么是K means
  • vue3运行npm run serve报错ERROR Error: Cannot find module ‘babel-plugin-import‘ Require stack:

    1 完整报错 gt ims support demo 0 1 0 serve Users yizhikaixinya Desktop charmplus ims gt vue cli service serve ERROR Error Ca
  • 实验SparkSQL编程初级实践

    实验SparkSQL编程初级实践 实践环境 Oracle VM VirtualBox 6 1 12 Ubuntu 16 04 Hadoop3 1 3 JDK1 8 0 162 spark2 4 0 python3 5 Windows11系统
  • 领域建模

    忙碌的过着周末 一边思考如何建设自己知识体系 另外一遍白板的各种算法在脑袋互相争抢时间 低音炮单曲循环的的Ava Max Salt 心 静下来 环境燥起来 思绪继续飞行 前期读了一半的书 重新拿起 在建模方式上理解场景方法的研究 之前分享的
  • asp.net zero 8.2 学习-3-添加实体,并迁移到数据库

    系列目录 asp net zero 8 2 学习 1 安装 asp net zero 8 2 学习 2 创建一个页面 asp net zero 8 2 学习 3 添加实体 并迁移到数据库 asp net zero 8 2 学习 4 创建接口
  • 压缩伪影的探讨

    1 压缩伪影的由来 常用的视频编码器中 在一个框架中使用了多种编码方法 01 预测编码 不编码预测值 而是编码预测值与实际值的差值 02 变换编码 对信号的样本值进行某种形式的函数变换 从一种空间变换到另一种空间 然后再根据信号在另一个空间
  • SOA中国路线图活动感受

    下午参加了SOA中国路线图活动 主要由普元公司和相关的媒体以及电信客户进行演讲 对于SOA我之前一直认为是个很虚的东西 概念大于实践 但听了普元公司黄柳青博士的介绍以及在电信领域中的应用 感觉还是有收获的 很多思想可以应用到系统的设计和开发
  • 数据结构——红黑树

    1 什么是红黑树 红黑树是一种特定类型的二叉树 用于组织数据 它是一种平衡二叉查找树 AVL树 的变体 每个结点都带有颜色属性 红色或黑色 在红黑树中 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长 具体来说 红黑树满足以下性质 每
  • 结构型模式-享元模式

    package per mjn pattern flyweight 抽象享元角色 public abstract class AbstractBox 获取图形的方法 内部状态 public abstract String getShape
  • 机器学习 可视化_机器学习-可视化

    机器学习 可视化 机器学习导论 Introduction to machine learning In the traditional hard coded approach we program a computer to perform
  • 【Unity 几何着色器】简单的网格线描边

    水文 几何着色器 第一个pass就默认的unlit效果 第二个pass是新建的 属性都没有用到 先留个坑吧 Shader GeoHelp LineMesh Properties MainTex Texture 2D white EdgeWi
  • 优质数对的数目[位运算特点+抽象能力考察+分组快速统计]

    位运算特点 抽象能力考察 分组快速统计 前言 一 优质数对的数目 二 思路与优化过程 总结 参考文献 前言 位运算是计算机最基本的计算 是最快的运算方式 与或非各有特点 抽象能力考察我理解成一种 拿核心去累赘 的能力 分组快速统计 我们不必
  • 1Python入门小结(1)

    Python入门小结 1 万丈高楼平地起 简介 Python是一种通用编程语言 其在科学计算和机器学习领域具有广泛的应用 本小节包含的内容 变量 运算符与数据类型 位运算 条件语句 循环语句 异常处理 变量 运算符与数据类型 注释 Pyth
  • 我使用过的Linux命令之stty - 显示和修改终端行设置

    原文链接 http codingstandards iteye com blog 826924 用途说明 stty命令用于显示和修改终端行设置 change and print terminal line settings 常用参数 stt
  • 【Linux学习】虚拟机VMware 安装Qt5 一条龙讲解

    如何在Linux下安装Qt5呢 若已在Linux下载好安装包 可直接从第三步进行阅读 目录 第一步 下载所需版本Qt 第二步 将Qt安装包传输到Linux 第三步 Linux下安装Qt 第四步 配置 Qt 环境 本文安装版本 linux上的
  • 浅谈软件构件和软件构件测试

    什么是构件 构件也称为组件 是一个独立发布的功能部分 通过接口可以访问它的服务 其特点是 l 软件系统中具有相对独立功能 可以明确辨识 接口由契约指定 和语境有明显依赖关系 可独立部署 且多由第三方提供的可组装软件实体 l 软件构件须承载有
  • 前端导出后端文件的方法

    一般存在两种方式 1 请求接口之后 后端返回文件路径 前端直接下载 2 请求接口之后 后端以文件流的形式返回给前端 前端再下载到本地 第一种方式 window location href res request responseURL 直接
  • CVPR 2017论文

    近期在看CVPR2017的文章 顺便就把CVPR2017整理一下 分享给大家 更多的 Computer Vision的文章可以访问Computer Vision Foundation open access CVPapers Machine
  • Vue实现给按钮的点击事件绑定id参数

    当我们需要给按钮所绑定的值做出判断并记录时 eg 为答题的正确以及题号做判断 第一种情况 使用v for循环 div div 我是id div div 1 2 3 然后在 vue 的实例中就可以拿到对应的 id b index this l
  • 持久化数据&缓存数据双写一致性

    背景 缓存中数据更新一般有两个入口 数据缓存过期 数据在访问时发现缓存中无数据时重新查库然后更新至缓存 场景和问题等同于缓存查询 相关solution参考 缓存数据查询的注意事项 缓存未过期 数据库数据有变动主动更新至缓存 比较常见的场景