Redis 缓存更新策略:原理、实现与生产实践
缓存更新是 Redis 作为缓存中间件的核心应用环节,其策略选择直接影响系统性能 、数据一致性 和服务稳定性 。在实际业务中,缓存与数据库的双存储架构下,如何合理更新缓存、避免脏数据、减少缓存穿透 / 击穿 / 雪崩等问题,是后端开发的重点和难点。本文将从缓存更新的核心原则出发,详细解析业界主流的 6 种缓存更新策略,包括原理、实现方式、优缺点、适用场景,并结合生产环境给出选型建议和避坑指南,同时梳理缓存更新与数据一致性的核心解决方案。
一、缓存更新的核心前提与原则
在分析具体策略前,需明确缓存更新的底层逻辑和设计原则,这是所有策略选型的基础。
1. 核心前提:缓存与数据库的双存储模型
日常业务中,Redis 缓存通常作为数据库(MySQL/PostgreSQL 等)的读缓存存在,数据流转的核心逻辑为:
- 读请求:先查缓存,缓存命中则直接返回;缓存未命中则查数据库,再将数据写入缓存后返回。
- 写请求 :需同时处理数据库和缓存,更新策略的本质就是定义写请求中数据库与缓存的操作顺序和方式。
2. 三大核心设计原则
- 数据一致性优先:核心业务(如交易、支付、库存)需严格保证缓存与数据库的数据一致,非核心业务(如资讯、列表)可容忍短暂的数据不一致,优先保证性能。
- 性能最优:尽量减少缓存和数据库的 IO 操作,避免不必要的网络请求和锁竞争,降低服务响应时间。
- 容错性强:策略需考虑异常场景(如服务宕机、网络超时、并发写),避免因单点异常导致脏数据无限扩散,同时尽可能降低缓存问题(穿透 / 击穿 / 雪崩)的发生概率。
3. 两个关键概念
- 强一致性:缓存数据与数据库数据实时保持一致,任意时刻读请求获取的都是最新数据,适用于核心交易场景。
- 最终一致性 :缓存数据与数据库数据在短时间内可能不一致,但经过一定时间后会自动同步为最新数据,适用于非核心读多写少场景,兼顾性能和一致性。
二、业界主流 6 种缓存更新策略全解析
业界主流的缓存更新策略可分为主动更新 和被动更新 两大类,其中主动更新包含更新数据库后更新缓存 、删除数据库后删除缓存 、更新数据库后淘汰缓存 等,被动更新主要为缓存过期自动淘汰 ,同时衍生出读写穿透 和写回(缓存落地) 两种进阶策略。以下按使用频率 和生产实用性排序,逐一解析各策略的核心细节。
策略 1:缓存过期自动淘汰(被动更新)
核心思想 :不主动执行缓存的更新 / 删除操作,仅为缓存键设置合理的过期时间 ,当缓存过期后,由读请求触发缓存回源 (查数据库 + 写缓存),实现缓存的被动更新。这是最简单、使用最广泛的缓存更新策略,也是大部分非核心业务的首选。
核心实现
- 写请求:只更新数据库,不做任何缓存操作,缓存保持原有数据或自然过期。
- 读请求:遵循「缓存优先」原则,缓存命中则返回;未命中则查数据库,将最新数据写入 Redis 并设置过期时间后返回。
- 过期配置:根据业务数据的更新频率 设置过期时间,如资讯数据设置 1 小时,商品列表设置 10 分钟,同时建议增加随机过期时间 (如
EX 600 + random(0, 120)),避免大量缓存同时过期导致缓存雪崩。
优缺点分析
✅ 优点
- 实现极简:写请求只需操作数据库,无需关注缓存,降低开发复杂度和代码侵入性。
- 性能最优:写请求减少一次 Redis 操作,降低服务响应时间和 Redis 压力。
- 避免并发脏数据:无需处理并发写场景下的缓存覆盖问题,天然规避并发更新导致的脏数据。
- 容错性高:即使写请求失败,仅数据库未更新,缓存无影响,不会出现缓存与数据库的反向不一致。
❌ 缺点
- 短暂数据不一致 :写请求更新数据库后,缓存未及时更新,此时读请求会获取到缓存中的旧数据,直到缓存过期,属于最终一致性策略。
- 缓存击穿风险:热点键过期的瞬间,大量读请求会同时穿透到数据库,造成数据库瞬时压力飙升。
- 缓存回源开销:缓存过期后,首次读请求需要查数据库 + 写缓存,响应时间比缓存命中略长。
适用场景
绝大多数非核心业务的读多写少场景 ,如:资讯详情、商品列表、用户信息(非实时)、文章评论数(非精准)等,是生产环境中性价比最高的选型,推荐优先使用。
生产优化
- 针对热点键击穿:结合互斥锁(分布式锁) 实现缓存回源的单线程化,避免大量请求穿透到数据库。
- 针对大量缓存同时过期:增加随机过期时间,打散缓存过期节点。
- 针对核心数据的短暂不一致:可结合定时任务主动刷新热点缓存,减少过期回源的概率。
策略 2:更新数据库后淘汰缓存(Cache Aside)
核心思想 :写请求时,先更新数据库,再删除对应的缓存键 ;读请求遵循「缓存优先,未命中则回源写缓存」,是兼顾一致性和性能的经典主动更新策略,也被称为「旁路缓存策略」,是核心业务读多写少场景的主流选择。
核心实现
- 写请求流程 :
更新数据库 → 删除缓存键(若缓存键不存在,删除操作无副作用)。 - 读请求流程 :
查缓存 → 命中则返回 → 未命中则查数据库 → 写缓存(设置过期时间)→ 返回。 - 核心逻辑 :删除缓存而非更新缓存,避免并发写场景下的缓存覆盖问题,这是该策略的设计精髓。
关键问题:为何是「删缓存」而非「更缓存」?
这是缓存更新的核心避坑点 ,若将策略改为「更新数据库后更新缓存」,在并发写请求 下会产生脏数据,举例说明:
-
场景:两个并发写请求 A 和 B,操作同一数据 ID。
- 写请求 A:更新数据库为数据 A;
- 写请求 B:更新数据库为数据 B(因网络 / 线程调度,B 的数据库更新比 A 快);
- 写请求 B:更新缓存为数据 B;
- 写请求 A:更新缓存为数据 A;
-
结果:数据库最终是数据 B,缓存是数据 A,出现脏数据,且该脏数据会一直存在直到缓存过期,无法自动恢复。
而删除缓存则不会出现此问题:无论并发写的顺序如何,最终都是删除缓存,后续读请求会回源数据库获取最新数据,写入缓存后保证数据一致。
优缺点分析
✅ 优点
- 数据一致性更好:相比缓存过期策略,能快速让旧缓存失效,大幅缩短数据不一致的窗口(仅存在于「数据库更新完成」到「缓存删除完成」的极短时间)。
- 规避并发脏数据:删除缓存而非更新缓存,从根本上解决了并发写的缓存覆盖问题。
- 实现简单:仅在写请求中增加一步缓存删除操作,开发成本低,易落地。
❌ 缺点
- 存在极短时间的脏数据 :若在「数据库更新完成」和「缓存删除完成」之间,有读请求命中缓存,会获取到旧数据,属于最终一致性(不一致窗口通常在毫秒级,可接受)。
- 缓存删除失败风险:若数据库更新成功后,服务宕机 / 网络超时导致缓存删除失败,会造成缓存中旧数据长期存在,形成脏数据。
- 读请求回源略增:每次写请求都会删除缓存,后续首次读请求需要回源数据库,比缓存过期策略的回源频率略高。
适用场景
核心业务的读多写少场景 ,如:商品详情(库存 / 价格)、订单基础信息、用户余额(非实时交易)等,要求数据一致性高、可容忍毫秒级的短暂不一致,是生产环境中核心业务的首选策略。
生产优化
-
解决缓存删除失败:
- 方式 1:异步重试:通过消息队列(RocketMQ/Kafka)或本地消息表,将缓存删除操作异步化,若删除失败则定时重试,保证最终删除成功。
- 方式 2:缓存设置过期时间:为缓存键增加兜底过期时间,即使删除失败,缓存也会自然过期,避免脏数据永久存在。
-
解决极短时间的脏数据 :若业务要求极致一致性,可在写请求时增加分布式锁,保证同一数据的写请求串行执行,缩小不一致窗口。
策略 3:先删除缓存,再更新数据库
核心思想 :写请求时,先删除缓存键,再更新数据库 ;读请求仍遵循「缓存优先,未命中则回源写缓存」,是一种尝试追求「强一致性」的主动更新策略,但存在严重的并发脏数据问题 ,生产环境中不推荐直接使用。
核心实现
- 写请求流程 :
删除缓存 → 更新数据库。 - 读请求流程:与其他策略一致,缓存未命中则查数据库并写缓存。
核心问题:并发读 + 写导致的脏数据
这是该策略的致命缺陷,在并发读和写请求下,会产生长期脏数据,举例说明:
-
场景:写请求 A 更新数据 ID,读请求 B 查询同一数据 ID,二者并发执行。
- 写请求 A:删除缓存键;
- 读请求 B:缓存未命中,查数据库(此时数据库尚未被 A 更新,获取到旧数据);
- 写请求 A:更新数据库为新数据;
- 读请求 B:将旧数据写入缓存;
-
结果:数据库是新数据,缓存是旧数据,形成脏数据 ,且该脏数据会一直存在直到缓存过期或被再次删除,不一致窗口可能很长。
优缺点分析
✅ 优点:理论上若无并发请求,能保证缓存与数据库的强一致性,读请求不会获取到旧数据。
❌ 缺点
- 并发脏数据必现:在高并发读 + 写场景下,脏数据问题无法避免,这是最致命的缺陷。
- 实现无优势:相比「更新数据库后删缓存」,实现复杂度相当,但一致性更差。
- 缓存回源频率高:先删缓存会导致后续读请求必然回源,增加数据库压力。
适用场景
无并发读的纯写场景 (几乎不存在),或低并发、对一致性要求极高且能接受数据库压力 的小众场景,生产环境中严禁直接使用。
补救方案(仅作了解,不推荐落地)
可通过分布式锁 将同一数据的读请求和写请求串行化,即写请求加锁后,读请求必须等待写请求完成才能执行,彻底避免并发问题。但此方案会导致读请求阻塞,大幅降低系统并发性能,违背缓存的设计初衷,性价比极低。
策略 4:更新数据库后更新缓存(不推荐)
核心思想 :写请求时,先更新数据库,再更新缓存 ;读请求遵循「缓存优先」,是最直观的缓存更新策略,但存在严重的并发缓存覆盖问题 ,生产环境中几乎不使用。
核心实现
- 写请求流程 :
更新数据库 → 更新缓存。 - 读请求流程:与其他策略一致。
核心问题:并发写的缓存覆盖(前文已提及,此处再强化)
多个并发写请求会因执行顺序问题,导致数据库是最新数据,缓存是旧数据 ,且脏数据会长期存在,举例见「策略 2」的关键问题部分,此问题无低成本的有效解决方案。
优缺点分析
✅ 优点:实现最直观,开发门槛最低,读请求缓存命中率极高,几乎无回源开销。
❌ 缺点
- 并发脏数据必现:高并发写场景下脏数据问题无法规避,是致命缺陷。
- 缓存更新冗余:若某数据频繁更新,会导致缓存被频繁覆盖,增加 Redis 的写压力,而这些更新后的缓存可能并未被读请求访问,造成资源浪费。
- 大 value 更新开销大:若缓存值是大对象(如复杂的商品详情),每次更新缓存的网络 IO 和 Redis 序列化开销较大。
适用场景
无并发写的单线程场景 (如后台管理系统的低频写操作),生产环境的高并发业务严禁使用。
策略 5:读写穿透(Read/Write Through)
核心思想 :将缓存作为数据访问的唯一入口 ,业务层不直接操作数据库 ,所有读 / 写请求都通过缓存层转发,由缓存层负责与数据库的交互和数据更新,属于缓存驱动数据的高级策略,也被称为「缓存作为主存储」。
核心实现
该策略将缓存层分为缓存命中 和缓存未命中两种情况处理,全程由缓存层主导:
- 读请求(Read Through) :业务层查缓存 → 缓存命中则直接返回 → 缓存未命中则缓存层主动查数据库,将数据写入缓存后再返回给业务层。
- 写请求(Write Through) :业务层更新缓存 → 缓存层先更新自身数据 ,再同步更新数据库 → 数据库更新成功后,缓存层返回成功给业务层。
关键特性
- 业务层完全屏蔽数据库,仅与缓存交互,降低业务层的开发复杂度。
- 缓存层与数据库的交互是同步 的,保证数据的强一致性。
- 缓存中的数据都是有效数据,无过期键(可按需设置兜底过期),避免缓存击穿 / 穿透。
优缺点分析
✅ 优点
- 强数据一致性:缓存层同步更新数据库,缓存与数据库实时保持一致,适用于核心交易场景。
- 业务层极简:业务层无需关注缓存和数据库的双存储逻辑,只需操作缓存,降低代码耦合。
- 缓存管理更规范:由缓存层统一负责数据的读写和更新,避免业务层随意操作导致的数据不一致。
❌ 缺点
- 实现复杂度高:需要自定义开发缓存层的转发逻辑,封装数据库的读写操作,对技术架构要求高。
- 性能略有损耗:写请求需要经过缓存层→数据库的同步操作,比直接操作数据库多一层转发,响应时间略长。
- 缓存层成为单点:缓存层是数据访问的唯一入口,其宕机会导致整个服务不可用,需要做高可用集群部署。
- Redis 原生不支持:Redis 本身是纯缓存,无原生的读写穿透实现,需要基于 Redis 二次开发或使用专业的缓存中间件(如 Memcached + 插件)。
适用场景
中大型分布式系统的核心业务,如:金融交易、电商支付、核心库存管理等,要求强一致性且希望简化业务层逻辑,适合有成熟架构团队的场景。
策略 6:写回(Write Back,也叫缓存落地)
核心思想 :与读写穿透类似,将缓存作为数据访问的唯一入口 ,但写请求时,缓存层先更新自身数据 ,异步更新数据库 ,数据库仅作为数据持久化的兜底存储 ,属于高性能优先的高级缓存策略,也被称为「延迟持久化」。
核心实现
全程由缓存层主导,核心差异在写请求的异步化:
- 读请求:与读写穿透完全一致,缓存未命中则由缓存层回源数据库并写入缓存。
- 写请求 :业务层更新缓存 → 缓存层立即更新自身数据并返回成功 给业务层 → 缓存层在合适的时机异步更新数据库(如缓存数据被淘汰时、定时批量更新、达到指定更新阈值时)。
关键特性
- 写请求是异步非阻塞 的,响应速度极快,是所有策略中性能最高的。
- 缓存层是主存储 ,数据库是从存储,数据最终会通过异步操作落地到数据库,保证数据持久化。
- 可设置批量更新策略,将多个小的写请求合并为一个数据库批量操作,降低数据库的 IO 压力。
优缺点分析
✅ 优点
- 性能极致:写请求异步化,无需等待数据库更新,响应时间达到毫秒级甚至微秒级,适用于高并发写场景。
- 降低数据库压力:通过批量更新、延迟更新,减少数据库的单次写操作,提升数据库的处理能力。
- 业务层极简:与读写穿透一致,业务层仅操作缓存,屏蔽数据库细节。
❌ 缺点
- 数据丢失风险 :若缓存层宕机,尚未异步落地到数据库的缓存数据会永久丢失,属于性能换一致性的策略。
- 实现复杂度极高:需要开发缓存层的异步更新队列、批量更新、故障恢复(如宕机后的数据恢复)等逻辑,对技术架构和运维能力要求极高。
- 强一致性无法保证 :异步更新导致数据库与缓存存在一定时间的不一致,直到数据落地。
适用场景
超高并发写的非核心业务,如:短视频点赞、评论数、文章阅读量、用户行为统计等,能容忍短暂的数据丢失,优先保证高并发和高性能,适合大型互联网公司的高流量场景。
三、缓存更新策略的生产选型指南
以上 6 种策略各有优劣,无绝对的 "最优策略",选型的核心是结合业务场景的「数据一致性要求」「读写并发量」「开发 / 架构成本」 进行综合判断。以下是生产环境的通用选型优先级 和场景化选型建议,覆盖 99% 的后端业务场景。
1. 通用选型优先级(从高到低)
缓存过期自动淘汰 → 更新数据库后淘汰缓存 → 读写穿透 → 写回 → 先删缓存再更数据库 / 更新数据库后更缓存(后两者几乎不选)
- 小公司 / 快速迭代业务:优先选缓存过期自动淘汰,开发快、问题少、性价比高。
- 中大型公司 / 核心业务:优先选更新数据库后淘汰缓存,兼顾一致性和性能,易落地。
- 大型分布式系统 / 极致一致性要求:选读写穿透,需配套成熟的架构设计。
- 超高并发写 / 非核心统计业务:选写回,需做好数据丢失的兜底方案。
2. 场景化精准选型
| 业务场景 | 一致性要求 | 读写特征 | 推荐策略 | 核心原因 |
|---|---|---|---|---|
| 资讯 / 文章 / 商品列表 | 最终一致性(容忍分钟级不一致) | 读多写极少 | 缓存过期自动淘汰 | 实现简单,性能最优,无需关注缓存更新 |
| 商品详情 / 订单基础信息 / 用户余额 | 最终一致性(容忍毫秒级不一致) | 读多写少 | 更新数据库后淘汰缓存 | 一致性好,规避并发脏数据,易落地 |
| 金融交易 / 电商支付 / 核心库存 | 强一致性 | 读写均有,并发中等 | 更新数据库后淘汰缓存 + 分布式锁 / 读写穿透 | 保证数据实时一致,核心数据零误差 |
| 短视频点赞 / 评论数 / 行为统计 | 最终一致性(容忍秒级不一致) | 超高并发写,读多 | 写回 + 缓存过期兜底 | 异步写性能极致,适配高并发,统计数据可容忍短暂不一致 |
| 后台管理系统 / 低频写操作 | 强一致性 | 低并发,读写少 | 更新数据库后更新缓存 | 无并发问题,实现直观,开发效率高 |
四、缓存更新的核心避坑与生产优化
无论选择哪种缓存更新策略,都需要关注数据一致性保障 和缓存问题规避,以下是生产环境中最常遇到的坑和对应的通用优化方案,适用于所有策略。
1. 核心避坑:解决缓存删除失败导致的脏数据
该问题主要出现在「更新数据库后淘汰缓存」策略中,数据库更新成功但缓存删除失败,是生产环境中脏数据的主要来源之一,必须做兜底处理,推荐两种方案:
方案 1:本地消息表 + 定时重试(适合中小型系统)
- 写请求更新数据库后,将缓存删除任务写入本地数据库的消息表(记录缓存键、操作类型、状态);
- 启动定时任务,轮询消息表中的未完成任务,尝试删除缓存;
- 缓存删除成功则更新消息表状态为完成,删除失败则继续重试,直到达到最大重试次数(如 3 次);
- 对最大重试次数仍失败的任务,进行人工告警,由运维介入处理。
方案 2:消息队列异步重试(适合中大型分布式系统)
- 写请求更新数据库后,向消息队列(RocketMQ/Kafka)发送缓存删除消息;
- 消费端监听消息队列,接收消息后执行缓存删除操作;
- 若消费端删除失败,消息队列会自动重试(可配置重试次数和间隔),保证最终删除成功;
- 结合死信队列,处理多次重试仍失败的消息,避免消息堆积。
通用兜底 :无论采用哪种重试方案,都要为缓存键设置过期时间,即使重试失败,缓存也会自然过期,避免脏数据永久存在。
2. 通用优化:规避缓存三大问题(穿透 / 击穿 / 雪崩)
缓存更新策略的执行过程中,极易引发缓存穿透、击穿、雪崩,需结合策略做针对性优化,这是缓存使用的必做功课:
-
缓存穿透(请求不存在的数据,穿透到数据库):
- 方案:布隆过滤器过滤不存在的键 + 缓存空值(设置短过期时间,如 5 分钟)。
-
缓存击穿(热点键过期 / 被删除,大量读请求穿透到数据库):
- 方案:热点键永不过期 + 分布式锁(互斥锁)实现缓存回源的单线程化。
-
缓存雪崩(大量缓存同时过期 / 被删除,数据库被压垮):
- 方案:为缓存键增加随机过期时间 + 缓存集群高可用(主从 + 哨兵 / 集群) + 数据库做限流 / 降级 / 熔断。
3. 性能优化:减少 Redis 的无效操作
- 批量操作 :对多个缓存键的删除 / 更新,使用 Redis 的批量命令(如
DEL key1 key2),减少网络请求。 - 异步删除大键 :Redis 4.0 + 开启
lazyfree-lazy-del yes,异步删除 Hash/List/Set 等大键,避免阻塞 Redis 主线程。 - 减少缓存更新冗余:对频繁更新的数据,避免频繁删除 / 更新缓存,优先使用缓存过期策略,降低 Redis 压力。
4. 一致性优化:缩小数据不一致窗口
对于「更新数据库后淘汰缓存」策略,其不一致窗口存在于「数据库更新完成」到「缓存删除完成」的极短时间,可通过以下方式进一步缩小:
- 串行化写请求:对同一数据的写请求加分布式锁,保证串行执行,避免并发写导致的删除顺序问题。
- Redis 删除操作前置:在数据库更新前,先发送缓存删除的异步请求,再执行数据库更新,减少删除操作的延迟。
- 监控缓存操作 :通过 Redis 的
INFO stats监控keyspace_hits(缓存命中数)、keyspace_misses(缓存未命中数),及时发现数据不一致问题。
五、缓存更新与数据一致性的核心总结
缓存更新的本质,是在「性能」和「数据一致性」之间做权衡,业界没有 "鱼和熊掌兼得" 的完美策略,所有选择都是基于业务场景的取舍:
- 最终一致性 是大部分业务的最优选择 :除金融、支付、核心库存等强一致性场景外,90% 以上的业务都可容忍短暂的数据不一致,优先选择 缓存过期自动淘汰或更新数据库后淘汰缓存**,兼顾性能和开发效率。
- 删除缓存是比更新缓存更安全的选择 :并发写场景下,更新缓存必然导致脏数据,而删除缓存能从根本上规避该问题,这是缓存更新的核心设计技巧。
- 异常容错是缓存更新的必备环节 :任何策略都要考虑操作失败的场景(如网络超时、服务宕机),通过重试机制 和过期兜底,避免脏数据无限扩散。
- 缓存不是银弹 :缓存更新策略需与服务限流、降级、熔断 ,数据库主从、分库分表等架构方案结合,才能保证整个系统的稳定性。
六、全文总结
Redis 缓存更新策略是后端开发的核心知识点,其选型直接决定系统的性能和稳定性。本文详细解析的 6 种策略中,缓存过期自动淘汰 和更新数据库后淘汰缓存是生产环境的主流选择,覆盖了绝大多数业务场景;读写穿透和写回属于高级策略,适合有成熟架构团队的中大型分布式系统;而先删缓存再更数据库、更新数据库后更缓存因存在严重的并发脏数据问题,几乎不推荐使用。
在实际开发中,无需拘泥于固定策略,应根据业务的「一致性要求」「读写并发量」和「开发成本」做灵活组合 ,例如:核心热点数据采用「更新数据库后淘汰缓存 + 永不过期」,非核心数据采用「缓存过期自动淘汰」,统计数据采用「写回」。同时,必须做好异常容错 和缓存问题规避,通过重试机制、过期兜底、分布式锁等方案,让缓存更新策略更健壮、更适配生产环境的复杂场景。
缓存的核心价值是提升系统性能,而合理的缓存更新策略,是让这份价值发挥到极致的关键。