缓存与数据库数据一致性 详解
缓存与数据库数据一致性详解
在分布式系统中,缓存(如 Redis、Memcached)与数据库(如 MySQL、PostgreSQL)一起使用是提高系统性能的常用方法。然而,缓存与数据库可能因更新时序、操作失误等原因出现数据不一致的问题,导致数据读取异常,影响用户体验和业务逻辑的正确性。
1. 缓存与数据库数据不一致的原因
-  
先更新数据库,再删除缓存:
- 更新数据库后,如果删除缓存的操作失败或延迟,缓存仍会返回旧数据。
 
 -  
先删除缓存,再更新数据库:
- 缓存删除后,其他并发请求可能从数据库读取旧数据并重新写入缓存,导致缓存出现脏数据。
 
 -  
缓存过期:
- 缓存中数据过期后,新的请求直接访问数据库,但可能未正确写入或更新缓存。
 
 -  
分布式系统中的延迟与故障:
- 在分布式环境中,网络延迟、服务故障、节点不一致等问题会导致数据同步失败。
 
 -  
异步更新:
- 使用异步任务更新缓存时,任务执行延迟或失败会导致缓存与数据库数据不同步。
 
 
2. 数据一致性要求
数据一致性可以分为以下三种场景:
-  
强一致性:
- 数据写入数据库后,缓存必须立即更新或删除,确保读取到的数据与数据库一致。
 - 适用场景:金融系统、订单系统等对一致性要求极高的业务。
 
 -  
最终一致性:
- 数据更新后,允许短时间内数据不一致,但最终状态需要保持一致。
 - 适用场景:电商商品库存、用户行为分析等。
 
 -  
弱一致性:
- 数据更新后,不保证缓存与数据库的数据一致性。
 - 适用场景:对一致性要求不高的业务,如热点排行榜等。
 
 
3. 缓存与数据库一致性的解决方案
3.1 经典策略:Cache Aside(旁路缓存模式)
流程
- 读操作: 
- 先从缓存读取数据,如果缓存命中则返回数据;
 - 如果缓存未命中,则从数据库读取数据,并将结果写入缓存。
 
 - 写操作: 
- 更新数据库后,删除对应的缓存数据。
 
 
优点
- 简单易实现;
 - 减少了写缓存的复杂性,避免数据库和缓存操作的顺序冲突。
 
缺点
- 仍可能出现先更新数据库再删除缓存导致的不一致问题。
 
3.2 延时双删策略
流程
- 删除缓存:更新数据库之前,先删除缓存中的数据。
 - 更新数据库:更新数据库中的数据。
 - 二次删除:等待一定延时后,再次删除缓存。
 
伪代码实现
// 更新逻辑
redis.del("key");  // Step 1: 删除缓存
updateDatabase(data);  // Step 2: 更新数据库
Thread.sleep(500);  // Step 3: 延迟一定时间
redis.del("key");  // Step 4: 再次删除缓存
 
优点
- 有效解决先删缓存再更新数据库引起的并发脏数据问题。
 
缺点
- 延时选择的时间需合理,太短可能无效,太长会影响性能;
 - 依赖数据库事务的准确性。
 
3.3 读写一致性控制
流程
- 数据写入或更新时,先删除缓存;
 - 设置一个短时间内的“读屏障”,在此期间内,读取数据库的最新数据,而不是缓存的数据。
 
实现方式
- 设置一个标记位,表示某数据正在更新,禁止读取缓存。
 - 示例:
String lockKey = "lock:key"; if (redis.get(lockKey) == null) {data = redis.get("key");if (data == null) {data = database.query("key");redis.set("key", data, 60);} } else {data = database.query("key"); } 
优点
- 能够在数据更新时有效屏蔽脏数据读取。
 
缺点
- 增加了系统复杂性;
 - 需要合理设计“屏障时间”。
 
3.4 分布式锁控制一致性
流程
- 更新数据库和缓存时加分布式锁,确保同一时间只有一个线程能操作缓存和数据库。
 - 锁释放后,其余线程才能进行读写操作。
 
实现方式
- 使用 Redis 的分布式锁:
boolean lock = redis.setnx("lock:key", "1", 30); // 获取分布式锁 if (lock) {updateDatabase(data); // 更新数据库redis.del("key"); // 删除缓存redis.del("lock:key"); // 释放锁 } 
优点
- 有效避免并发引发的数据不一致问题。
 
缺点
- 性能开销较大,适合对一致性要求高的场景。
 
3.5 消息队列异步更新
流程
- 数据库更新后,发送一条更新消息到消息队列。
 - 消费者监听消息队列,收到更新消息后同步更新缓存。
 
实现方式
- 数据库更新逻辑:
updateDatabase(data); messageQueue.sendMessage("update_cache", "key"); - 消息消费者逻辑:
messageQueue.listen("update_cache", (key) -> {data = database.query(key);redis.set(key, data, 60); }); 
优点
- 提高了系统解耦性,缓存更新操作由异步任务完成。
 
缺点
- 依赖消息队列的可靠性;
 - 延迟可能导致短时间内不一致。
 
3.6 缓存更新重试机制
流程
- 当缓存更新失败时,记录失败操作并定期重试。
 - 可结合延时队列或定时任务实现。
 
实现方式
- 更新失败记录到延时队列:
try {redis.del("key"); } catch (Exception e) {delayQueue.add("key"); } 
优点
- 确保缓存最终更新成功。
 
缺点
- 增加了系统复杂度。
 
4. 数据一致性解决方案的对比
| 方案 | 优点 | 缺点 | 适用场景 | 
|---|---|---|---|
| Cache Aside | 简单易用,易于实现 | 高并发场景下可能导致短时间不一致 | 一般业务场景 | 
| 延时双删 | 有效解决并发问题 | 延迟时间难以准确控制 | 一般业务场景 | 
| 读写一致性控制 | 避免数据更新时的脏读 | 增加系统复杂性 | 高一致性要求场景 | 
| 分布式锁 | 保证强一致性 | 性能较低 | 强一致性要求场景 | 
| 消息队列异步更新 | 解耦数据库和缓存逻辑,提高吞吐量 | 消息延迟可能引起短暂不一致 | 高吞吐量、最终一致性场景 | 
| 重试机制 | 保证最终一致性 | 增加系统复杂度 | 缓存更新易失败的场景 | 
5. 实际案例分析
案例1:秒杀场景
- 问题:秒杀商品库存频繁更新,要求缓存与数据库一致。
 - 解决方案: 
- 使用延时双删策略,确保缓存数据与数据库同步。
 
 
案例2:电商商品价格
- 问题:商品价格需要强一致性,不能显示过期价格。
 - 解决方案: 
- 使用分布式锁,确保价格更新后缓存一致。
 
 
案例3:用户信息修改
- 问题:用户更新个人信息后,需保证缓存中的数据一致。
 - 解决方案: 
- 使用消息队列异步更新缓存。
 
 
6. 总结
缓存与数据库数据一致性问题是分布式系统设计中的核心问题,需要根据业务场景和一致性要求选择适合的方案:
- 一般场景:优先使用 Cache Aside 模式。
 - 高一致性要求:可结合延时双删或分布式锁。
 - 最终一致性:推荐使用消息队列异步更新。
 - 高可用性
:采用重试机制或缓存预热。 
通过合理设计,可以在性能和一致性之间找到最佳平衡点,提升系统的稳定性和用户体验。
相关文章:
缓存与数据库数据一致性 详解
缓存与数据库数据一致性详解 在分布式系统中,缓存(如 Redis、Memcached)与数据库(如 MySQL、PostgreSQL)一起使用是提高系统性能的常用方法。然而,缓存与数据库可能因更新时序、操作失误等原因出现数据不一…...
每日计划-1203
1. 完成 236. 二叉树的最近公共祖先  /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/ class Solution {public:TreeNode* lowe…...
HTML5动漫主题网站——天空之城 10页 html+css+设计报告成品项目模版
📂文章目录 一、📔网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站演示 五、⚙️网站代码 🧱HTML结构代码 💒CSS样式代码 六、🔧完整源码下载 七、📣更多 一、&#…...
分布式会话 详解
分布式会话详解 在分布式系统中,用户的会话状态需要在多个服务器或节点之间共享或存储。分布式会话指的是在这种场景下如何管理和存储会话,以便在多个节点上都能正确识别用户状态,从而保证用户体验的一致性。 1. 为什么需要分布式会话 在单…...
探索仓颉编程语言:官网上线,在线体验与版本下载全面启航
文章目录 每日一句正能量前言什么是仓颉编程语言仓颉编程语言的来历如何使用仓颉编程语言在线版本版本下载后记 每日一句正能量 当你被孤独感驱使着去寻找远离孤独的方法时,会处于一种非常可怕的状态。因为无法和自己相处的人也很难和别人相处,无法和别人…...
Ubuntu无法连接Linux
检查网络连接 确保你的机器能够正常连接互联网。你可以尝试 ping 一下 GitHub 或其他网站,确认是否有网络问题: ping github.com如果无法 ping 通 GitHub,检查一下你的网络连接。 检查 GitHub 状态 有时候 GitHub 本身可能会出现服务故障。你…...
【Spring】注解开发
为了提高开发效率,从 Spring 2.0 开始引入了多种注解,而在 Spring 3.0 中则实现了纯注解的开发方式。 一、注解的使用 在 Spring 2.0 之后,使用注解进行开发主要分为两个步骤: 定义 Bean:使用 Component 注解来定义…...
数字图像稳定DIS介绍目录
之前用OpenCV做过防抖,OpenCV处理时,先处理一遍,再输出视频。二者相差还是挺大的。 前 言.......................................................................................................................................... …...
【人工智能-基础】SVM中的核函数到底是什么
文章目录 支持向量机(SVM)中的核函数详解1. 什么是核函数?核函数的作用:2. 核技巧:从低维到高维的映射3. 常见的核函数类型3.1 线性核函数3.2 多项式核函数3.3 高斯径向基函数(RBF核)4. 总结支持向量机(SVM)中的核函数详解 支持向量机(SVM,Support Vector Machine)…...
字节青训Marscode——8:找出整形数组中超过一半的数
问题描述 小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。 测试样例 样例1: 输入:array [1, 3, 8, 2, 3, 1, 3, 3, 3] 输出…...
C++ 异步编程的利器std::future和std::promise
1、背景 在现代计算机系统中,许多任务可能需要花费较长时间才能完成,例如网络请求、文件读取、大规模数据计算等。如果在程序中同步地执行这些任务,会导致主线程被阻塞,整个程序在任务执行期间无法响应其他操作,用户体…...
CRM 系统中的 **知识库功能** 的设计与实现
CRM 系统中的 **知识库功能** 旨在为用户提供一个集中的平台,用于存储、组织和管理有关系统功能、常见问题、使用技巧、操作文档等信息。它能够帮助用户高效解决问题、快速获取所需信息,从而提升使用体验并减少客户支持负担。 ### 一、知识库功能的设计…...
重学设计模式-工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
在平常的学习和工作中,我们创建对象一般会直接用new,但是很多时候直接new会存在一些问题,而且直接new会让我们的代码变得非常繁杂,这时候就会巧妙的用到设计模式,平常我们通过力扣学习的算法可能并不会在我们工作中用到…...
【C语言】结构体(四)
本篇重点是typedef关键字 一,是什么? typedef用来定义新的数据类型,通常typedef与结构体的定义配合使用。 简单来说就是取别名 ▶ struct 是用来定义新的数据类型——结构体 ▶ typedef是给数据类型取别名。 二,为什么…...
swift类方法为什么使用表派发?
直接上答案:因为表派发允许子类重写父类的方法,并在运行时根据对象的实际类型调用正确的方法实现。 什么是表派发? 首先我们先知道的是,swift当中函数的派发机制主要分为静态派发和动态派发。动态派发又分为表派发和消息派发。 …...
php实现AES/CBC/PKCS5Padding加密
接口文档 文档给过来的案例是java程序的,参照其思路,造一个php版本 构造aes对称加密 public static function encry($data){$data "要加密的数据";$key 你的256位密钥; // 密钥应该是16字节(128位),24字节…...
Anaconda3安装及使用
Anaconda3安装及使用 Linux中安装Anaconda31.安装 Anaconda32.配置环境变量3.验证是否成功 Conda环境和包管理1.Conda 环境初始化2.Conda Env 管理3.Conda 软件包管理 Linux中安装Anaconda3 下面是在Linux中安装Anaconda3-2021.05的教程,其他版本Anaconda更换名字即…...
Argon2-cffi与argon2-cffi-bindings:深入理解及其应用
Argon2-cffi与argon2-cffi-bindings的关系 在Python密码学领域,argon2-cffi和argon2-cffi-bindings是两个经常被提及的库。尽管它们的名字相似,但它们在实现和用途上有所不同。argon2-cffi是一个提供Argon2哈希算法的Python库,而argon2-cffi-…...
spring boot+jpa接入达梦数据库
文章目录 前言依赖配置对应的domain类和repository 前言 最近有一个新项目,由于信息安全等要求只能使用达梦数据库(dm8),之前从来没用过,特此开一个笔记记录一下spring bootjpa如何使用达梦数据库完成开发。 依赖 p…...
Vite构建,用NodeJS搭建一个简单的Vite服务
Vite 是一个现代的前端构建工具,由 Vue.js 作者尤雨溪创建。它主要用于开发和构建现代 JavaScript 应用,尤其是单页应用(SPA)。Vite 相比于传统的构建工具(如 Webpack)有几个显著的优势: 即时开…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
