4. 缓存更新策略:Cache Aside、Read/Write Through、Write Behind Caching
缓存与数据库之间的数据一致性,核心在于选择正确的更新策略。不同的策略在一致性、复杂度和性能上各有取舍。本章深入剖析三种主流策略:Cache Aside、Read/Write Through 和 Write Behind Caching,并结合搜索场景分析其适用性。
4.1 Cache Aside(旁路缓存)
Cache Aside 是业界最广泛采用的策略,应用程序直接同时管理缓存和数据库。其核心原则是:读时回填,写时淘汰。
核心原则: 写操作只更新数据库,并删除缓存中的对应键;读操作先查缓存,未命中则从数据库加载并回填缓存。
4.1.1 读写流程
- 读流程: 应用先查询缓存 → 命中则直接返回 → 未命中则查询数据库 → 将结果写入缓存 → 返回数据。
- 写流程: 应用先更新数据库 → 然后删除缓存中的对应键。
4.1.2 优缺点分析
| 维度 | 说明 |
|---|---|
| 优点 | 实现简单,易于理解;缓存中只保留热点数据,内存利用率高;写操作不直接操作缓存,避免写放大。 |
| 缺点 | 存在短暂的缓存不一致窗口(数据库已更新,缓存尚未删除);并发场景下可能出现缓存击穿;需要应用层处理缓存回填逻辑。 |
搜索场景建议: 对于搜索结果缓存,Cache Aside 是最常用的策略。当索引更新时,直接删除对应的搜索结果缓存,下次查询时自动回填最新数据。
4.2 Read/Write Through(读写穿透)
Read/Write Through 将缓存抽象为底层存储的代理,应用只与缓存交互,缓存负责与数据库同步。这种策略对应用层更透明。
4.2.1 读写流程
- Read Through: 应用读取缓存 → 命中则返回 → 未命中则缓存组件自动从数据库加载并回填 → 返回给应用。
- Write Through: 应用写入缓存 → 缓存组件同步将数据写入数据库 → 确认数据库写入成功 → 返回成功给应用。
4.2.2 优缺点分析
| 维度 | 说明 |
|---|---|
| 优点 | 应用层代码简洁,数据一致性由缓存层保证;写操作同步写入数据库,数据可靠性高。 |
| 缺点 | 缓存组件实现复杂;写操作延迟较高(需等待数据库写入完成);不适合写密集型场景。 |
注意: 在搜索系统中,如果使用 Read/Write Through,需要确保缓存组件(如 Redis 模块)具备数据库访问能力,这会增加缓存的复杂度和故障风险。
4.3 Write Behind Caching(异步写回)
Write Behind Caching 与 Write Through 类似,但写操作只更新缓存,然后异步批量将数据写入数据库。这种策略追求极致的写性能。
4.3.1 读写流程
- 读流程: 与 Read Through 相同,缓存未命中时从数据库加载。
- 写流程: 应用直接写入缓存 → 立即返回成功 → 后台异步任务将缓存中的脏数据批量写入数据库。
4.3.2 优缺点分析
| 维度 | 说明 |
|---|---|
| 优点 | 写操作延迟极低(仅操作缓存);可合并批量数据库写入,减少 I/O 压力;适合写密集型工作负载。 |
| 缺点 | 数据一致性弱,存在丢失风险;实现复杂,需要处理异步回写失败、重试、幂等性等问题。 |
搜索场景建议: 对于搜索索引的批量更新或日志类数据,Write Behind 非常合适。例如,将用户搜索行为日志先写入 Redis,再异步批量写入 Elasticsearch 或数据库。
4.4 三种策略对比总结
| 策略 | 一致性 | 写性能 | 实现复杂度 | 数据丢失风险 | 适用场景 |
|---|---|---|---|---|---|
| Cache Aside | 最终一致(短窗口) | 中 | 低 | 低 | 通用场景,搜索结果缓存 |
| Read/Write Through | 强一致(同步写库) | 低 | 中 | 极低 | 对一致性要求高的场景 |
| Write Behind | 最终一致(异步) | 高 | 高 | 高 | 写密集型、可容忍丢失的场景 |
选型建议: 在搜索缓存闭环中,Cache Aside 是最实用的选择,配合“延迟双删”和消息队列可以大幅缩小不一致窗口。Write Behind 适合索引批量重建或日志场景,但必须做好持久化和故障恢复机制。