Write Behind 模式:异步回写的数据持久化策略
什么是 Write Behind 模式
Write Behind(滞后写入,也称 Write Back)是一种缓存写入策略:应用程序只写入缓存就立即返回,由后台线程异步地将缓存数据批量或延迟写回数据库。这种策略的核心思想是”我先记下来,有空再存盘”。
工作流程
- 应用程序写入数据到缓存层(通常直接更新内存中的数据)
- 缓存层立即返回成功,应用程序继续处理其他逻辑
- 缓存层通过后台线程(或定时任务),收集一段时间内或一定数量的修改
- 将收集到的修改批量写回数据库
与 Write Through 的对比
| 维度 | Write Behind | Write Through |
|---|---|---|
| 响应延迟 | 低(只写缓存) | 高(等待DB写入) |
| 数据安全性 | 低(宕机丢数据) | 高(持久化保证) |
| 数据库压力 | 低(批量写入) | 高(每次写入) |
| 实现复杂度 | 高(需异步队列) | 中等 |
| 一致性 | 弱一致性 | 强一致性 |
适用场景
适合使用的场景
- 写多读少的数据:如用户行为日志、点击流数据、埋点数据,允许少量丢失
- 对延迟极度敏感:如实时排行榜、计数器场景,用户写入后希望立即获得响应
- 数据库写入成为瓶颈:通过批量合并写入减少数据库 I/O
- 写入可合并的场景:如计数器累加、状态更新,最终值才是关键
不适合的场景
- 金融交易:不允许任何数据丢失,必须保证每次写入持久化
- 强一致性业务:写入后立马需要新的查询结果一致
- 监管审计需求:需要完整的写入日志和追踪链
实现要点
异步队列设计
@Component
public class WriteBehindCache {
private final Map<String, String> cache = new ConcurrentHashMap<>();
private final BlockingQueue<WriteTask> queue = new LinkedBlockingQueue<>();
@PostConstruct
public void init() {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// 每5秒批量刷一次,或者队列积累到100条
executor.scheduleWithFixedDelay(this::flush, 5, 5, TimeUnit.SECONDS);
}
public void put(String key, String value) {
cache.put(key, value); // 先写缓存
queue.offer(new WriteTask(key, value)); // 异步入队
}
private void flush() {
List<WriteTask> batch = new ArrayList<>();
queue.drainTo(batch, 100);
if (!batch.isEmpty()) {
// 批量写入数据库
jdbcTemplate.batchUpdate("INSERT INTO ...", batch);
}
}
}
关键设计决策
- 刷盘策略:定时刷新(时间驱动) vs 数据量触发(条数驱动) vs 混合策略
- 队列容量:超过容量时需做策略选择——阻塞生产、丢弃新旧、强制立即刷盘
- 失败重试:写入数据库失败时的重试机制,指数退避或死信队列
- 持久化保证:使用 Redis RDB/AOF 持久化,或配合消息队列(Kafka)保证不丢
断点恢复与数据一致性
Write Behind 最大的问题是宕机导致内存中的脏数据丢失。常见补救措施:
- Redis AOF + 定时刷盘:利用 Redis 自身的持久化保护数据,但存在秒级丢失窗口
- WAL(Write Ahead Log):先写日志再更新缓存,宕机后通过重放日志恢复
- 消息队列中间层:先发送到 Kafka/RocketMQ,消费者异步写入数据库,配合偏移量管理和重试机制
极端场景处理
- 服务突然宕机:缓存中未刷盘的数据丢失 → 业务上可接受丢失的范围 + 补偿机制
- 数据库压力飙升:刷盘队列堆积 → 限流降级,丢弃非关键写入或转为立即写
- 双写不一致:写数据库成功但缓存更新失败 → 利用数据库 binlog 监听做最终一致性补偿
面试要点
- 讲清楚 Write Behind 是以数据安全换性能的策略
- 能对比 Write Through、Write Behind、Cache Aside 的差异
- 能说出 Write Behind 的核心风险(宕机丢数据)和解决方案(WAL、MQ 中间层)
- 结合实际场景说明何时选用:计数器场景选 Write Behind,支付场景绝不选
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容