为什么会出现缓存删除失败的情况

文章目录

      • [1. 物理环境与网络层故障(最常见)](#1. 物理环境与网络层故障(最常见))
      • [2. 应用程序异常崩溃](#2. 应用程序异常崩溃)
      • [3. 业务逻辑与时序冲突](#3. 业务逻辑与时序冲突)
      • [🛠 如何解决删除失败?(解决方案演进)](#🛠 如何解决删除失败?(解决方案演进))
        • [方案 A:消息队列(MQ)重试机制(异步补救)](#方案 A:消息队列(MQ)重试机制(异步补救))
        • [方案 B:订阅 Binlog 异步删除(目前的大厂标杆)](#方案 B:订阅 Binlog 异步删除(目前的大厂标杆))
      • [💡 深度思考:失效即正义](#💡 深度思考:失效即正义)

在分布式系统中,**"先更新数据库,后删除缓存"**虽然是最佳实践,但它并非"原子操作"。从代码逻辑执行到网络传输,每一个环节都存在导致删除失败的风险。

我们可以将缓存删除失败的原因拆解为以下几个维度:

1. 物理环境与网络层故障(最常见)

这是最不可控的因素:

  • 网络抖动: 在数据库更新成功后,应用程序向 Redis 发送 DEL 指令时,恰好发生了网络波动或丢包,导致指令未到达或响应超时。
  • Redis 服务不可用: 此时 Redis 可能恰好因为 OOM(内存溢出)导致进程崩溃、正在进行 RDB 镜像持久化导致瞬间阻塞,或者正在经历主从切换(Failover)。
  • 连接池耗尽: 高并发下,应用程序的 Redis 连接池被占满,无法获取可用连接来执行删除操作。

2. 应用程序异常崩溃

  • 非原子性导致的"半路夭折": 代码执行完 db.update() 后,程序还没来得及执行 redis.delete(),JVM 就因为 OOM 崩溃了,或者所在的 Pod/容器被 K8s 强制重启。
  • 异常处理不当: 程序员在代码中只对 db.update() 做了事务控制,但在更新完数据库后的删除逻辑里没有写完备的 try-catch 或重试逻辑。如果删除时抛出异常且未被捕获,这个"失效"指令就丢失了。

3. 业务逻辑与时序冲突

  • 分布式事务失效: 在微服务环境下,数据库和缓存往往不在同一个事务域内。即使数据库本地事务成功提交,全局事务(如 Seata)或本地后续逻辑的异常也可能干扰删除动作。
  • 并发删除竞争: 极少数情况下,多个实例同时对同一个 Key 进行写操作,由于执行顺序的错乱,早期的删除指令可能覆盖了晚期的删除逻辑(虽然这在"删缓存"策略中影响较小,但在"更新缓存"中是致命的)。

🛠 如何解决删除失败?(解决方案演进)

针对上述失败风险,工业界有两套主流的"补丁"方案:

方案 A:消息队列(MQ)重试机制(异步补救)

既然同步删除可能失败,就利用 MQ 的持久化重试特性。

  1. 更新数据库成功。
  2. 将"删除某个 Key"的消息发送到 MQ(比如 RocketMQ 或 RabbitMQ)。
  3. 消费者从 MQ 消费消息并执行 Redis 删除。
  4. 优点: 如果删除失败,MQ 会自动按时间间隔重试,直到成功为止。
方案 B:订阅 Binlog 异步删除(目前的大厂标杆)

这是目前解决"双写一致性"最优雅的方案,因为它完全解耦了业务代码。

  1. 业务代码: 只管更新数据库,啥也不用管缓存。
  2. 中间件(如 Canal): 伪装成 MySQL 的从库,实时监听 Binlog(数据库变更日志)。
  3. 解析与执行: Canal 监听到数据变动后,提取出 Key,再由专门的消费者去执行 Redis 删除。
  4. 优点: 只要数据库更新成功,Binlog 就一定存在。即使删除进程挂了,重启后依然能从上次的偏移量继续同步。

💡 深度思考:失效即正义

在分布式系统设计中,我们通常追求的是**"最终一致性"**而非"强一致性"。

作为开发者,在处理类似《小哈书》这类高并发内容社区时,你不仅要防范"删除失败",还要配合 "缓存过期时间(TTL)" 进行双重保障。即使所有重试方案都挂了,缓存到期后依然会强制回源数据库,从而拉回正确的数据。

你目前在实现 Redis 缓存时,是否已经给关键业务数据(比如用户信息、帖子详情)设置了合理的随机抖动过期时间?这种设计往往能解决很多由于"意外失败"带来的长效脏数据问题。

相关推荐
三8443 小时前
SSRF 从入门到实战(下篇):gopher 打 Redis 实现 RCE
数据库·redis·缓存
名字还没想好☜4 小时前
Java 用 LinkedHashMap 三行实现 LRU 缓存:accessOrder、removeEldestEntry 与线程安全
java·后端·安全·spring·缓存
手握风云-4 小时前
Redis:不只是缓存那么简单(十六)
缓存
驾驭人生20 小时前
Hangfire.Redis.StackExchange 已归档停更!生产任务卡死、重复执行、网络抖动问题解决方案
数据库·redis·缓存
TDengine (老段)1 天前
TDengine Catalog 与元数据缓存
大数据·数据库·物联网·缓存·时序数据库·tdengine
hui-梦苑1 天前
[Gromacs]核心性能梯度测试:GROMACS 多线程性能调优实验
缓存·性能优化·多线程·gromacs·动力学模拟
Lyyaoo.1 天前
【链表】【中等】两数相加/倒N删除/两个交换/排序链表/LRU缓存
数据结构·链表·缓存
努力努力再努力wz1 天前
【Redis入门系列】:从 RESP 协议到 redis-plus-plus:Redis 客户端编程与 C++ 接口设计
开发语言·数据库·c++·redis·分布式·缓存·架构
OH_TPC2 天前
【鸿蒙优选三方库】@react-native-ohos/react-native-fast-image:高性能图片加载与缓存组件
缓存·华为·harmonyos·鸿蒙
liangshanbo12152 天前
手写 Event Emitter(事件发射器)
缓存