当前位置: 首页 > news >正文

Redis如何与数据库保持双写一致性

一、核心问题与基础策略

问题根源

并发场景下,“更新数据库” 与 “操作缓存” 是两个独立动作,无法保证原子性,可能出现:

  1. 线程 A 删了缓存,还没更新数据库,线程 B 读了旧数据到缓存。
  2. 线程 A 更新了数据库,还没删缓存,线程 B 读了旧缓存。

基础原则

不建议 “更新缓存”,只建议 “删除缓存”。因为更新缓存容易在并发下产生脏数据,而删除缓存后,下次读取会从数据库重新加载最新数据(Cache-Aside Pattern)。


二、方案一:延迟双删

流程

  1. 先删除缓存
  2. 更新数据库
  3. 休眠一小段时间(如 500ms - 1s)再次删除缓存

解决的问题

如果在步骤 1 和 2 之间有并发读把旧数据写回了缓存,步骤 3 的 “延迟二次删除” 可以把这个脏数据删掉。

Java 代码示例

@Service public class ProductService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private ProductMapper productMapper; // 自定义线程池用于异步延迟删除 private final ExecutorService executor = Executors.newSingleThreadExecutor(); @Transactional(rollbackFor = Exception.class) public void updateProduct(Product product) { String key = "product:" + product.getId(); // 1. 先删除缓存 redisTemplate.delete(key); // 2. 更新数据库 productMapper.updateById(product); // 3. 异步延迟二次删除 (防止并发脏读) executor.submit(() -> { try { Thread.sleep(500); // 时间根据业务耗时评估 redisTemplate.delete(key); System.out.println("延迟双删成功: " + key); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } }

三、方案二:基于消息队列的异步删缓存

如果 “延迟双删” 的第二次删除失败了,数据就不一致了。为了保证删除动作一定执行,可以引入消息队列(MQ)。

  1. 更新数据库。
  2. 数据库操作成功后,发送一条 “删除缓存” 的消息到 MQ。
  3. 消费者接收消息,执行删除缓存操作。
  4. 配合消费者的重试机制(如果 Redis 挂了或删除失败,不断重试直到成功)。

四、方案三:基于 Binlog 订阅

核心思路

  1. 业务代码只需要更新数据库,不关心缓存。
  2. 利用 MySQL 的 Binlog 日志,使用中间件伪装成 MySQL Slave 监听 Binlog。
  3. 监听到数据变更后,通过程序解析 Binlog,异步删除 Redis 中对应的缓存。

优点

  • 业务代码极其清爽,没有任何缓存逻辑。
  • 只要数据库更新成功,缓存最终一定会被删除。

五、方案四:分布式锁

如果业务对数据一致性要求极高(比如金融、订单核心),不允许任何中间状态的脏读,可以使用分布式锁。(强一致性带来的坏处就是性能较差)

流程

  1. 读数据:加分布式锁 -> 读缓存 -> 没命中则读数据库并写回缓存 -> 释放锁。
  2. 写数据:加分布式锁 -> 删缓存 -> 更新数据库 -> 释放锁。

Java代码示例

@Autowired private RedissonClient redissonClient; public void updateWithLock(Product product) { String lockKey = "lock:product:" + product.getId(); RLock lock = redissonClient.getLock(lockKey); try { // 尝试加锁 lock.lock(); // 1. 删缓存 // 2. 更数据库 } finally { // 释放锁 lock.unlock(); } }

六、总结与选型建议

表格

方案一致性性能维护成本适用场景
延迟双删最终并发量一般,业务简单
消息队列最终并发量高,需要可靠性保证
Binlog最终中高首选,业务解耦,数据量大
分布式锁对一致性要求极高(钱、库存)
http://www.jsqmd.com/news/449635/

相关文章:

  • 枚举类的设计模式
  • GTE-Base-ZH在互联网内容生态中的应用:从文本理解到价值挖掘
  • ComfyUI快速部署:镜像开箱即用,省去复杂环境配置步骤
  • 盘点2026无线流量计值得选的品牌,专业评测给你参考 - 工业品网
  • 汇川三轴示教 可编程 触摸屏程序 自己买来学习的 功能强大 触摸屏 PLC 程序 汇川H5U与
  • 不想安装软件?几个好用的 WebP 转 PNG 在线工具推荐
  • 对比一圈后!风靡全网的降AI率网站 —— 千笔AI
  • MySQL 双机互备
  • 在线学籍管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
  • CDN安全防护体系的完整构建指南
  • OpenClaw前世今生与未来展望
  • 循环链表初识
  • 【CTFshow-pwn系列】03_栈溢出【pwn 056-057】详解:32位 与64位Shellcode 与 Linux 系统调用底层原理剖析
  • 2026徐汇宠物耳道内窥镜检查专家推荐,别错过!猫咪乳糜胸手术/狗狗绝育/宠物绝育,宠物耳道内窥镜检查专家选哪个 - 品牌推荐师
  • Qwen3-4B实战:手把手教你用纯文本模型解决日常办公问题
  • MiniCPM-o-4.5-nvidia-FlagOS快速上手:Ollama本地部署与模型管理对比
  • 【2025最新】基于SpringBoot+Vue的智慧党建系统管理系统源码+MyBatis+MySQL
  • 【ThreadLocal忘记清理把堆吃爆了:一次线上OOM救火到半夜】
  • 备课一半全耗在找素材上?老师用什么 AI 工具做课件素材,我对比后才知道差距
  • 中文语义检索新范式:GTE-Chinese-Large在无监督关键词扩展与主题建模中的创新应用
  • RexUniNLU零样本机器阅读理解教程:中文问答式信息抽取详细步骤
  • 企业级智慧学生校舍系统管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】
  • DT7遥控器与DR16接收器
  • 小白努力学习技术,从1级升级开始 目前等级:13级(5/10)
  • 拒绝魔法值:用枚举/常量替代,Java代码更易维护
  • 基于瑞萨的血压测量仪电路实现
  • 《创业之路》-891- 法律的本质是利用国家的群体的力量,强制性约束自私的人性、打击残酷的兽性,维持社会的稳定。
  • HC04-Arduino UNO-LED开关
  • Qwen3-VL:30B模型应用:智能文档处理系统开发
  • 《创业之路》-892- 法律的本质是秩序,正义只是它的副产品