redis面试:如何保证双写一致

一、实际问题

在实际开发中,我们通常会用 Redis 作为缓存来加速数据读取。但当数据需要更新时,就面临一个经典问题:应该先删除缓存,还是先更新数据库?

遗憾的是,无论选择哪种顺序,在并发场景下都可能产生脏数据。

以"先删缓存,再更新数据库"为例,考虑以下时序:

  1. 线程 A 删除缓存
  2. 线程 B 读取缓存未命中,从数据库读到旧值,写入缓存
  3. 线程 A 更新数据库为新值

此时数据库中是新值,而缓存中却是线程 B 写入的旧值,数据不一致就此产生。反过来"先更新数据库,再删缓存"同样存在类似的竞态窗口。

由于线程调度的不确定性,这类问题在高并发下并非小概率事件。要解决它,常见的思路有两种:延迟双删读写锁,各有适用场景。

二、延迟双删

复制代码
1. 删除 Redis 缓存          ← 第一次删
2. 更新数据库
3. 发送一条延时消息到 MQ或者Canal监听binlog日志
4. MQ 消费者收到消息后再次删除缓存  ← 第二次删

延迟双删的核心思想很简单:既然一次删除缓存无法保证一致性,那就删两次,中间给数据库更新留出时间窗口。

基本流程:

  1. 删除 Redis 缓存 ← 第一次删除
  2. 更新数据库
  3. 发送一条延迟消息到 MQ(或通过 Canal 监听 binlog)
  4. MQ 消费者收到消息后再次删除缓存 ← 第二次删除

第一次删除是为了让后续读请求能走到数据库;更新数据库后,再补一次删除,把可能在第一步和第二步之间被脏数据回填的缓存清理掉。

两种实现方式:

  • MQ 延迟消息:业务代码中,更新数据库后向消息队列发送一条延迟消息,消费者到期后执行缓存删除。常用 RocketMQ 的延迟消息或 RabbitMQ 的 TTL + 死信队列来实现。
  • Canal 监听 binlog :Canal 伪装为 MySQL 的从节点,通过读取 binlog 日志感知数据变更,自动触发缓存删除。这种方式的优势在于不需要修改业务代码,对应用层完全透明。

两个需要注意的点:

第一,第二步发送的应该是延迟消息而非立即删除。这是为了给数据库主从复制留出同步时间------如果主库刚写完、从库还没同步,此时删缓存后读请求打到从库,读到的可能还是旧值,缓存又会被旧数据回填。

第二,延迟双删并不能做到强一致,两次删除之间仍然存在一个短暂的不一致窗口。如果业务对一致性要求不高(比如缓存热点商品数据、文章浏览量等),这种方案完全够用,且实现成本低、性能影响小。

三、读写锁

如果对数据一致性有严格要求,延迟双删的短暂不一致窗口是不可接受的,这时可以考虑读写锁方案。

Redis 提供了两种锁的语义:

  • 读锁(共享锁):多个线程可以同时持有读锁进行读操作,但读锁存在时,写操作必须等待。
  • 写锁(排他锁):一个线程持有写锁后,其他线程的读操作和写操作全部阻塞。

使用方式:

  • 读数据时加读锁,从缓存读取;缓存未命中则加写锁,从数据库加载并回填缓存。
  • 写数据时加写锁,先更新数据库,再删除(或更新)缓存。

读写锁能够完全保证数据一致性,因为同一时刻要么只有读操作在并发执行,要么只有一个写操作在执行,从根本上消除了竞态条件。

代价是性能------写操作会阻塞所有读写请求,在读多写少的场景下影响尤为明显。因此,读写锁适合对一致性要求极高的业务,比如优惠券计算、订单交易、库存扣减等场景。

四、方案对比

延迟双删 读写锁
一致性 最终一致,存在短暂窗口 强一致
性能 影响小,异步删除 写操作阻塞读写,性能下降明显
实现复杂度 中等(需引入 MQ 或 Canal) 较低(Redis 原生支持)
适用场景 热点数据、一致性要求不高的读多场景 交易、库存等强一致性场景

实际选型时,不需要非此即彼。同一个系统中,对不同业务模块可以根据一致性要求分别采用不同策略------大部分读场景用延迟双删,关键写场景用读写锁,这样能在一致性和性能之间取得较好的平衡。

相关推荐
ACP广源盛1392462567318 小时前
Qwen3.8‑2.4T 开源落地@ACP#国产 MoE 大模型私有化部署中 MST 多屏转换芯片 GSV2231 硬件价值与应用场景
大数据·数据库·人工智能·嵌入式硬件
java_logo18 小时前
Docker 部署 openGauss:轻松搭建企业级开源关系型数据库平台
数据库·docker·开源·opengauss·轩辕镜像·opengauss部署教程·opengauss部署文档
旺仔学长 哈哈18 小时前
52486|高校专业评估不再靠表格:Spring Boot 本科专业质量评估管理系统实战
数据库·spring boot·毕业设计·教育管理
梦想的旅途219 小时前
企业微信Webhook回调怎么做自动回复和AI客服
数据库·机器人·自动化·企业微信·rpa
Zguigo19 小时前
OCR 核心原理 + 模型 + 检测与识别
数据库·ocr
凤山老林19 小时前
零停机数据库演进:Spring Boot 集成 Flyway 与平滑DDL变更策略
数据库·spring boot·后端
曹牧19 小时前
Oracle:取固定分隔符字符串中第一个元素
数据库·oracle
szarron19 小时前
VNA6 便携式矢量网络分析仪:从入门到精通
数据库·嵌入式硬件·测试工具·5g·射频工程
临床数据科学和人工智能兴趣组20 小时前
R语言中,列表是一种非常灵活的数据结构,它可以存储不同类型的对象,如向量、矩阵、数据框、甚至其他列表
数据结构·数据库·r语言·r语言-4.2.1·临床试验
cmes_love20 小时前
期货五档Level2行情数据和分钟历史行情数据下载分析
数据库