Redis 与 MySQL 数据一致性保障方案
在高并发场景下,Redis 作为缓存中间件与 MySQL 数据库配合使用时,数据一致性是一个关键挑战。本文将详细探讨如何保障 Redis 与 MySQL 的数据一致性,并结合 Java 代码实现具体方案。
数据不一致的原因分析
在分布式系统中,Redis 与 MySQL 的数据不一致主要由以下原因导致:
- 读写并发问题:多个线程同时进行读写操作时,可能导致数据在缓存和数据库中的状态不一致
- 更新策略不当:缓存更新策略选择不合理,如先删除缓存再更新数据库时可能出现并发问题
- 异常处理不足:更新过程中出现异常,导致缓存和数据库的更新操作未完成
缓存更新策略选择
常见的缓存更新策略有以下几种:
-
Cache-Aside Pattern(旁路缓存模式)
- 读操作:先读缓存,缓存不存在则读数据库并更新缓存
- 写操作:先更新数据库,再删除缓存
-
Read/Write Through Pattern(读写穿透模式)
- 应用程序只操作缓存,由缓存层负责数据库的读写
-
Write Behind Caching Pattern(写回模式)
- 写操作只更新缓存,由缓存层异步更新数据库
在实际应用中,Cache-Aside Pattern 是最常用的策略,下面将详细介绍其实现。
基于 Cache-Aside Pattern 的 Java 实现
以下是基于 Spring Boot 框架实现的 Cache-Aside Pattern 代码示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.concurrent.TimeUnit;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 缓存键前缀private static final String CACHE_KEY_PREFIX = "user:";// 缓存过期时间(秒)private static final long CACHE_EXPIRE_TIME = 3600;/*** 查询用户(Cache-Aside Pattern读取实现)*/public User getUserById(Long userId) {// 1. 先从Redis中获取数据String cacheKey = CACHE_KEY_PREFIX + userId;User user = (User) redisTemplate.opsForValue().get(cacheKey);if (user != null) {return user;}// 2. Redis中不存在,从数据库中获取user = userRepository.findById(userId).orElse(null);if (user != null) {// 3. 将数据库结果写入RedisredisTemplate.opsForValue().set(cacheKey, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);}return user;}/*** 更新用户(Cache-Aside Pattern写入实现)*/@Transactionalpublic User updateUser(User user) {// 1. 先更新数据库User updatedUser = userRepository.save(user);// 2. 删除缓存String cacheKey = CACHE_KEY_PREFIX + user.getId();redisTemplate.delete(cacheKey);return updatedUser;}/*** 删除用户(Cache-Aside Pattern删除实现)*/@Transactionalpublic void deleteUser(Long userId) {// 1. 先删除数据库记录userRepository.deleteById(userId);// 2. 删除缓存String cacheKey = CACHE_KEY_PREFIX + userId;redisTemplate.delete(cacheKey);}
}
解决并发问题的优化方案
上述实现中,在高并发场景下仍可能出现数据不一致问题,以下是几种优化方案:
- 延迟双删策略
@Transactional
public User updateUser(User user) {// 1. 先删除缓存String cacheKey = CACHE_KEY_PREFIX + user.getId();redisTemplate.delete(cacheKey);// 2. 更新数据库User updatedUser = userRepository.save(user);// 3. 延迟一段时间后再次删除缓存(异步执行)CompletableFuture.runAsync(() -> {try {// 等待一段时间,确保读请求全部完成Thread.sleep(100);redisTemplate.delete(cacheKey);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});return updatedUser;
}
- 分布式锁机制
public User getUserById(Long userId) {String cacheKey = CACHE_KEY_PREFIX + userId;User user = (User) redisTemplate.opsForValue().get(cacheKey);if (user != null) {return user;}// 获取分布式锁RLock lock = redissonClient.getLock("user_cache_lock:" + userId);try {// 尝试获取锁,等待10秒,自动释放时间30秒boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);if (isLocked) {// 再次检查缓存,避免重复查询数据库user = (User) redisTemplate.opsForValue().get(cacheKey);if (user != null) {return user;}// 查询数据库user = userRepository.findById(userId).orElse(null);if (user != null) {redisTemplate.opsForValue().set(cacheKey, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);}}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 释放锁if (lock.isHeldByCurrentThread()) {lock.unlock();}}return user;
}
最终一致性保障方案
对于一些对实时一致性要求不是特别高的场景,可以采用异步补偿机制保证最终一致性:
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;@Service
public class CacheSyncService {@Autowiredprivate KafkaTemplate<String, Object> kafkaTemplate;// 发送缓存同步消息public void sendCacheSyncMessage(Long userId) {kafkaTemplate.send("cache_sync_topic", userId);}// 消费缓存同步消息@KafkaListener(topics = "cache_sync_topic")public void handleCacheSyncMessage(Long userId) {try {// 查询数据库最新数据User user = userRepository.findById(userId).orElse(null);// 更新缓存if (user != null) {String cacheKey = CACHE_KEY_PREFIX + userId;redisTemplate.opsForValue().set(cacheKey, user, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);}} catch (Exception e) {// 记录异常日志,可添加重试机制log.error("处理缓存同步消息失败,userId: {}", userId, e);}}
}
总结
保障 Redis 与 MySQL 的数据一致性需要根据业务场景选择合适的策略,并结合多种技术手段:
- 优先使用 Cache-Aside Pattern 作为基础缓存更新策略
- 在高并发场景下采用延迟双删或分布式锁解决并发问题
- 对于非实时场景,可采用异步消息队列实现最终一致性
- 完善监控和告警机制,及时发现并处理数据不一致问题
通过以上方案的综合应用,可以有效保障 Redis 与 MySQL 的数据一致性,提升系统的稳定性和可靠性。
相关文章:
Redis 与 MySQL 数据一致性保障方案
在高并发场景下,Redis 作为缓存中间件与 MySQL 数据库配合使用时,数据一致性是一个关键挑战。本文将详细探讨如何保障 Redis 与 MySQL 的数据一致性,并结合 Java 代码实现具体方案。 数据不一致的原因分析 在分布式系统中,Redis…...
Sentry 接口返回 Status Code 429 Too Many Requests
Sentry 是一个 开源的错误追踪(Error Tracking)平台,主要用于实时捕获和监控应用程序中的异常、错误日志,并帮助开发者快速定位问题根源。 📌 Sentry 的核心功能 自动捕获异常 自动捕捉 JavaScript、Vue、React、Node.…...
数学建模期末速成 聚类分析与判别分析
聚类分析是在不知道有多少类别的前提下,建立某种规则对样本或变量进行分类。判别分析是已知类别,在已知训练样本的前提下,利用训练样本得到判别函数,然后对未知类别的测试样本判别其类别。 聚类分析 根据样本自身的属性…...

【工具教程】PDF电子发票提取明细导出Excel表格,OFD电子发票行程单提取保存表格,具体操作流程
在企业财务管理领域,电子发票提取明细导出表格是不可或缺的工具。 月末财务结算时,财务人员需处理成百上千张电子发票,将发票明细导出为表格后,通过表格强大的数据处理功能,可自动分类汇总不同项目的支出金额ÿ…...

基于STM32的DHT11温湿度远程监测LCD1602显示Proteus仿真+程序+设计报告+讲解视频
DHT11温湿度远程监测proteus仿真 1. 主要功能2.仿真3. 程序4. 设计报告5. 资料清单&下载链接 基于STM32的DHT11温湿度远程监测LCD1602显示Proteus仿真设计(仿真程序设计报告讲解视频) 仿真图proteus 8.9 程序编译器:keil 5 编程语言:C…...

分类预测 | Matlab实现CNN-BiLSTM-Attention高光谱数据分类预测
分类预测 | Matlab实现CNN-BiLSTM-Attention高光谱数据分类预测 目录 分类预测 | Matlab实现CNN-BiLSTM-Attention高光谱数据分类预测分类效果功能概述程序设计参考资料 分类效果 功能概述 该MATLAB代码实现了一个结合CNN、BiLSTM和注意力机制的高光谱数据分类预测模型&#x…...

微软推出SQL Server 2025技术预览版,深化人工智能应用集成
在Build 2025 大会上,微软向开发者社区开放了SQL Server 2025的测试版本。该版本的技术改进主要涵盖人工智能功能集成、系统性能优化与开发工具链升级三个维度,展示了数据库管理系统在智能化演进方向上的重要进展。 智能数据处理功能更新 新版本的技术亮…...
.net webapi http参数自定义绑定模型
.NET Web API 中 HTTP 参数自定义绑定模型的深度解析 在 .NET Web API 开发里,常规的参数绑定往往能满足大部分需求。不过,当遇到一些特殊情况时,就需要自定义将 HTTP 参数绑定到 action 特定模型参数了。接下来,我们就深入探讨如…...

RocketMQ入门5.3.2版本(基于java、SpringBoot操作)
一、RocketMQ概述 RocketMQ是一款由阿里巴巴于2012年开源的分布式消息中间件,旨在提供高吞吐量、高可靠性的消息传递服务。主要特点有: 灵活的可扩展性 海量消息堆积能力 支持顺序消息 支持多种消息过滤方式 支持事务消息 支持回溯消费 支持延时消…...

使用osqp求解简单二次规划问题
文章目录 一、问题描述二、数学推导1. 目标函数处理2. 约束条件处理 三、代码编写 一、问题描述 已知: m i n ( x 1 − 1 ) 2 ( x 2 − 2 ) 2 s . t . 0 ⩽ x 1 ⩽ 1.5 , 1 ⩽ x 2 ⩽ 2.5 min(x_1-1)^2(x_2-2)^2 \qquad s.t. \ \ 0 \leqslant x_1 \leqslant 1.5,…...
Ubuntu创建修改 Swap 文件分区的步骤——解决嵌入式开发板编译ROS2程序卡死问题
Ubuntu创建修改 Swap 文件分区的步骤——解决嵌入式开发板编译ROS2程序卡死问题 1. 问题描述2. 创建 / 修改 Swap 分区2.1 创建 Swap 文件 (推荐)2.2 使用 Swap 分区 (如果已经存在) 3. 注意事项 同步发布在个人笔记Ubuntu创建修改 Swap 文件分区的步骤——解决嵌入式开发板编译…...

【C语言】通用统计数据结构及其更新函数(最值、变化量、总和、平均数、方差等)
【C语言】通用统计数据结构及其更新函数(最值、变化量、总和、平均数、方差等) 更新以gitee为准: gitee 文章目录 通用统计数据结构更新函数附录:压缩字符串、大小端格式转换压缩字符串浮点数压缩Packed-ASCII字符串 大小端转换什…...

Spring AI(10)——STUDIO传输的MCP服务端
Spring AI MCP(模型上下文协议)服务器Starters提供了在 Spring Boot 应用程序中设置 MCP 服务器的自动配置。它支持将 MCP 服务器功能与 Spring Boot 的自动配置系统无缝集成。 本文主要演示支持STDIO传输的MCP服务器 仅支持STDIO传输的MCP服务器 导入j…...

Sklearn 机器学习 缺失值处理 填充数据列的缺失值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 💡使用 Scikit-learn 处理数据缺失值的完整指南 在机器学习项目中,数据缺失是不可避…...

猜字符位置游戏-position gasses
import java.util.*;public class Main {/*字符猜位置游戏;每次提交只能被告知答对几个位置;根据提示答对的位置数推测出每个字符对应的正确位置;*/public static void main(String[] args) {char startChar A;int gameLength 8;List<String> ballList new ArrayList&…...

宝塔安装配置FRP
FRP(Fast Reverse Proxy)作为一款高性能的反向代理应用,能够帮助我们轻松实现内网穿透,将内网服务暴露到公网,满足远程访问、开发调试等多种需求。宝塔面板以其简洁易用的界面和强大的功能,成为众多站长和开…...
元器件基础学习笔记——结型场效应晶体管 (JFET)
场效应晶体管(Field Effect Transistor,FET)简称场效应管,是一种三端子半导体器件,它根据施加到其其中一个端子的电场来控制电流的流动。与双极结型晶体管 (BJT) 不同,场效应晶体管 …...

tableau 实战工作场景常用函数与LOD表达式的应用详解
这是tableau实战工作场景图表制作第七期--常用函数与LOD表达式的应用 数据资源已经与这篇博客捆绑,有需要者可以下载通过网盘分享的文件:3.2-8月成交数据.xlsx等3个文件 链接: https://pan.baidu.com/s/17WtUoZTqzoNo5kTFjua4hw?pwd0623 提取码: 06…...
智能终端与边缘计算按章复习
第1章:智能终端与边缘计算概述 简述计算机网络和Web技术发展过程中,信息和运算从用户本地向Web服务器迁移的趋势,并解释这一过程如何逐步形成了如今的云计算形态。 随着计算机网络和Web技术的不断发展,信息和运算的重心发生了显著…...
C#面试问题61-80
66. What is reflection? 反射是一种机制,它使我们能够编写可以检查应用程序中所 用类型的代码。例如,调用名称与给定字符串相等的方法,或者列出属于给定 对象的所有字段及其值。 在 Convert 方法中,我们根本不知道处理的是什么…...
分布式Session处理的五大主流方案解析
在分布式环境下,Session 处理的核心挑战是确保用户请求在不同服务器间流转时能保持会话状态一致。以下是主流解决方案及优缺点分析: 🔐 一、集中存储方案(主流推荐) Redis/Memcached 存储 原理:将 Session…...
C++ 中的 const 知识点详解,c++和c语言区别
目录 一。C 中的 const 知识点详解1. 基本用法1.1) 定义常量1.2) 指针与 const 2. 函数中的 const2.1)const 参数2.2)const 成员函数 3. 类中的 const3.1)const 成员变量3.2)const 对象 4. const 返回值5. …...

《PyTorch:开启深度学习新世界的魔法之门》
一、遇见 PyTorch:深度学习框架新星登场 在当今的技术领域中,深度学习已然成为推动人工智能发展的核心动力,而深度学习框架则是这场技术革命中的关键工具。在众多深度学习框架里,PyTorch 以其独特的魅力和强大的功能,迅速崛起并占据了重要的地位,吸引着无数开发者和研究者…...

分布式光纤传感(DAS)技术应用解析:从原理到落地场景
近年来,分布式光纤传感(Distributed Acoustic Sensing,DAS)技术正悄然改变着众多传统行业的感知方式。它将普通的通信光缆转化为一个长距离、连续分布的“听觉传感器”,对振动、声音等信号实现高精度、高灵敏度的监测。…...

Spring事务回滚在系统中的应用
以文章发布为例,介绍Spring事务回滚在系统中的应用 事务回滚的核心概念 事务回滚是数据库管理系统中的关键机制,它确保数据库操作要么全部成功,要么全部失败。在Spring框架中,我们可以通过Transactional注解轻松实现事务管理。 …...
.Net Framework 4/C# 属性和方法
一、属性的概述 属性是对实体特征的抽象,用于提供对类或对象的访问,C# 中的属性具有访问器,这些访问器指定在它们的值被读取或写入时需要执行的语句,因此属性提供了一种机制,用于把读取和写入对象的某些特征与一些操作…...

ASP.NET Core使用Quartz部署到IIS资源自动被回收解决方案
iis自动回收的原因 回收机制默认配置,间隔时间是1740分钟,意思是:默认情况下每1740分钟(29小时)回收一次,定期检查应用程序池中的工作进程,并终止那些已经存在很长时间或已经使用了太多资源的工作进程 进程模型默认配…...
Fullstack 面试复习笔记:Spring / Spring Boot / Spring Data / Security 整理
Fullstack 面试复习笔记:Spring / Spring Boot / Spring Data / Security 整理 之前的笔记: Fullstack 面试复习笔记:操作系统 / 网络 / HTTP / 设计模式梳理Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结Fullsta…...

调用.net DLL让CANoe自动识别串口号
1.前言 CANoe9.0用CAPL控制数控电源_canoe读取程控电源电流值-CSDN博客 之前做CAPL通过串口控制数控电源,存在一个缺点:更换电脑需要改串口号 CSDN上有类似的博客,不过要收费,本文根据VID和PID来自动获取串口号,代码…...
第5章:Cypher查询语言进阶
在掌握了Cypher的基础知识后,本章将深入探讨更高级的查询技术。这些进阶技能将帮助您构建更复杂、更高效的查询,解决实际业务中的复杂问题,并充分发挥Neo4j的图数据处理能力。 5.1 复杂查询构建 随着业务需求的复杂性增加,查询也…...