老话新谈之缓存一致性

news2025/2/1 18:04:59

前言

缓存一致性常见的更新策略也比较多,如先更新数据库再更新缓存,先删缓存再更新数据库等等,我在理解的时候有些混乱,所以这个文章提供了一些理解上的技巧去理解缓存一致性。

为什么会有缓存一致性的问题

  1. 缓存与数据库是两套中间件,存在网络抖动之类的原因导致没有更新任一方的可能
  2. 数据库大多都是事务型的中间件,支持错误回滚,缓存大多是非事务型的中间件,这里缓存更新失败了没办法回滚

所以根因是缓存大部分不支持事务无法回滚。

怎么尽量解决缓存一致性的问题

操作二者必定有先后顺序,存在以下两个情况:

  1. 先操作缓存,再操作数据库。操作缓存成功,数据库更新失败,缓存无法回滚,数据不一致
  2. 先操作数据库,再操作缓存。操作数据库成功,缓存操作失败,可触发异常回滚数据库,数据一致

根据上述所列,只能先操作数据库,再操作缓存了。

操作缓存也分两种:

  1. 更新缓存数据,可能并发请求,后一请求更新缓存的数据被前一请求的更新覆盖了,导致数据不一致
  2. 删除缓存数据,并发请求,二者都使缓存失效,查询请求将数据库数据加载到缓存中,数据一致

根据上述所列,只能使缓存失效,查询请求加载数据到缓存中了。

所以,如果在不加任何重试措施的情况下,先操作数据库,再删除缓存是一个容错较好的方法。

缓存一致性的分类 & 存在的问题

Client 维护缓存 & 数据库的一致性

  1. 更新缓存 -> 更新数据库

    图片1

@startuml
Database Database   as DB
entity   Cache      as Cache

transaction1 -> Cache: update data
transaction1 <-- Cache: update result

transaction1 -> DB: update data
transaction1 <-- DB: update result

@enduml
  • 可能出现的数据不一致

​ 数据不一致:更新缓存成功了,更新数据库失败了,有数据不一致的问题,直到缓存超时失效或又一更新请求操作成功都会不一致

  • 改进方式

    若保证更新数据仅有少数的服务更新,可以将更新数据库请求入队处理,且可加入重试机制。但是队列的加入会增大系统复杂度,并且重试以及缓存更新顺序不一致会加剧数据不一致

  1. 更新数据库 -> 更新缓存

@startuml
Database Database   as DB
entity   Cache      as Cache

transaction1 -> DB: update data
transaction1 <-- DB: update result

transaction2 -> DB: update data
transaction2 <-- DB: update result


transaction2 -> Cache: update data
transaction2 <-- Cache: update result

transaction1 -> Cache: update data
transaction1 <-- Cache: update result

@enduml
  • 可能出现的数据不一致

​ 数据不一致:如 t1 先更新数据库,t2 在 t1 更新缓存前把数据库缓存都更新完了,t1 再更新缓存,这时候缓存上是 t1 的数据,数据库是 t2 的数据

  • 改进方式

    若保证更新数据仅有少数的服务更新,可以将更新数据库请求入队处理,但是队列更新的引入增大了系统复杂度

  1. 删除缓存 -> 更新数据库

@startuml
Database Database   as DB
entity   Cache      as Cache

transaction1 -> Cache: delete data

query1 -> DB: select data
query1 -> Cache: insert data

transaction1 -> DB: update result

@enduml
  • 可能出现的数据不一致

    1. 如图所示,更新请求先删除缓存,查询请求从缓存获取不到数据从数据库获取数据(老数据)加载到缓存中,更新请求更新数据库
    2. 这样的流程会导致查询请求加载老数据到缓存中,后续更新请求更新新数据到数据库中,导致数据不一致
  • 改进方式

    暂无。

  1. 更新数据库 -> 删除缓存

@startuml
Database Database   as DB
entity   Cache      as Cache

query1 -> DB: select data
transaction1 -> DB: update result
transaction1 -> Cache: delete data
query1 -> Cache: insert data

@enduml
  • 可能出现的数据不一致

    查询请求先拿到数据,在插入缓存前更新请求进来更新数据库并使缓存失效,这个请求比较罕见

    1. 发生的场景
      1. 查询请求所在机器请求缓存比更新请求做完的整个流程都要慢
    2. 发生的概率
      1. 很低。因为操作缓存一般会比操作数据库要快
  • 改进方式

    1. 变更数据记录变更事件
      1. 步骤
        1. 更新数据同步记录一个事件在本地内存中
        2. 查询请求在插入缓存前查询事件,如果存在变更则查数据库获取最新数据
        3. 如果此数据在查询请求插入缓存过程中一直变更,这里需要先返回当前数据库结果给上游,再开异步任务轮训事件/数据库插入缓存
      2. 适用场景
        1. 只适用单节点

Server 维护缓存 & 数据库的一致性

  1. Read though/Write though

    • read though

    @startuml
    Database Database   as DB
    entity   Cache      as Cache
    
    query -> repository: select data
    
    repository -> cache: get data
    repository -> DB: get data
    DB -> repository: return data
    repository -> cache: update data
    repository -> query: return data
    
    @enduml
    
    • wirte though

    @startuml
    Database Database   as DB
    entity   Cache      as Cache
    
    transcation -> repository: update data
    
    repository -> cache: update data
    repository -> DB: update data
    DB -> repository: return result
    repository -> transcation: return result
    
    @enduml
    
  • 可能出现的数据不一致
    • 程序没有优雅关闭,更新请求先更新了缓存,但还没更新数据库,数据丢失
    • 更新缓存成功,更新数据库失败导致的数据不一致
  • 适用场景
    • 更新数据库极低概率失败
    • 程序有优雅关闭功能
  • 改进方式
    • 暂无
  1. Write Behind

@startuml
Database Database   as DB
entity   Cache      as Cache

query -> repository: query data

repository -> cache: query data
repository -> DB: query data
DB -> repository: return data
repository -> cache: update data

repository -> query: return data

@enduml

@startuml
Database Database   as DB
entity   Cache      as Cache

transcation -> repository: update data

repository -> cache: update data

repository -> DB: batch update data

@enduml
  • 可能出现的数据不一致
    • 程序没有优雅关闭,更新请求先更新了缓存,但还没更新数据库,数据丢失
    • 批量更新数据库失败导致的数据不一致
  • 适用场景
    • 更新数据库极低概率失败
    • 程序有优雅关闭功能
  • 改进方式
    • 暂无

参考

https://coolshell.cn/articles/17416.html

本文首发于cartoon的博客

转载请注明出处:https://cartoonyu.github.io

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全从业人员2023年后真的会被AI取代吗?

随着ChatGPT的火爆&#xff0c;很多人开始担心网络安全从业人员会被AI取代。如果说网络安全挖洞的话&#xff0c;AI可能真的能取代。但是网络安全不仅仅只是挖洞&#xff0c;所以AI只是能缓解网络安全人员不足的情况&#xff0c;但是是不会取代人类的作用的。 就拿最近很火的C…

【STL】priority_queue的使用及模拟实现

目录 前言 priority_queue的使用 功能解析 基本接口 写点题目 模拟实现 结构解析 插入删除 调整函数结合仿函数 仿函数介绍 结合使用 其他功能 接口补齐 迭代器区间构造 前言 &#x1f37e;打开 queue 头文件后&#xff0c;我们发现除了我们之前介绍过的普通队列…

用maven安装JUnit 5并运行单元测试

一&#xff1a;首先讲如何安装 JUnit 5 JUnit Platform JUnit Jupiter JUnit Vintage 如果不需要执行基于JUnit 3 和JUnit 4 的用例&#xff0c;那么JUnit Vintage就不需要安装。 1&#xff09;在pom文件dependencies的小节内增加如下依赖&#xff1a; <dependency>&l…

Docker安装ClickHouse22.6.9.11并与SpringBoot、MyBatisPlus集成

背景 上一篇文章CentOS6.10上离线安装ClickHouse19.9.5.36并修改默认数据存储目录记录了在旧版的操作系统上直接安装低版本 ClickHouse &#xff08;脱胎于俄罗斯头号搜索引擎的技术&#xff09;的过程&#xff0c;开启远程访问并配置密码&#xff1b; 其实通过 Docker 运行 …

一文读懂性能测试

01、常见概念 吞吐量&#xff08;TPS, QPS&#xff09; 简单来说就是每秒钟完成的事务数或者查询数。通常吞吐量大表明系统单位时间能处理的请求数越多&#xff0c;所以通常希望TPS越高越好 响应时间 即从请求发出去到收到系统返回的时间。响应时间一般不取平均值&#xff0…

二叉树及其链式结构

目录 一&#xff1a;树概念及结构 1.树的概念 2.树的相关概念 3.树的表示 二&#xff1a;二叉树的概念及结构 1.概念 2.特殊的二叉树 <1>. 满二叉树&#xff1a; <2>. 完全二叉树&#xff1a; 3.二叉树的性质 4.二叉树的存储结构 <1>.顺序结构 <…

win11 无法登录微软账户 终极解决方案

背景&#xff1a;win11突然无法登录微软账户&#xff0c;office无法激活&#xff0c;Edge里的微软账户也无法登录&#xff0c;反馈中心也无法打开等&#xff0c;有网络&#xff0c;浏览器可以访问微软并进行登录。 试过网上的网络配置&#xff08;SSL及TLS协议勾选&#xff09…

数学模型:Python实现线性规划

文章摘要&#xff1a;线性规划的Python实现。 参考书籍&#xff1a;数学建模算法与应用(第3版)司守奎 孙玺菁。 PS&#xff1a;只涉及了具体实现并不涉及底层理论。学习底层理论以及底层理论实现&#xff1a;可以参考1.最优化模型与算法——基于Python实现 渐令 粱锡军2.算法导…

vim复制,剪切觉得麻烦?今天就来教会你:vim复制和剪切教程

上次讲了vim移动光标的快捷键&#xff0c;今天我们来了解一下vim的复制多行的功能。 快速复制多行 第一步&#xff1a;将光标移动到复制的文本开始的地方&#xff0c;按下v进入可视模式&#xff0c;这里的v的意思就是visual的缩写&#xff0c;就是可视的意思。 第二步&#x…

DBSCAN 集群

目录 DBSCAN 集群 主要代码 DBSCAN 绘制结果中的集群 DBSCAN 集群 基于密度的空间聚类应用&#xff08;DBSCAN&#xff09;是一种基于密度的聚类算法&#xff0c;由Martin Ester等人在1996年提出。该算法在半径为ε的圆内找到数据点的相邻点&#xff0c;并将它们加入到同一…

Git原理详解+指令操作,带你快速掌握Git

一、相关概念 版本控制 什么是版本控制&#xff1f; 用于管理多人协同开发项目的技术对文件的版本控制&#xff0c;要对文件进行修改、提交等操作 版本控制的分类 1.本地版本控制 就是放在本地 2.集中版本控制 SVN 所有的版本数据都保存在服务器上&#xff0c;协同开发…

GDAL读取属性表值乱码解决方法

文章目录 方法1&#xff1a;方法2&#xff1a;1.通过在线网网站&#xff0c;查看编码2.参考C中gbk和utf8互相转换文章 gdal示例代码 方法1&#xff1a; 在网上查看的推荐方法&#xff08;C代码&#xff09;&#xff0c;但没解决我遇到的问题&#xff1b; CPLSetConfigOption(&…

SSM整合快速入门案例

文章目录 前言一、设计数据库表二、创建工程三、SSM技术整合四、功能模块开发五、接口测试总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者…

Java网络开发(Tomcat)——登陆和注册功能 的 迭代升级 从Jsp到JavaScript + axios + vue 诸多bug 同步到异步

目录 引出前置工作vueaxiosresp0.vue版本的jsp模板1.导包--Json&#xff1a;pom.xml文件&#xff1a;2.新建一个专门用来处理响应的实体类ResData3.在axios中&#xff0c;所有响应必须是 resp.getWriter().write() 的方式&#xff0c;核心代码如下4.在jsp前端代码中导包&#x…

华为OD机试真题B卷 JavaScript 实现【查字典】,附详细解题思路

一、题目描述 输入一个单词前缀和一个字典&#xff0c;输出包含该前缀的单词。 二、输入描述 单词前缀字典长度字典。 字典是一个有序单词数组。 输入输出都是小写。 三、输出描述 所有包含该前缀的单词&#xff0c;多个单词换行输出。 若没有则返回-1。 四、解题思路…

SLAM十四讲——ch3实践

ch3的实践及避坑 一、ch3的总体步骤二、文件的执行1. 实践&#xff1a;Eigen2. 实践&#xff1a;Eigen几何模块3. 可视化演示 出现的问题 一、ch3的总体步骤 确保已经安装Eigen库&#xff0c;Eigen库是一个C开源线性代数库。 sudo apt-get install libeigen3-dev说明&#xf…

在 Compose 中实现缓存列表数据提升用户体验(Stale-while-revalidate)

前言 最近在利用业余时间使用 Compose 实现一个 Github APP 客户端。 对标的是 GSY 大佬使用多种不同语言框架实现的 Github APP。 在实现过程中发现一些问题&#xff0c;因为这个客户端的数据几乎全部来自于 Github API&#xff0c;所以 UI 渲染也极度依赖于请求到的数据。…

JAVA面向对象(三)

第三章 封装与继承 目录 第三章 封装与继承 1.1.封装 1.2.包 1.3.访问权限控制 1.4.static修饰符 1.4.1.成员变量 1.4.2.成员方法 1.4.3.代码块 总结 内容仅供学习交流&#xff0c;如有问题请留言或私信&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

java并发编程:ArrayBlockingQueue详解

文章目录 一、简介二、数据结构三、源码分析3.1 属性3.2 构造方法3.3 方法3.3.1 入队3.3.2 出队3.3.3 获取元素3.3.4 删除元素 四、总结 一、简介 ArrayBlockingQueue 顾名思义&#xff1a;基于数组的阻塞队列。数组是要指定长度的&#xff0c;所以使用 ArrayBlockingQueue 时…

在知乎逮到一个腾讯10年老测试,聊过之后收益良多...

老话说的好&#xff0c;这人呐&#xff0c;一单在某个领域鲜有敌手了&#xff0c;就会闲得蛋疼。前几天我在上班摸鱼刷知乎的时候认识了一位腾讯测试大佬&#xff0c;在腾讯工作了10年&#xff0c;因为本人天赋比较高&#xff0c;平时工作也兢兢业业&#xff0c;现在企业内有一…