在电商系统缓存架构中,保障数据一致性是核心挑战之一(缓存与数据库的数据需保持同步,避免出现 “缓存脏数据” 导致业务异常,如商品价格、库存错误)。以下是常见的解决方案和实践思路,需根据业务场景(如实时性要求、并发量)灵活选择:
一、核心原则:明确数据一致性的 “程度”
电商场景中,并非所有数据都需要强一致性(如商品详情可容忍毫秒级延迟,而库存、订单需接近强一致性)。需先定义:
强一致性:缓存与数据库实时完全一致(适用于库存、支付金额等核心数据)。
最终一致性:短时间内允许不一致,但最终会同步(适用于商品描述、用户等级等非核心数据)。
二、常见技术方案
1. 缓存更新策略:避免 “写脏” 与 “读脏”
(1)Cache Aside Pattern(旁路缓存模式)
流程:
读操作:先查缓存,命中则返回;未命中则查数据库,再写入缓存并返回。
写操作:先更新数据库,再删除缓存(而非直接更新缓存)。
优势:避免 “数据库更新后,缓存未更新” 导致的不一致(如并发写场景下,缓存可能被旧值覆盖)。
注意:
若删除缓存失败,需通过重试机制(如消息队列异步重试)保障。
适用于大多数电商场景(如商品信息、用户地址等)。
(2)Write Through(写透模式)
流程:写操作时,先更新缓存,再由缓存同步更新数据库(缓存作为数据库的 “代理”)。
优势:缓存与数据库同步性高,读操作无脏数据。
劣势:写性能低(需等待数据库确认),适用于写操作少、实时性要求高的场景(如订单状态变更)。
(3)Write Back(写回模式)
流程:写操作时,只更新缓存,缓存异步批量更新数据库(如定时刷新或缓存满时)。
优势:写性能极高(适合高并发写场景)。
劣势:缓存宕机可能导致数据丢失,电商核心数据(如库存)慎用,可用于非核心数据(如用户浏览记录)。
2. 解决 “缓存穿透”“缓存击穿”“缓存雪崩” 导致的一致性问题
(1)缓存穿透:避免查询 “不存在的数据” 穿透到数据库
场景:恶意请求查询不存在的商品 ID,缓存未命中,直接冲击数据库。
解决方案:
对不存在的数据,缓存 “空值”(设置短期过期时间,如 1 分钟),避免重复穿透。
用布隆过滤器预过滤不存在的 Key(如商品 ID 集合),直接拦截无效请求。
(2)缓存击穿:热点 Key 失效时,并发请求穿透到数据库
场景:某商品(如爆款)缓存过期瞬间,大量请求同时查数据库,导致数据库压力骤增。
解决方案:
热点 Key 设置 “永不过期”(或通过后台任务定时更新)。
加互斥锁(如 Redis 的SETNX):当缓存未命中时,只有一个线程能查数据库并更新缓存,其他线程等待重试。
(3)缓存雪崩:大量缓存同时过期,导致数据库被压垮
场景:某时段(如大促零点)大量商品缓存同时失效,请求全部涌向数据库。
解决方案:
缓存过期时间 “随机化”(如基础过期时间 + 1~5 分钟随机值),避免集中失效。
多级缓存:用本地缓存(如 Caffeine)+ 分布式缓存(如 Redis),本地缓存设置较短过期时间,缓解分布式缓存压力。
3. 分布式锁:解决并发写冲突
场景:高并发下,多个线程同时更新同一条数据(如秒杀商品库存),可能导致 “缓存与数据库不一致”(如 A 线程更新数据库后删除缓存,B 线程在删除前读取旧缓存并写入数据库)。
解决方案:
用分布式锁(如 Redis 的RedLock、ZooKeeper)保证同一时间只有一个线程执行 “更新数据库 + 删除缓存” 操作。
示例:秒杀库存扣减时,先获取锁,再执行 “查库存→扣减数据库→删除缓存→释放锁”,避免并发冲突。
4. 异步补偿:缓存更新失败后的兜底机制
场景:更新数据库后,删除缓存失败(如网络波动),导致缓存残留旧值。
解决方案:
写入消息队列:数据库更新后,发送 “删除缓存” 消息到队列,消费者重试删除操作(直到成功)。
定期全量同步:对核心数据(如商品库存),定时(如每 10 分钟)从数据库全量刷新缓存,兜底修正不一致。
5. 版本号 / 时间戳:解决并发更新顺序问题
场景:并发写场景下,若两个更新请求顺序颠倒(如 A 请求更新价格为 100,B 请求更新为 90,但 B 的缓存更新先于 A),会导致缓存值错误。
解决方案:
为数据增加版本号(如数据库字段version),更新时校验版本号:
数据库更新:UPDATE goods SET price=? WHERE id=? AND version=?(版本号匹配才更新,同时 version+1)。
缓存写入时,携带版本号,读取时只接受 “版本号更高” 的数据,避免旧值覆盖新值。
三、特殊场景处理
1. 库存一致性(电商核心痛点)
方案:
库存更新采用 “数据库事务 + 缓存删除 + 分布式锁”:扣减库存时,先通过数据库事务保证原子性,再删除缓存,并用锁防止并发冲突。
缓存只存 “近似库存”,最终以数据库为准(如商品详情页显示 “库存紧张”,实际下单时再校验真实库存)。
2. 大促活动(高并发场景)
方案:
预热缓存:活动前将热点商品数据(价格、库存)提前写入缓存,并设置较长过期时间。
限流降级:对非核心接口(如商品评价)降级为 “只读缓存”,优先保障下单、支付等核心流程的数据一致性。
四、监控与兜底
监控告警:实时监控缓存命中率、数据库与缓存数据差异(如定时抽样对比),发现不一致时告警。
缓存失效策略:为所有缓存设置合理过期时间(即使是热点 Key,也建议设置较长时间 + 后台更新,避免永久脏数据)。
灾备方案:缓存宕机时,快速切换为 “直接读数据库” 模式,并通过熔断机制保护数据库(如 Hystrix 限流)。
总结
电商系统缓存一致性的核心是 “在性能与一致性之间找平衡”:
非核心数据(如商品描述):采用 “最终一致性”(Cache Aside + 异步补偿)。
核心数据(如库存、订单):采用 “强一致性倾向”(分布式锁 + 数据库事务 + 实时删除缓存)。
结合监控和兜底机制,确保异常时可快速恢复,避免业务损失。