news 2026/7/3 2:14:59

缓存一致性实践:删除缓存不是银弹

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
缓存一致性实践:删除缓存不是银弹

缓存一致性实践:删除缓存不是银弹

一、缓存问题的本质是读写时序

缓存能显著降低数据库压力,但一致性问题也会随之出现。最常见的方案是“更新数据库后删除缓存”,它比直接更新缓存更稳妥,但并不是银弹。并发读写、删除失败、主从延迟、事务未提交、热点 key 重建,都可能导致脏数据或缓存击穿。

讨论缓存一致性时,应先明确业务容忍度。账户余额、库存、订单状态对一致性要求高;商品详情、文章统计、推荐列表可以接受短时间延迟。不同场景应该采用不同策略,而不是把所有缓存都套进同一个模板。

二、典型链路:数据库和缓存不是一个事务

sequenceDiagram participant U as 用户请求 participant S as Java 服务 participant C as Redis participant D as Database U->>S: 更新数据 S->>D: 提交事务 S->>C: 删除缓存 U->>S: 查询数据 S->>C: 未命中 S->>D: 读取新值 S->>C: 回填缓存

这个顺序看起来合理,但有几个细节需要注意。删除缓存必须发生在数据库事务提交之后,否则另一个线程可能在事务未提交时读到旧数据并回填缓存。删除失败要有重试或补偿,否则旧缓存会持续存在。高并发热点 key 回填时,要避免大量请求同时打到数据库。

还要考虑主从延迟。如果写入主库后,读请求从从库读取,再回填缓存,就可能把旧值放回 Redis。此时需要对关键读请求短时间走主库,或者使用版本号校验,避免旧数据覆盖新缓存。

三、代码实现:用事务后事件触发缓存删除

下面示例展示一种较稳妥的做法:业务事务提交后,再发布缓存删除动作。

@Transactional public void updateProduct(ProductUpdateCommand command) { Product product = productRepository.findById(command.id()) .orElseThrow(() -> new IllegalArgumentException("product not found")); product.changePrice(command.price()); productRepository.save(product); eventPublisher.publishEvent(new ProductChangedEvent(command.id())); } @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void onProductChanged(ProductChangedEvent event) { cacheService.delete("product:" + event.productId()); }

如果删除缓存失败,不要只打印日志。可以把失败事件写入可靠消息队列或本地补偿表,由后台任务重试。对于高价值数据,还可以加入版本号:数据库记录带version,缓存值也带version,回填前比较版本,避免旧查询覆盖新值。

热点 key 要配合互斥重建或逻辑过期。缓存失效瞬间,如果大量请求同时回源数据库,会形成击穿。可以用 Redis 分布式锁控制单线程回填,其余请求短暂等待或返回旧值。逻辑过期适合读多写少场景,用旧值换稳定性。

四、工程取舍:一致性、性能和复杂度要分层

强一致缓存通常成本很高,甚至违背缓存的初衷。大多数业务需要的是“可解释的最终一致”。例如商品价格更新后 1 秒内刷新可以接受,库存扣减则不能依赖缓存作为最终依据。把数据按一致性等级分层,才能避免过度设计。

监控也很重要。缓存命中率、删除失败次数、回源耗时、热点 key、数据库 QPS 和脏读投诉都应该被观察。没有监控时,缓存一致性问题往往只在用户反馈或对账中暴露,定位成本很高。

最后要给缓存 key 制定规范。包含业务前缀、对象 ID、版本或租户信息,避免不同模块误删、覆盖或复用同一个 key。缓存不是散落在代码里的小优化,而是系统架构的一部分。

缓存预热和缓存雪崩也需要提前规划。预热应在服务启动或扩容时主动加载高频数据,避免冷启动瞬间大量请求击穿到数据库。雪崩则来自批量 key 同时失效,可以通过随机过期时间、热点 key 永不过期配合后台刷新、或熔断机制来缓解。对于核心链路,建议准备本地缓存作兜底,在 Redis 不可用或大量 key 失效时提供降级能力。

五、总结

缓存一致性要围绕读写时序设计,更新数据库后删除缓存只是基础方案。事务后删除、失败补偿、版本校验、热点重建和一致性分层,才是生产环境更完整的答案。把业务容忍度说清楚,缓存策略才不会走偏。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/3 2:11:14

网易智企IM Web体验馆:一站式在线体验即时通讯

当 AI 真正走进通信场景之后,即时通讯早已不再是一个工具,而正在变成企业经营客户、运营社区、协同业务的底层操作系统。而当这个能力越来越成为应用标配,开发者和企业在选型时往往面临一个共性难题—— "光看文档不够直观,接…

作者头像 李华
网站建设 2026/7/3 2:06:53

LeetCode 高频题:双指针不是模板,是单调关系

LeetCode 高频题:双指针不是模板,是单调关系 一、别把双指针背成口诀 双指针是高频技巧,但很多人只会背“左右指针往中间走”“快慢指针找环”。背模板能做几道题,遇到变形就懵。双指针真正成立的前提,是问题里存在某种…

作者头像 李华
网站建设 2026/7/3 2:06:15

C# Playwright自动化模板:开箱即用的Web测试与RPA解决方案

1. 项目概述:为什么需要一个“开箱即用”的自动化模板?如果你是一名C#开发者,最近正在研究UI自动化,那么“Playwright”这个名字你一定不陌生。它是由微软官方出品的一款现代化、跨浏览器、跨平台的Web自动化与测试框架。相比于老…

作者头像 李华
网站建设 2026/7/3 2:06:04

Node.js高并发原理与RESTful API实战指南

1. 为什么Node.js值得你投入时间?第一次接触Node.js是在2013年,当时为了快速搭建一个实时聊天应用。传统方案需要ApachePHPMySQL的完整环境,而Node.js仅用100行代码就实现了相同功能。这种效率上的震撼,让我彻底成为了Node.js的信…

作者头像 李华
网站建设 2026/7/3 1:59:10

内蒙古本地实体企业线上获客指南:GEO + 官网 + 短视频组合打法

对于内蒙古的实体企业,尤其是制造业工厂、商贸经销商、本地服务商而言,客源高度集中在本地及周边盟市,传统线下拓客成本越来越高,线上获客又常常找不到精准方向,泛流量多、意向客户少。针对本地实体企业的特性&#xf…

作者头像 李华
网站建设 2026/7/3 1:55:18

医养智伴APP的设计与开发

医养智伴是一款面向中老年群体的智慧医养健康管理平台,采用 Spring Boot Vue3 uni-app 前后端分离架构开发。系统包含微信小程序用户端、Web 用户端及后台管理系统,支持管理员与医生双角色。核心功能涵盖用药提醒、药品商城、在线图文问诊、电子处方、…

作者头像 李华