分布式系统:缓存与数据库一致性问题

前言

缓存设计是应用系统设计中重要的一环,是通过空间换取时间的一种策略,达到高性能访问数据的目的;但是缓存的数据并不是时刻存在内存中,当数据发生变化时,如何与数据库中的数据保持一致,以满足业务系统要求,本篇将给出具体分析。

image.png

一致性分类

  • 强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大,这种情况比如秒杀系统,商家后台,他会设置秒杀商品,参与秒杀活动,一旦说他参与了秒杀活动,商品的库存本来是在数据库里的,此时必须直接被加载到缓存里,缓存立马就要可以被使用。
  • 弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态
  • 最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型,比如微博的粉丝数,页面每天的访问数

缓存更新机制

缓存的更新,一般分为被动更新与主动更新,被动更新是指缓存在有效期到后,被淘汰。

被动更新如下步骤:
step1: 发起方查数据,缓存中没有,从数据库中获取,并写入缓存,同时设置过期时间 t;
step2: 在 t 内,所有的查询,都由缓存提供,所有的写,直接写数据库;
step3: 当缓存数据到过期时间 t 后,缓存数据失效。后面的查询,回到了第 1 步。

主动更新,一般为调用方发起缓存与数据库同时更新,缓存分为删除、更新,数据库分为更新,通过组合与先后顺序,分为如下四种情况:
更新缓存、更新数据库更新数据库,更新缓存删除缓存,更新数据库更新数据库,删除缓存

更新缓存、更新数据库

这种情况,当缓存更新成功,数据库更新不成功时,数据不一致的风险比较高,所以一般不采用

更新数据库、更新缓存

当更新完数据库,缓存的加载前需要通过大量复杂计算才能得出缓存的值,不仅让发起方阻塞,影响性能;而且如果缓存命中率不高,很少使用,更浪费前期的复杂计算成本与缓存空间,这里就不符合懒加载的设计思想,故一般也不采用

删除缓存、更新数据库

如图所示,当两个调用方线程高并发访问的情况下,A 线程先删除缓存,再更新数据库,此过程时间较长,B 线程在 A 删除缓存后,迅速读取缓存,因缓存每命中,从数据库中读取再加载缓存,此时缓存还是旧值,等 A 线程更新完数据库后,发现又出现数据不一致的现象。

image.png

一般大概率情况下,出现此根源的原因是读比写快,所以这种一般也不采用,如果非得采用,需要在写完数据库之后延迟一段时间再删除一次缓存,也就是我们熟知的 延时双删,延迟多久呢,一般看数据库的更新时长来决定,此做法也会带来系统吞吐量下降

Cache-Aside

也叫做旁路缓存模式,流程就是先更新数据库,再删除缓存,虽然这种方式也会带来不一致的情况,比如如下场景:

image.png

前提:缓存无数据,数据库有数据。
A:查询,B:更新
过程如下:
step1: A 查缓存,无数据,去读数据库,旧值;
step2: B 更新数据库为新值;
step3: B 删除缓存;
step4: A 将旧值写入缓存。

该场景最终也会出现不一致,产生的根源是是读比写慢,这种是小概率事件,一般很少出现,如果非要解决这种情况,还是上面说的延迟双删

Read/Write Through

上面的方式,数据库是缓存的来源,主导是数据库,而  Read/Write Through模式,相当于缓存占主导。在 cache-aside 模式中,我们的应用代码需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。而 Read/Write Through 做法是把更新数据库(Repository)的操作由缓存自己代理了,所以,对于应用层来说,就简单很多了。可以理解为,应用认为后端就是一个单一的存储,而存储自己维护自己的 Cache。

Read Through

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

image.png

这个简要流程和 Cache-Aside 很像,其实Read-Through就是多了一层Cache-Provider,流程如下:

image.png

Read-Through 实际只是在Cache-Aside之上进行了一层封装,它会让程序代码变得更简洁,同时也减少数据源上的负载

Write Through

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

image.png

值得注意的是,该方案在实现过程中,程序启动时,需将数据库的数据, 提前放到缓存中,不能等启动完成,再放缓存中。

Read Through/Write Through 策略的特点是由缓存节点而非应用程序来和数据库打交道,在我们开发过程中相比 Cache Aside 策略要少见一些,原因是我们经常使用的分布式缓存组件,无论是 Memcached 还是 Redis 都不提供写入数据库和自动加载数据库中的数据的功能。而我们在使用本地缓存的时候可以考虑使用这种策略。

Write Behind

Write Behind 又叫 Write Back。底层思想就是在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的 I/O 操作速度飞快(因为是直接操作内存),同时带来吞吐量大幅上升;因为异步,Write Behind 还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。

image.png

但是,其带来的问题是,数据不是强一致性的,而且可能会丢失(我们知道 Unix/Linux 非正常关机会导致数据丢失,就是因为这个事)。在软件设计上,我们基本上不可能做出一个没有缺陷的设计,就像算法设计中的时间换空间,空间换时间一个道理,有时候,强一致性和高性能,高可用和高性性是有冲突的。如果说软件功能模块的思维是逻辑与实现,那么软件架构设计的思维是权衡与取舍

这种方式下,缓存和数据库的一致性不强,对一致性要求高的系统要谨慎使用。但是它适合频繁写的场景,MySQL 的InnoDB Buffer Pool 机制就使用到这种模式。

Write Behind 实际应用

按照实际架构应用层面落地参考方案,流程图如下(以用户发表视频为例)

image.png

按照这种方案,正常来说只要 10 分钟内数据正常写完就没问题,可以实现最终一致性,但是一旦超出 10 分钟,就会缓存失效,造成缓存不一致

如果出现这种 kafka 消费入库失败,则会触发报警系统,看具体是什么问题,基本上 kaka 入库失败只有两种情况,一种是出错了,另外一种是消费能力不够,那么直接扩容即可

后记

说到底这个问题根本没有通用方案,需要根据场景做权衡,比如类似于微博这样高并发场景,那么上边只要涉及删除缓存的方案基本都很难实现,因为很可能删除缓存的下一时间热点数据直接全部打在数据库上,整个服务直接崩溃

然后本质上缓存和数据库的更新就不是一个原子操作,想要彻底实现强一致性,可以了解下分布式事务或是其他强一致性协议,比如说两阶段提交协议 —— prepare, commit/rollback,比如 Java 7 的 XAResource,还有 MySQL 5.7 的 XA Transaction,有些 cache 也支持 XA,比如 EhCache

参考链接

  • 美团二面:Redis 与 MySQL 双写一致性如何保证? - 掘金 (juejin.cn)
  • 分布式缓存--缓存与数据库一致性方案 - 小猪爸爸 - 博客园 (cnblogs.com)
  • 如何保障 MySQL 和 Redis 的数据一致性? | 二哥的 Java 进阶之路 (javabetter.cn)
  • 三种缓存策略:Cache Aside 策略、Read/Write Through 策略、Write Back 策略
  • 分布式系统中的缓存与数据库一致性

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

AI推介-多模态视觉语言模型VLMs论文速览(arXiv方向):2024.04.05-2024.04.10

文章目录~ 1.BRAVE: Broadening the visual encoding of vision-language models2.ORacle: Large Vision-Language Models for Knowledge-Guided Holistic OR Domain Modeling3.MedRG: Medical Report Grounding with Multi-modal Large Language Model4.InternLM-XComposer2-4…

论软件系统的架构风格,使用三段论 写一篇系统架构师论文

软件系统的架构风格是指在软件系统设计与开发过程中,采用的一组相互协调的设计原则、模式和实践。这些风格不仅影响着系统的技术实现,还关乎到系统的可维护性、可扩展性和可靠性等关键质量属性。通过三段论的结构,本文旨在探讨软件系统架构风…

cocos jsb调用IOS震动功能

js端代码: export default class JsbUtils {public static callVibration(data: any): void {if (CC_JSB) {// ts-ignorejsb.reflection.callStaticMethod(JsBridge, sayMe);jsb.reflection.callStaticMethod(JsBridge, callVibration:, data);}}} export const jsb…

华为校招机试 - 网络保卫战(20240410)

题目描述 公有云的某个 region 内,N 个网络节点组网情况可以使用一个 N * N 的矩阵 matrix 表示。 在这个组网图中,matrix[i][j] = p 时,表示用户在编号为 i 的节点访问编号为 j 的节点时,必须在 i 节点上具有 ≥ p 的权限等级( p = 0 时表示无法通过第 i 节点访问 j 节…

hive的自定义函数

一、自定义函数的实现方式 1.创建临时函数 (1)创建maven项目&#xff0c;并加入依赖 <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>3.1.2</version> </dependency> (2)编写…

02-攻防世界PHP2

题目 authenticate:证明什么是真的 解题 观察题目可知&#xff0c;访问index.phps可能会有不一样的发现 http://61.147.171.105:51671/index.phps访问该链接&#xff0c;可以得到下面的界面 这里只显示出了部分代码&#xff0c;右键该界面&#xff0c;点击查看源代码&#xf…

使用ArrayList.removeAll(List list)导致的机器重启

背景 先说一下背景&#xff0c;博主所在的业务组有一个核心系统&#xff0c;需要同步两个不同数据源给过来的数据到redis中&#xff0c;但是每次同步之前需要过滤掉一部分数据&#xff0c;只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。 由…

Qt/QML编程之路:图片进度条的实现(50)

要实现进度条,而进度条是通过一个图片来展示的,比如逐渐增大的音量,或者逐步增大的车速,通过图片显示的效果肯定更好一些。最直接的想法是通过一个透明的rectagle,把不想让看到的遮住,实际上这种方法不可行。 import QtQuick 2.5 import QtQuick.Window 2.2 import QtGra…

【学习】移动端兼容性测试有什么方法及重要性

随着移动互联网的快速发展&#xff0c;移动应用程序已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;由于各种移动设备的硬件和软件差异&#xff0c;移动应用程序的兼容性问题也越来越突出。因此&#xff0c;移动端兼容性测试成为了一个重要的环节&#xff0c;它可以…

如何在 Android 设备上恢复已删除/丢失的文档

随着Android设备内存容量的不断增加&#xff0c;许多人将手机作为移动硬盘来存储大量文档或其他文件。由于某些原因&#xff0c;文件丢失绝对是一场彻头彻尾的噩梦&#xff0c;因为里面的数据可能是要汇报的学习档案、领导会议的安排、或者付费电子书等。通常&#xff0c;你首先…

如何备考蓝桥杯赛事 怎样才能取得好成绩?

在计算机领域&#xff0c;蓝桥杯赛事一直是备受关注的比赛之一。参加蓝桥杯不仅可以锻炼自己的编程能力&#xff0c;还能够结识志同道合的朋友&#xff0c;拓展自己的人际关系。然而&#xff0c;想要在蓝桥杯赛事中取得好成绩并不是一件容易的事情&#xff0c;需要充分的准备和…

02_JavaWeb中的Tomcat(详解)

文章目录 Tomcat1, 概述1.1 安装1.2 目录结构1.3 启动/停止 2, 资源部署2.1 直接部署: 主要和重要的方式2.2 虚拟映射: 重要2.2.1 方式一:2.2.1 方式二: 2.3 原理解析 3, Tomcat组件3.1 Connector3.2 Engine3.2.1 Host3.2.1.1 Context 4, 其它: 重要4.1 设置 Tomcat 1, 概述 w…

sql server2008触发器

sql server在Navicat工具不能插入数据 可以去写代码插入&#xff0c;代码连接sql server可以插入 或者使用sql server专门的工具 BEGINdeclare a int;declare s t_amount;select a baddebt_age_id,srate from aa_baddebt_age;INSERT INTO dade(id,name) VALUES(a,s) END

手机外屏碎8折维修,天星金融(原小米金融)助阵米粉节

今年4月6日是小米公司14岁生日&#xff0c;也是一年一度的盛会“米粉节”。据“小米服务”官方公众号发布的消息&#xff0c;为庆祝这一重要日子&#xff0c;从4月6日起至4月17日&#xff0c;为用户带来一系列服务权益。 据悉&#xff0c;活动共包括8项服务权益&#xff0c;其…

2024年认证杯SPSSPRO杯数学建模B题(第一阶段)神经外科手术的定位与导航全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 B题 神经外科手术的定位与导航 原题再现&#xff1a; 人的大脑结构非常复杂&#xff0c;内部交织密布着神经和血管&#xff0c;所以在大脑内做手术具有非常高的精细和复杂程度。例如神经外科的肿瘤切除手术或血肿清除手术&#xff0c;通常需要…

12.4.1 实验1:配置CDP

12.4.1 实验1&#xff1a;配置CDP 1、实验目的 通过本实验可以掌握: CDP特征。CDP配置和调试方法。通过CDP查看设备直连邻居信息的方法。 2、实验拓扑 配置CDP的实验拓扑如上图所示。 3、实验步骤 &#xff08;1&#xff09;配置路由器R1 R1(config)#interface serial 0…

选择成为一名程序员:兴趣与职业发展的交织

在当今信息化、数字化的时代&#xff0c;程序员这一职业越来越受到年轻人的青睐。那么&#xff0c;是什么原因驱使他们选择走上编程这条道路呢&#xff1f;是纯粹的兴趣使然&#xff0c;还是对未来职业发展的深思熟虑&#xff1f;本文将深入探讨这一话题&#xff0c;分析兴趣与…

【opencv】示例-pca.cpp PCA图像重建演示

// 加载必要的头文件 #include <iostream> // 用于标准输入输出流 #include <fstream> // 用于文件的输入输出 #include <sstream> // 用于字符串的输入输出流操作#include <opencv2/core.hpp> // OpenCV核心功能的头文件 #include "o…

Pycharm通过配置隧道连接远程服务器

前言&#xff1a; 上篇有说到局域网windows和服务器互通的情况下连接远程pycharm&#xff0c;这次咱们来说下通过跳板机的方式连接服务器如何做到windows远程连接到服务器 1&#xff1a;设置SSH隧道或SSH代理 ssh -L localhost:LOCAL_PORT:FINAL_SERVER_IP:FINAL_SERVER_PORT…

Prompt编写——安全边界

在编写prompt&#xff08;提示、指令或引导语&#xff09;时&#xff0c;设置安全边界主要是为了防止生成的内容超出预期或包含不合适、有害的信息。特别是在使用大型语言模型时&#xff0c;如GPT系列模型&#xff0c;设置安全边界至关重要。以下是一些建议来设置安全边界&…