以下是 “问题场景 → 最优解决方案” 的直接对应表,不用纠结复杂理论,遇到对应问题直接选方案,清晰落地:
| 核心问题场景 | 最优解决方案 | 核心做法 | 关键说明(避坑 + 兜底) |
|---|---|---|---|
| 1. 读多写少、数据低频变更(如商品详情、秒杀库存查询),想提升接口吞吐量、减轻数据库压力 | 缓存热点数据(Cache Aside 模式) | ① 查询:先查缓存,命中直接返回;未命中查数据库,写入缓存后返回② 写操作:后续配合 “删缓存” 方案 | 基础方案,必须给缓存设过期时间(兜底防脏数据),适合无强一致性要求的场景 |
| 2. 需保证缓存与数据库最终一致,不想写复杂代码(大多数常规业务场景) | 先更新数据库,再删除缓存 | 写操作时:先修改数据库数据 → 数据库更新成功后,删除对应缓存 | 脏数据概率极低(数据库读比写快),实现最简单,无业务侵入,搭配缓存过期时间兜底 |
| 3. 并发极高,用 “先删缓存再更数据库” 时,出现 “查询旧数据写入缓存” 的脏数据 | 延时双删 | ① 先删除缓存② 更新数据库③ 异步休眠(预估读业务耗时 + 300~500ms)后,再次删除缓存 | 解决并发读写冲突,休眠时间要适配业务:- 普通场景:读业务耗时 + 500ms- 读写分离场景:主从同步延时 + 读耗时 + 500ms用线程池异步执行,避免阻塞用户请求 |
| 4. 删除缓存失败(如网络波动、缓存集群故障),导致脏数据残留 | 删除缓存重试机制(优先选 Canal 订阅 binlog) | 方案 1(无侵入):① 数据库更新后写入 binlog② Canal 订阅 binlog,解析出变更数据的 key③ 异步删除缓存,失败则存入消息队列重试方案 2(简单侵入):删缓存失败后,直接将 key 丢进消息队列重试 | 方案 1 更优(不污染业务代码),依赖 Canal + 消息队列,确保删除操作 “最终成功” |
| 5. 写操作频繁、或更新缓存需复杂计算(如多表统计),更新缓存性价比低 | 直接删除缓存,不更新缓存 | 写操作时只删缓存,不做任何更新;下次查询自动从数据库加载最新数据到缓存 | 避免无效计算和频繁更新缓存,减少一致性风险,是通用推荐的写缓存策略 |
| 6. 用 MySQL 读写分离(主写从读),主从同步有延时,导致从库旧数据写入缓存 | 延时双删(调整休眠时间) | 同 “延时双删”,但休眠时间 = 主从同步延时(如 1~2s)+ 读业务耗时 + 500ms | 等主从数据同步完成后再删缓存,避免从库旧数据被写入缓存 |
| 7. 高一致性要求(如金融、订单核心数据),需最小化脏数据时间窗口 | 延时双删 + 缓存重试机制 + 缓存过期时间 | ① 写操作走 “延时双删”② 删缓存失败触发 Canal + 消息队列重试③ 缓存设短过期时间(如 5~10 分钟) | 无绝对一致性方案,此组合是 “最终一致性” 的最优实践,最大化降低脏数据概率 |
最后 3 个关键兜底提醒(必看):
- 所有方案都要给缓存设 过期时间(哪怕是 1 小时),这是极端情况下的最后保障,防止脏数据永久残留;
- 不要用 “先更新缓存再更新数据库” 的方案,线程安全问题严重,脏数据概率极高;
- 秒杀等超高并发场景,优先用 “缓存热点数据 + 先更数据库再删缓存 + 过期时间”,足够支撑业务,无需过度设计。