缓存有哪些读写模式?(Cache Aside、Read/Write Through、Write Behind)
简化版
缓存和数据库的读写协作有几种经典模式:① Cache Aside(旁路缓存,最常用)——应用自己维护缓存,读时「查缓存→未命中查库→回填」,写时「更新库→删缓存」;② Read Through / Write Through(读写穿透)——应用只跟缓存打交道,由缓存层自己负责在未命中时回源数据库(Read Through)、或在写时同步更新数据库(Write Through),对应用透明;③ Write Behind(写回/异步写)——写时只更新缓存就返回,缓存异步批量地把数据刷回数据库,性能极高但可能丢数据。Cache Aside 最通用,Write Behind 适合写密集且能容忍丢失的场景。
详细版
四种模式对比:
| 模式 | 读 | 写 | 特点 |
|---|---|---|---|
| Cache Aside | 应用查缓存,未命中查库回填 | 应用更新库 + 删缓存 | 最常用,应用管缓存 |
| Read Through | 应用只查缓存,缓存未命中自己回源 | — | 回源逻辑封装在缓存层 |
| Write Through | — | 应用写缓存,缓存同步写库 | 写时缓存和库都更新,一致性好但慢 |
| Write Behind | — | 应用写缓存即返回,缓存异步批量刷库 | 性能极高,可能丢数据 |
核心区别:Cache Aside 是应用直接管理缓存和数据库;Read/Write Through 是缓存层代理数据库读写(应用只对接缓存);Write Behind 是 Write Through 的异步版(先写缓存后异步落库)。
完整版教学
一、Cache Aside(旁路缓存):最常用
Cache Aside 是最常见的缓存模式,特点是应用程序自己负责协调缓存和数据库——缓存「在旁边」,应用主动读写它。
- 读:先查缓存,命中直接返回;未命中则查数据库,把结果回填到缓存,再返回。
- 写:先更新数据库,再删除缓存(下次读时重新回填)。
它的优点是简单、通用、可控,是绝大多数业务的默认选择。缺点是应用要自己写缓存逻辑,且有一致性问题需要处理(先更库再删缓存、延迟双删等,见「缓存一致性」专题)。
二、Read Through(读穿透):缓存代理读
Read Through 改变了「谁负责回源」——应用只跟缓存打交道,不直接访问数据库。当缓存未命中时,由缓存组件自己去数据库加载数据、回填、返回,整个回源过程对应用透明。
- 应用视角:只调用
cache.get(key),永远从缓存拿数据,不用管缓存有没有、怎么回源。 - 缓存层:内部实现「未命中就查库回填」的逻辑。
和 Cache Aside 的区别:Cache Aside 的「未命中查库回填」逻辑写在应用代码里;Read Through 把这段逻辑下沉到缓存组件,应用更简洁。需要缓存框架支持(如 Guava/Caffeine 的 LoadingCache、Ehcache)。
三、Write Through(写穿透):缓存代理写
Write Through 是写操作的对应模式——应用写数据时只写缓存,由缓存层同步地把数据也写入数据库(缓存和数据库一起更新,都成功才返回)。
- 应用视角:只调用
cache.put(key, value),不用管数据库。 - 缓存层:内部同步地更新缓存 + 更新数据库。
优点:缓存和数据库始终一致(同步双写),应用简洁。缺点:每次写都要同步写数据库,写延迟高(等数据库写完才返回)。Read Through + Write Through 常配对使用,让应用完全只对接缓存。
四、Write Behind(写回/异步写):极致写性能
Write Behind(也叫 Write Back) 是 Write Through 的异步版本,追求极致写性能:
- 应用写数据时,只更新缓存就立即返回(不等数据库)。
- 缓存异步地、批量地把数据刷回数据库(比如攒一批、每隔一段时间批量写库)。
优点:写性能极高——写只操作内存缓存,不等数据库;且异步批量写库能合并多次写(如对同一 key 的多次修改合并成一次落库),大幅降低数据库压力。
缺点:可能丢数据——数据先在缓存里、还没刷到数据库时,如果缓存宕机,这部分数据就丢了;且缓存和数据库之间存在不一致窗口。所以 Write Behind 适合写非常密集、且能容忍少量数据丢失/延迟一致的场景(如计数器、日志、点赞数)。操作系统的 Page Cache 写盘、数据库的 buffer 刷盘都是 Write Behind 思想。
五、如何选择
- Cache Aside:默认选择,通用、可控,适合绝大多数读多写少的业务。要自己处理一致性。
- Read/Write Through:想让应用完全只对接缓存、逻辑简洁,且有支持的缓存框架时用。Write Through 一致性好但写慢。
- Write Behind:写密集、追求极致写性能、能容忍丢数据/延迟一致的场景(计数、指标、点赞)。
大多数业务用 Cache Aside 就够了。Write Through/Behind 更多出现在缓存框架内部实现或特定高写入场景。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| Cache Aside | 应用自己读写缓存,未命中查库回填 |
| Read/Write Through | 缓存层负责读写后端存储 |
| Write Behind | 先写缓存,异步批量落库 |
Cache Aside read:
v = cache.get(k)
if v == null:
v = db.query(k)
cache.set(k, v)
write:
db.update(k, v)
cache.delete(k)
面试最常考的是 Cache Aside:读时旁路回填,写时更新数据库并删除缓存。
- 误区:所有模式都由缓存自动同步数据库。 Cache Aside 中同步逻辑在应用侧,Read/Write Through 才更像缓存层代理存储。
- 误区:Write Behind 没有丢数据风险。 异步落库在缓存宕机或队列丢失时可能丢数据,要有持久化和补偿。
- 误区:写缓存比删缓存一定更一致。 并发写下更新缓存可能被旧值覆盖,删除缓存更常见。
- 追问:Cache Aside 为什么常用? 简单可控,缓存故障时应用仍可回源数据库。
- 追问:Write Through 的代价是什么? 写请求要经过缓存层同步写后端,延迟较高但一致性更直观。
- 追问:Write Behind 适合什么? 适合写多、可异步落库、能容忍短暂延迟的统计或日志类场景。
七、加强记忆
缓存读写模式:① Cache Aside(旁路,最常用)——应用自己管缓存和库,读「查缓存→未命中查库回填」、写「更新库+删缓存」,通用可控但要自己处理一致性;② Read Through(读穿透)——应用只查缓存,缓存未命中时自己回源查库回填(回源逻辑下沉到缓存层,应用透明);③ Write Through(写穿透)——应用只写缓存,缓存同步写库(缓存库都更新,一致性好但写慢);④ Write Behind(写回/异步)——应用写缓存即返回,缓存异步批量刷库,写性能极高但可能丢数据(适合计数/点赞等写密集可容忍丢失)。核心区别:Cache Aside 应用直管、Read/Write Through 缓存代理读写、Write Behind 是 Write Through 异步版。选择:默认 Cache Aside,极致写性能且容忍丢失用 Write Behind。