Redis删除缓存失败的原因和解决方案

 更新时间:2026年04月30日 08:52:01   作者:014-code  
本文讨论了数据库操作成功但缓存删除失败时,线上环境下的应对策略,提出了一种更稳定的方法,包括主流程写库后再删除缓存、删除失败进入重试队列、超过重试上限进入死信队列并触发告警和补偿等措施,需要的朋友可以参考下

这篇聊一个很现实的问题:数据库已经改成功了,但缓存删除失败了,线上怎么办?

先给答案

如果你项目里只有一句 redis.del(key),那一致性是靠运气。

一套更稳的做法是:

  1. 主流程里先写库再删缓存
  2. 删除失败立刻进入重试队列
  3. 超过重试上限进入死信队列
  4. 死信触发告警和人工/自动补偿
  5. 全链路打点,能看见“删失败率”和“补偿成功率”

一句话:删除缓存不是一个动作,而是一条可观测、可补偿的链路。

为什么“删缓存失败”必须单独设计

很多同学会说:“删失败就下次再读库呗。”

这句话在低并发时看起来没毛病,但线上高峰期会出事:

  • 热点 key 还在,用户继续读到旧值
  • 读流量越大,旧值传播越快
  • 你又没有补偿机制,脏数据会“活很久”

最麻烦的是:这个问题不会立刻报错,而是以“偶发投诉”“数据不对”的形式出现,排查成本很高。

一个真实可落地的链路

你会发现,核心不是“怎么删”,而是“删不掉时怎么兜”。

代码示例:主流程 + 异步重试

1. 主流程(写库后删缓存)

@Service
public class ProductService {
    @Resource
    private ProductMapper productMapper;
    @Resource
    private StringRedisTemplate redisTemplate;
    @Resource
    private CacheDeleteProducer cacheDeleteProducer;
    @Transactional(rollbackFor = Exception.class)
    public void updateProduct(Product product) {
        String key = "product:" + product.getId();
        // 1) 数据库是事实来源
        productMapper.updateById(product);
        // 2) 主流程删缓存,失败则入重试队列
        try {
            redisTemplate.delete(key);
        } catch (Exception ex) {
            cacheDeleteProducer.sendDeleteEvent(key, 1);
        }
    }
}

2. 重试消费者(指数退避 + 最大次数)

@Component
public class CacheDeleteConsumer {
    private static final int MAX_RETRY = 5;
    @Resource
    private StringRedisTemplate redisTemplate;
    @Resource
    private CacheDeleteProducer cacheDeleteProducer;
    @Resource
    private DeadLetterProducer deadLetterProducer;
    public void onMessage(CacheDeleteEvent event) {
        try {
            redisTemplate.delete(event.getCacheKey());
            // 打点:delete_success_total +1
        } catch (Exception ex) {
            int nextRetry = event.getRetryCount() + 1;
            if (nextRetry > MAX_RETRY) {
                deadLetterProducer.send(event.getCacheKey(), ex.getMessage());
                return;
            }
            long delaySeconds = (long) Math.pow(2, nextRetry); // 2,4,8,16,32
            cacheDeleteProducer.sendDeleteEvent(event.getCacheKey(), nextRetry, delaySeconds);
        }
    }
}

3. 死信补偿任务(定时巡检)

@Component
public class CacheDeleteCompensationJob {
    @Resource
    private DeadLetterRepository deadLetterRepository;
    @Resource
    private StringRedisTemplate redisTemplate;
    // 每 5 分钟跑一次
    @Scheduled(cron = "0 */5 * * * ?")
    public void compensate() {
        List<DeadLetterRecord> records = deadLetterRepository.queryUnresolved(200);
        for (DeadLetterRecord record : records) {
            try {
                redisTemplate.delete(record.getCacheKey());
                deadLetterRepository.markResolved(record.getId());
            } catch (Exception e) {
                deadLetterRepository.increaseFailCount(record.getId(), e.getMessage());
            }
        }
    }
}

这 5 个细节,决定你方案能不能用

幂等性
删缓存天生幂等,删不存在 key 也算成功,别把它当异常。

重试上限
不要无限重试,超过阈值必须死信,不然就是隐性消息堆积。

退避策略
固定 1 秒重试容易打爆 Redis,用指数退避更稳。

死信可见性
死信不等于丢弃,要有告警和处理面板。

链路监控
至少要有这几个指标:

  • cache_delete_fail_total
  • cache_delete_retry_total
  • cache_delete_dlt_total
  • cache_delete_compensation_success_total

常见误区

误区 1:删失败概率很低,可以忽略

线上你总会遇到:网络抖动、Redis 短暂超时、连接池耗尽。
低概率 * 高频请求 = 可观事故数。

误区 2:有延迟双删就够了

延迟双删只能覆盖一部分并发窗口,无法替代失败重试链路

误区 3:死信就是失败,人工看就行

只靠人工盯死信,夜里一定会漏。
最好是“告警 + 自动补偿 + 人工兜底”三层。

选型建议(按团队规模)

团队阶段推荐方案
小团队、单体服务写库后删缓存 + 本地重试(短期)
中型团队、多服务写库后删缓存 + MQ 重试 + 死信告警
大团队、高一致性要求事件驱动一致性 + 死信平台 + 自动补偿任务

最后总结

“删除缓存失败”不是小概率边角料,它是缓存一致性的主战场。

真正能扛线上流量的方案,通常长这样:

  1. 主链路快:写库后删缓存
  2. 失败可恢复:异步重试
  3. 极端可兜底:死信补偿
  4. 整体可观测:指标和告警

把这四件事做到位,你的缓存一致性就不是“玄学”,而是工程能力。

以上就是Redis删除缓存失败的原因和解决方案的详细内容,更多关于Redis删除缓存失败的资料请关注脚本之家其它相关文章!

相关文章

  • Redis字符串String操作详解从基础到高级应用小结

    Redis字符串String操作详解从基础到高级应用小结

    本文全面解析Redis字符串类型,涵盖设置、修改、数字运算、过期处理及分布式锁、计数器等高级应用,提供基础命令与性能优化策略,助开发者高效利用这一核心数据类型实现缓存、统计等功能,感兴趣的朋友一起看看吧
    2025-07-07
  • 基于Redis实现共享Session登录的实现

    基于Redis实现共享Session登录的实现

    本文主要介绍了基于Redis实现共享Session登录的实现,包括发送短信验证码、短信验证码登录和注册、以及登录状态校验的流程,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • Redis 常用命令总结

    Redis 常用命令总结

    本文总结了Redis在开发中常用的基础操作、String(字符串)、Hash(哈希)、List(列表)、Set(集合)、ZSet(有序集合)等等常用应用场景,缓存场景、计数器、分布式锁、排行榜、消息队列、用户标签等,感兴趣的朋友跟随小编一起看看吧
    2026-04-04
  • redis数据结构之压缩列表

    redis数据结构之压缩列表

    这篇文章主要介绍了redis数据结构之压缩列表,压缩列表是列表list和hash数据结构的底层实现之一,是redis为了节约内存而开发的,由一系列特殊编码的连续内存块组成的顺序型数据结构,下面详细内容需要的小伙伴可以参考一下
    2022-03-03
  • redis如何实现清空缓存

    redis如何实现清空缓存

    这篇文章主要介绍了redis如何实现清空缓存,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • redis性能优化之生产中实际遇到的问题及排查总结

    redis性能优化之生产中实际遇到的问题及排查总结

    这篇文章主要介绍了redis性能优化之生产中实际遇到的问题及排查总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 浅谈redis加锁常用几种方式

    浅谈redis加锁常用几种方式

    这篇文章主要介绍了浅谈redis加锁常用几种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Redis哨兵主备切换的数据丢失问题及解决

    Redis哨兵主备切换的数据丢失问题及解决

    主备切换过程中可能会导致数据丢失,异步复制和脑裂是两种主要原因,异步复制可能导致部分数据未复制到slave而master宕机,脑裂则可能导致多个master存在,旧master恢复后数据被清空,从而丢失数据
    2024-12-12
  • redis远程连接不上的解决办法

    redis远程连接不上的解决办法

    本文主要介绍了redis远程连接不上的解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 图文详解Windows下使用Redis缓存工具的方法

    图文详解Windows下使用Redis缓存工具的方法

    这篇文章以图文结合的方式详解Windows下使用Redis缓存工具的方法,感兴趣的小伙伴们可以参考一下
    2015-12-12

最新评论