3. 缓存架构模式:旁路缓存、读写穿透、异步回写

在集成测试中验证 PostgreSQL 缓存数据一致性,首先需要理解主流的缓存架构模式。不同的模式决定了缓存与数据库之间的交互时序、一致性边界以及测试关注点。本节聚焦三种核心模式:旁路缓存 (Cache-Aside)、读写穿透 (Read/Write-Through) 和 异步回写 (Write-Behind),并分析每种模式在一致性验证中的关键特征。

3.1 旁路缓存 (Cache-Aside)

旁路缓存是应用最广泛的模式,应用代码直接同时管理缓存和数据库。读取时优先查缓存,未命中则从数据库加载并回填缓存;写入时先更新数据库,再删除或更新缓存。

核心特征:应用层承担缓存与数据库的双写/双删逻辑;缓存与数据库之间无自动同步机制。
旁路缓存 (Cache-Aside) 读写流程 应用服务 Redis 缓存 PostgreSQL ① 读缓存 ② 未命中,回填缓存 ③ 写数据库 ④ 删除/更新缓存 ⚠ 一致性风险: 写数据库与删缓存非原子操作,并发读可能读到旧缓存;测试需覆盖“删缓存失败”与“并发脏读”场景。

旁路缓存验证要点

验证场景测试方法预期结果
缓存未命中回填首次读取某 key从 PostgreSQL 加载并写入缓存,后续读取命中
写后删缓存更新数据库后立即删除缓存下次读取触发回填,数据一致
删缓存失败模拟 Redis 异常或网络超时缓存保留旧数据,需补偿或过期机制
并发读写竞争线程 A 写库删缓存,线程 B 同时读可能读到旧缓存,需加锁或延迟双删
测试建议:旁路缓存模式下,集成测试应重点构造“写后立即读”的并发场景,并验证缓存过期或删除失败后的数据收敛能力。

3.2 读写穿透 (Read/Write-Through)

读写穿透模式将缓存作为数据访问的唯一入口。应用只与缓存交互,缓存组件内部负责同步数据库。读取时缓存未命中由缓存层加载数据库;写入时缓存先更新自身,再同步写入数据库。

核心特征:缓存层承担同步代理职责;应用对数据库无感知;缓存与数据库之间由缓存中间件保证同步。
读写穿透 (Read/Write-Through) 流程 应用服务 缓存代理层 (同步读写数据库) PostgreSQL 读请求 未命中时回源 写请求 同步写库 ⚠ 一致性风险: 缓存代理层同步写库失败时,缓存与数据库可能不一致;测试需验证代理层的重试、回滚和降级策略。

读写穿透验证要点

  • 缓存未命中回源:验证缓存代理层在 key 不存在时,正确从 PostgreSQL 加载并回填缓存。
  • 同步写库失败:模拟数据库连接超时或写入冲突,验证代理层是否抛出异常或进入降级模式。
  • 缓存与数据库原子性:验证写操作在缓存更新后、数据库写入前发生崩溃时,数据是否回滚。
  • 并发穿透:高并发读取同一未命中 key 时,验证是否只触发一次数据库查询(防雪崩)。
注意:读写穿透模式对缓存中间件要求较高,集成测试需覆盖代理层本身的故障场景,而非仅关注应用逻辑。

3.3 异步回写 (Write-Behind)

异步回写模式中,写入操作只更新缓存,立即返回成功;缓存组件在后台异步批量将数据写入数据库。该模式追求极致写性能,但牺牲了强一致性。

核心特征:写操作先落缓存,异步批量刷库;读操作始终从缓存获取;数据最终一致。
异步回写 (Write-Behind) 流程 应用服务 Redis 缓存 异步写入队列 (批量/延迟刷入) PostgreSQL ① 写缓存(立即返回) ② 异步入队 ③ 批量刷库 读缓存(始终命中) ⚠ 一致性风险: 缓存与数据库存在时间窗口不一致;缓存崩溃导致数据丢失;测试需验证刷库延迟、崩溃恢复和幂等性。

异步回写验证要点

验证场景测试方法预期结果
写后立即读写入缓存后立即查询数据库数据库尚未更新,数据不一致(最终一致)
刷库延迟监控异步队列处理时间在配置的延迟窗口内完成刷库
缓存崩溃模拟 Redis 宕机后恢复未刷库的写入丢失,需持久化或 WAL 保护
重复刷库模拟队列重试机制数据库操作需幂等,避免重复写入
测试建议:异步回写模式应重点验证“缓存持久化”和“崩溃恢复”场景,确保异步队列在故障后不丢失关键数据。

3.4 三种模式对比总结

维度旁路缓存读写穿透异步回写
一致性模型最终一致(需补偿)强一致(同步写库)最终一致(时间窗口)
写性能中等(双写开销)中等(同步等待)高(异步批量)
实现复杂度低(应用自管理)中(需代理层)高(队列+幂等)
数据丢失风险低(数据库为主)低(同步写入)高(缓存崩溃)
集成测试重点并发竞争、删缓存失败代理层故障、回源穿透刷库延迟、崩溃恢复
选型建议:对一致性要求高的业务(如订单、支付)优先使用读写穿透或旁路缓存+延迟双删;对写性能要求极高且可接受短暂不一致的业务(如点赞、日志)可选用异步回写。