决策:选择哪种缓存策略?
缓存将频繁读取的数据保存在数据库前面的快速层中(例如 Redis),降低延迟和数据库负载。但并非所有数据都适合缓存,错误的失效处理会带来数据过时/错误的风险。这棵决策树首先检查你是否真的需要缓存,然后根据读写模式帮助你在 cache-aside、read-through、write-through 和 write-behind 之间做出选择,并介绍如何防止缓存击穿(stampede)。
你的负载是什么样的:读密集还是写密集?
当相同数据被多次读取(读密集)且不常变化时,缓存的收益最大。先确定总体模式:数据是读多改少,持续被更新,还是你不确定添加缓存是否值得?
- 读取非常密集,数据相对很少变化
- 写入也很密集;写入的数据会被立即/频繁读取
- 不确定;我真的需要缓存吗?
应该由谁负责填充缓存?
在读密集场景中,关键选择是由谁填充缓存。是由应用显式管理缓存并自行回退到数据库(灵活性高),还是由缓存层在未命中时从后端数据库获取数据并透明地返回?
- 由应用显式管理;我需要灵活性和控制力
- 由缓存层在未命中时从数据库获取(透明)
写入时,一致性和写入速度哪个优先?
如果写入密集,你必须决定写入的数据在缓存和数据库之间的一致程度。是在每次写入时同时更新缓存和数据库以保证一致性(写入稍慢),还是先写入缓存,再在后台异步应用到数据库(写入极快,但有丢失风险)?
- 一致性重要;读取必须始终是最新的
- 写入速度至关重要;可以接受略有延迟的一致性和丢失风险
建议:暂时不要添加缓存
缓存不是免费的:它会引入一致性问题(过时数据)、额外的基础设施和失效复杂性。如果查询已经很快,或者数据在每次请求时都会变化,缓存不会带来好处,只会增加故障面。
建议:cache-aside(懒加载)
使用 cache-aside 时,应用先检查缓存;如果数据不存在(未命中),则从数据库读取,写入缓存后返回。这是最常见、最灵活的模式;缓存中只会填充实际被请求的数据。
建议:read-through 缓存
使用 read-through 时,应用只与缓存交互;未命中时,由缓存层自己从数据库获取数据并填充(通过 provider/loader)。应用代码更简单,填充逻辑集中在一处。
建议:write-through 缓存
使用 write-through 时,每次写入都同时(同步)写入缓存和数据库;两者一起更新。因此缓存永远不会落后于数据库,读取总能看到最新数据。
建议:write-behind(write-back)缓存
使用 write-behind 时,写入先进入缓存并立即返回给用户;数据库写入在后台异步进行(批量/延迟)。写入速度最高,但存在持久性和一致性风险。