1. 缓存一致性概述
1.1 缓存一致性问题定义
缓存一致性(Cache Consistency)是指在分布式系统中,缓存层与持久化存储层(如数据库)之间数据副本的同步程度。当应用程序同时使用缓存和数据库时,同一份数据可能在不同存储介质中存在多个副本,这些副本之间的值差异即为缓存不一致。
核心定义:缓存一致性 = 缓存中的数据与数据库中的数据在任意时刻保持逻辑等价的程度。完全一致意味着缓存命中返回的值与数据库查询返回的值完全相同;不一致则意味着两者存在偏差。
从一致性强度维度,缓存一致性可分为三个级别:
| 一致性级别 | 描述 | 典型场景 |
|---|---|---|
| 强一致性 | 缓存更新后,所有后续读取立即看到最新值 | 金融交易、库存扣减 |
| 最终一致性 | 允许短暂不一致,但保证经过足够时间后数据一致 | 用户资料、内容推荐 |
| 弱一致性 | 不保证后续读取能看到更新,甚至可能永远看不到 | 日志聚合、非关键统计 |
1.2 为什么需要缓存一致性
引入缓存的核心目的是降低延迟和减轻数据库负载,但缓存与数据库之间的数据偏差会引发一系列业务问题。以下从三个维度说明缓存一致性的必要性:
- 业务正确性受损:用户读取到过期数据,导致展示错误、计费偏差、库存超卖等直接经济损失。
- 用户体验下降:同一用户在不同页面或不同时间看到矛盾的数据(如订单状态反复变化),降低信任度。
- 系统可靠性风险:不一致数据可能触发下游系统的错误逻辑,造成级联故障或数据污染。
经验法则:缓存一致性不是"要不要"的问题,而是"在什么粒度、什么延迟容忍度下达成一致"的问题。业务对一致性的要求直接决定了缓存策略的选择。
不同业务场景对缓存一致性的容忍度差异巨大:
| 业务场景 | 一致性要求 | 可容忍偏差时间 | 推荐策略 |
|---|---|---|---|
| 商品库存 | 强一致 | < 100ms | 缓存同步更新 + 分布式锁 |
| 用户头像 | 最终一致 | 1~5 分钟 | 缓存过期 + 异步刷新 |
| 热门文章阅读数 | 弱一致 | 10~30 分钟 | 定时批量回写 |
| 支付状态 | 强一致 | < 50ms | 缓存穿透 + 实时校验 |
1.3 缓存与数据库不一致的典型场景
在实际生产环境中,缓存不一致主要由并发写入、操作时序错乱和异常中断三类原因引发。以下是六种最常见的典型场景:
- 先更新数据库,后更新缓存(写写冲突):线程A更新数据库为v1,线程B更新数据库为v2,但B先更新缓存为v2,A后更新缓存为v1,导致缓存最终为旧值v1。
- 先删除缓存,后更新数据库(读写冲突):线程A删除缓存后,线程B读取缓存未命中,从数据库加载旧值写入缓存,然后线程A才更新数据库,导致缓存长期为旧值。
- 先更新数据库,后删除缓存(删除失败):数据库更新成功,但删除缓存时网络超时或Redis异常,缓存中残留旧数据。
- 缓存过期与数据库更新交错:缓存刚好过期,请求A从数据库加载旧数据,同时请求B更新数据库为新数据,A将旧数据写回缓存覆盖新值。
- 主从复制延迟:写入主库后缓存已更新,但从库尚未同步,后续读请求从从库获取旧数据并更新缓存。
- 分布式事务失败:缓存更新成功但数据库回滚,或数据库提交成功但缓存更新超时,两端状态不一致。
关键警示:上述"先删缓存 + 读写交错"场景是生产环境中最常见、最难排查的不一致问题。它不需要并发写入,仅需一个读请求在删除缓存之后、数据库更新之前插入,就能造成永久性不一致。这也是为什么业界更推荐"延迟双删"或"先更新数据库后删除缓存"策略的原因。