推文
@Zen_with_AI · 2026-10-11 12:44
小路带你60天学系统设计Day 14/60|缓存一致性:缓存和数据库,到底谁先谁后? Day 13 为了给 DB 减负加了缓存。但缓存和数据库是两份数据,写操作一来,两边怎么保持一致?这是缓存用得最多的地方,也是最容易出问题的地方。 最常用的模式叫 Cache-Aside(旁路缓存)。读:先查缓存,命中直接返回;没命中读 DB,再把结果写回缓存。写:更新 DB,然后删除缓存——注意是删除,不是更新缓存。原因有二:删操作天然幂等,重试多少次结果都一样;缓存未命中时会自己回源重建,删了不会丢数据。 那为什么不能"先更新缓存再更新 DB"?如果缓存更新成功、DB 更新失败,缓存里就躺着一份永远对不上的脏数据。反过来"先更新 DB 再更新缓存"也有坑:并发下,A 更新了 DB 还没来得及更新缓存,B 读到旧数据又写回缓存,脏数据照样产生。 工程上的标准答案是:更新 DB → 删除缓存 → 延迟 N 秒再删一次(延迟双删)。第二次删除兜住的是并发窗口:读请求在删缓存之前读到了旧数据、之后又写回缓存,延迟的第二次删除把它清掉。延迟时长一般取一次读业务的耗时。 还有三种读写策略,一句话版:Read-Through 是应用只跟缓存打交道,未命中由缓存层自己回源;Write-Through 是写操作同步写缓存和 DB,一致性强但写变慢;Write-Behind 是只写缓存、异步批量刷回 DB,最快但缓存挂了会丢数据。 再加一层兜底:如果删缓存这一步失败(网络抖动、进程崩溃),不一致就长期存在。用 CDC 监听 DB 的 binlog(如 Canal、Debezium),或把"删缓存"发进消息队列重试,保证最终删掉。 最后区分三个常考词:雪崩是大量 key 同时过期、请求全压到 DB;击穿是单个热点 key 过期被高并发打穿;穿透是查根本不存在的数据、每次都直达 DB。 💡 思考题:延迟双删的延迟设多长合适?设太短和太长分别有什么后果? Day 15 预告:消息队列 #系统设计60天
曝光 929 · 评论 2 · 点赞 7 · 书签 7 · 曝光/时 165.48955647304584