保证缓存一致性的常用套路

news2025/1/13 2:41:02

缓存更新的套路

看到好些人在写更新缓存数据代码时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中。然而,这个是逻辑是错误的。试想,两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。

我不知道为什么这么多人用的都是这个逻辑,当我在微博上发了这个贴以后,我发现好些人给了好多非常复杂和诡异的方案,所以,我想写这篇文章说一下几个缓存更新的Design Pattern(让我们多一些套路吧)。

这里,我们先不讨论更新缓存和更新数据这两个事是一个事务的事,或是会有失败的可能,我们先假设更新数据库和更新缓存都可以成功的情况(我们先把成功的代码逻辑先写对)。

更新缓存的的Design Pattern有四种:Cache aside, Read through, Write through, Write behind caching,我们下面一一来看一下这四种Pattern。

Cache Aside Pattern

这是最常用最常用的pattern了。其具体逻辑如下:

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。

  • 命中:应用程序从cache中取数据,取到后返回。

  • 更新:先把数据存到数据库中,成功后,再让缓存失效。

Cache-Aside-Design-Pattern-Flow-Diagram
Cache-Aside-Design-Pattern-Flow-Diagram
Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1
Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1

注意,我们的更新是先更新数据库,成功后,让缓存失效。那么,这种方式是否可以没有文章前面提到过的那个问题呢?我们可以脑补一下。

一个是查询操作,一个是更新操作的并发,首先,没有了删除cache数据的操作了,而是先更新了数据库中的数据,此时,缓存依然有效,所以,并发的查询操作拿的是没有更新的数据,但是,更新操作马上让缓存的失效了,后续的查询操作再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题,后续的查询操作一直都在取老的数据。

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。为什么不是写完数据库后更新缓存?你可以看一下Quora上的这个问答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕两个并发的写操作导致脏数据。

那么,是不是Cache Aside这个就不会有并发问题了?不是的,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

但,这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

所以,这也就是Quora上的那个答案里说的,要么通过2PC或是Paxos协议保证一致性,要么就是拼命的降低并发时脏数据的概率,而Facebook使用了这个降低概率的玩法,因为2PC太慢,而Paxos太复杂。当然,最好还是为缓存设置上过期时间。

Read/Write Through Pattern

我们可以看到,在上面的Cache Aside套路中,我们的应用代码需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。所以,应用程序比较啰嗦。而Read/Write Through套路是把更新数据库(Repository)的操作由缓存自己代理了,所以,对于应用层来说,就简单很多了。可以理解为,应用认为后端就是一个单一的存储,而存储自己维护自己的Cache。

Read Through

Read Through 套路就是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。

Write Through

Write Through 套路和Read Through相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

下图自来Wikipedia的Cache词条。其中的Memory你可以理解为就是我们例子里的数据库。

Write-through_with_no-write-allocation
Write-through_with_no-write-allocation
Write Behind Caching Pattern

Write Behind 又叫 Write Back。一些了解Linux操作系统内核的同学对write back应该非常熟悉,这不就是Linux文件系统的Page Cache的算法吗?是的,你看基础这玩意全都是相通的。所以,基础很重要,我已经不是一次说过基础很重要这事了。

Write Back套路,一句说就是,在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比(因为直接操作内存嘛 ),因为异步,write backg还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。

但是,其带来的问题是,数据不是强一致性的,而且可能会丢失(我们知道Unix/Linux非正常关机会导致数据丢失,就是因为这个事)。在软件设计上,我们基本上不可能做出一个没有缺陷的设计,就像算法设计中的时间换空间,空间换时间一个道理,有时候,强一致性和高性能,高可用和高性性是有冲突的。软件设计从来都是取舍Trade-Off。

另外,Write Back实现逻辑比较复杂,因为他需要track有哪数据是被更新了的,需要刷到持久层上。操作系统的write back会在仅当这个cache需要失效的时候,才会被真正持久起来,比如,内存不够了,或是进程退出了等情况,这又叫lazy write。

在wikipedia上有一张write back的流程图,基本逻辑如下:

Write-back_with_write-allocation
Write-back_with_write-allocation
再多唠叨一些

1)上面讲的这些Design Pattern,其实并不是软件架构里的mysql数据库和memcache/redis的更新策略,这些东西都是计算机体系结构里的设计,比如CPU的缓存,硬盘文件系统中的缓存,硬盘上的缓存,数据库中的缓存。基本上来说,这些缓存更新的设计模式都是非常老古董的,而且历经长时间考验的策略,所以这也就是,工程学上所谓的Best Practice,遵从就好了。

2)有时候,我们觉得能做宏观的系统架构的人一定是很有经验的,其实,宏观系统架构中的很多设计都来源于这些微观的东西。比如,云计算中的很多虚拟化技术的原理,和传统的虚拟内存不是很像么?Unix下的那些I/O模型,也放大到了架构里的同步异步的模型,还有Unix发明的管道不就是数据流式计算架构吗?TCP的好些设计也用在不同系统间的通讯中,仔细看看这些微观层面,你会发现有很多设计都非常精妙……所以,请允许我在这里放句观点鲜明的话——如果你要做好架构,首先你得把计算机体系结构以及很多老古董的基础技术吃透了

3)在软件开发或设计中,我非常建议在之前先去参考一下已有的设计和思路,看看相应的guideline,best practice或design pattern,吃透了已有的这些东西,再决定是否要重新发明轮子。千万不要似是而非地,想当然的做软件设计。

4)上面,我们没有考虑缓存(Cache)和持久层(Repository)的整体事务的问题。比如,更新Cache成功,更新数据库失败了怎么吗?或是反过来。关于这个事,如果你需要强一致性,你需要使用“两阶段提交协议”——prepare, commit/rollback,比如Java 7 的XAResource,还有MySQL 5.7的XA Transaction,有些cache也支持XA,比如EhCache。当然,XA这样的强一致性的玩法会导致性能下降,关于分布式的事务的相关话题,你可以看看《分布式系统的事务处理》一文。

本文由 mdnice 多平台发布

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

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

相关文章

傲梅轻松备份-备份rocky9,并还原备份

傲梅分区助手磁盘备份功能免费;备份iso镜像不支持linux,好像只能备份window;且备份镜像收费功能,永久授权99; 概述: 1)新建备份U盘 2)新建虚拟机,配置虚拟机&#xff…

C++将信息输入到文件内

第一步检查文件是否打开&#xff0c;用到头文件&#xff1a; #include <fstream> #include <sstream> 文件打开的函数为 file.isopen() 信息输入到文件应该为 file << "" << value; 注意是file<< 如图 定义file ofstream f…

Dell戴尔XPS 8930笔记本电脑原装Win10系统 恢复出厂预装OEM系统

链接&#xff1a;https://pan.baidu.com/s/1eaTQeX-LnPJwWt3fBJD8lg?pwdajy2 提取码&#xff1a;ajy2 原厂系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软件、MyDell等预装程序 文件格式&#xff1a;esd/wim/swm 安装方式&am…

FFT加窗和频谱泄露

所谓加窗&#xff0c;目的就是针对非整周期采样得到的非自窗信号&#xff08;self-windowing signal&#xff09;&#xff0c;缓解其频谱分析结果中的能量泄漏现象&#xff08;注意是“缓解”&#xff0c;而不是“避免”或“消除”&#xff09;。所谓信号整周期采样&#xff08…

一个非常流行的R语言调色板:RColorBrewer

R 语言有许多非常优秀的调色板&#xff0c;本文就介绍一个非常流行的&#xff0c;我也经常在用的调色板 R 包&#xff1a;RColorBrewer。 安装 install.packages("RColorBrewer") 加载 library(RColorBrewer) library(knitr) 初探 ?RColorBrewer 在帮助页面可以看到…

在MD编辑器里插入20次方问题

前言 看了很多文章里面没写怎么插入20次方&#xff0c;最后在官网的一篇文章上看到了很详细的数学公式的插入。 问题 大家肯定以为这样就可以了 效果 明显是不行的 解决 使用{}把数字括起来就可以了。 1 20 1^{20} 120 小知识 在行内显示(就是与文字在一起) $ $另起…

详解矩阵的三角分解A=LU

目录 一. 求解Axb 二. 上三角矩阵分解 三. 下三角矩阵分解 四. 矩阵的三角分解 举例1&#xff1a;矩阵三角分解 举例2&#xff1a;三角分解的限制 举例3&#xff1a;主元和乘法因子均为1 举例4&#xff1a;U为单位阵 小结 一. 求解Axb 我们知道高斯消元法可以对应矩阵…

二层交换机和三层交换机

二层交换机&#xff1a;将源mac和端口进行转发&#xff0c;是同一个网段进行通信的&#xff0c;不能实现路由转发&#xff0c;若想跨网段则需要接入一个路由器 如&#xff1a;pc1 192.168.1.1 与 pc2 192.168.1.2通信需要经过二层交换机&#xff0c;二层交换机不能配置ip的&am…

ERP进出库+办公用品管理系统

系统架构 简介系统架构部分页面结构图UML逻辑图办公用品入出库 简介 本系统适用于ERP企业公司职员关于系统化的申请相关办公用品&#xff0c;提高整体系统整合行&#xff0c;加大上下级之间的联系&#xff0c;规避因人员过多&#xff0c;而浪费人力在简单重复的工作中&#xf…

【车载HMI开发工具--EB GUIDE 与 Unity 合作提供一体化的沉浸式 HMI 设计开发工具链】【转载】

随着车载高性能计算平台的日益普及以及显示器尺寸和数量的不断增加&#xff0c;沉浸式车载人机交互界面&#xff08;HMI&#xff09;的需求也在持续增长。为了将实时 3D 技术带入车载 HMI 领域&#xff0c;Unity 与 Elektrobit (EB)展开了合作&#xff0c;EB 是推进 HMI 功能安…

进程间协同:从进程启动、同步与互斥到进程间通信

进程间协同的目的 在操作系统中&#xff0c;进程是计算机进行任务分配和调度的基本单位。在计算机系统中&#xff0c;有很多任务是无法由单个进程独立完成的&#xff0c;需要多个进程共同参与并协作完成。这就像在现实生活中&#xff0c;有些工作需要一个团队来完成&#xff0…

智慧照明迎来新升级:5G/4G智慧灯杆网关助力节能控制

近年来&#xff0c;随着智能科技的飞速发展&#xff0c;智慧照明系统已成为城市建设的热门话题。其中&#xff0c;5G/4G智慧灯杆网关作为智慧照明的重要组成部分&#xff0c;正逐渐引起人们的关注。其能够实现智能控制与远程管理&#xff0c;为城市节能减排做出了巨大贡献。  …

MCU常用外设总线

目录 前言一、时钟与中断二、GPIO三、ADC四、定时器4.1 基本定时器4.2 通用定时器4.2.1 输入捕获4.2.2 输出比较 五、UART六、IIC七、SPI 前言 本文主要讲单片机外设的功能&#xff0c;即这些外设是什么&#xff0c;可以用来干什么&#xff0c;了解了之后我们就可以通过相应的寄…

裁员1600人赔偿8000万,游戏巨头的寒冬要来啦!

据相关可靠消息&#xff0c;网易裁员1600人&#xff0c;消息确认可靠&#xff0c;并且基本都是网易游戏的技术人。 网易是一家靠门户网站起家的互联网公司&#xff0c;并提前布局了网易邮箱。当然最后靠网易游戏提前转型&#xff0c;将竞争对手搜狐和新浪远远的甩着后面啦。 网…

MacM1Pro Parallels19.1.0 CentOS7.9 Install PostgrepSQL

相关阅读 MacM1Pro安装 Parallels Desktop 19.1.0 https://blog.csdn.net/qq_41594280/article/details/135420241 MacM1Pro Parallels安装Parallels Tools https://blog.csdn.net/qq_41594280/article/details/135398780 MacM1Pro Parallels安装CentOS7.9 https://blog.csdn.n…

第十二站(20天):C++泛型编程

模板 C提供了模板(template)编程的概念。所谓模板&#xff0c;实际上是建立一个通用函数或类&#xff0c; 其 类内部的类型和函数的形参类型不具体指定 &#xff0c;用一个虚拟的类型来代表。这种通用的方式称 为模板。 模板是泛型编程的基础, 泛型编程即以一种独立于任何特定…

微软Microsoft推出针对学生的AI练习英语口语工具”阅读教练“:Reading Coach

阅读教练官网链接&#xff1a;https://coach.microsoft.com AI工具专区&#xff1a;AI工具-喜好儿aigc 学生可以通过选择角色和设定&#xff0c;利用AI生成独特的故事&#xff0c;从而激发阅读兴趣并提高阅读流畅度。语音转文本AI能够实时分析学生的阅读流利性&#xff0c;检测…

从 fatal 错误到 sync.Map:Go中 Map 的并发策略

为什么 Go 语言在多个 goroutine 同时访问和修改同一个 map 时&#xff0c;会报出 fatal 错误而不是 panic&#xff1f;我们该如何应对 map 的数据竞争问题呢&#xff1f; 这篇文章将带你一步步了解背后的原理&#xff0c;并引出解决 map 并发问题的方案。 Map 数据竞争 首先…

慢查询定位

慢查询 使用工具 mysql自带慢日志 默认没有开启需要手动开启 查看慢日志中的文件 总结

研学活动报名系统源码开发方案

一、项目背景与目标 &#xff08;一&#xff09;项目背景&#xff1a; 随着教育水平的提高和人们对综合素质培养的需求增加&#xff0c;研学活动作为一种教育方式受到了广大家长和学生的青睐。为了更好地组织和管理研学活动&#xff0c;需要建立一个研学活动报名系统&#xf…