Aman Kumar Singh

写通通过同步写入缓存和数据库来保持两者一致,这会使写入速度变得和数据库一样慢。写后(也叫写回)则采取相反的策略:立即写入缓存并立刻确认,稍后再异步将数据持久化到数据库。因为写入只触碰内存,速度变得更快。代价是:已提交的写入仅存在于 Redis 中,如果 Redis 崩溃,这些写入就会丢失。这是性能最高、风险也最高的缓存模式,使用前必须清楚自己在做什么权衡。

本文是《Redis 大师班》第 11 篇,接续读通与写通

写后模式的工作原理

在写后模式(也叫写回)中,写入先到达 Redis 并立即返回,数据库更新则异步、批量地进行:

  1. 应用写入 Redis 并立即收到确认。
  2. 写入被排入持久化队列。
  3. 后台进程消费队列并写入数据库,通常会将多条写入合并成更少的数据库操作。
// write is instant: only touches Redis
await redis.hset(`counter:${id}`, 'value', newValue);
await redis.rpush('write_queue', JSON.stringify({ id, value: newValue }));
// a background worker later batches these into the database

Enter fullscreen mode Exit fullscreen mode

应用看到的写入延迟仅为 Redis 写入,亚毫秒级。数据库稍后以高效批次吸收这些写入,同时把大量小写入合并成少量大操作,降低数据库负载。

为什么快,以及为什么有风险

速度来自两点:写入只触碰内存,以及批处理摊薄了数据库开销。对于写密集型负载(高频计数器、指标、遥测、浏览量追踪),写后模式可承受同步写入会压垮数据库的写入速率。

风险同样真实。在确认写入与最终持久化到数据库之间的窗口里,数据仅存在于 Redis 中。如果 Redis 在此窗口崩溃(或持久化工作进程死亡),这些写入就会丢失——尽管应用被告知它们已成功。写后模式削弱了持久性:你告诉用户“已保存”,但其实还没有真正持久化。这对某些数据可接受,对另一些则不可接受,诚实地判断数据类型至关重要。

写后模式适合哪些场景

该模式适合写吞吐量至关重要、且能容忍最近写入偶发丢失的场景:

  • 分析与指标:页面浏览、事件计数、遥测。极少数崩溃下丢失几秒计数无关紧要,且写入量巨大。
  • 聚合计数器:点赞、浏览量等,计数是累加值,小误差可自我修正。
  • 活动追踪:最后在线时间戳、非关键日志。

对这些场景,写后模式的高吞吐量是真正优势,持久性弱点也可接受。共同点是数据量大、单条价值低。

写后模式不适合哪些场景

任何丢失写入会造成严重问题的场景都不能使用:

  • 金融交易、订单、支付:绝不要对这些数据使用写后模式。“已保存”却消失是严重故障。
  • 用户被告知已永久保存、且丢失会造成恐慌的任何数据。
  • 没有其他真相来源的数据:如果 Redis 是唯一存在该写入的地方,丢失即是永久丢失。

规则:若崩溃导致最近几秒写入丢失会引发真实事故,就不要使用写后模式。请改用写通或普通旁路缓存 + 同步数据库写入,并接受更高延迟作为持久性的代价。

如何降低风险

若必须使用写后模式,可采取以下措施降低(而非消除)风险:

  • 启用 Redis 持久化(AOF,后续章节会介绍),让 Redis 重启后也能恢复最近写入,缩小丢失窗口。
  • 使用持久化队列存放待持久化写入,例如 Redis Stream,使待写入列表比纯内存结构更可靠。
  • 缩短刷盘间隔,缩小未持久化数据的窗口。
  • 监控队列深度,及时发现持久化落后情况,既能控制风险窗口,也能预警问题。

这些措施能降低写后模式的风险,但仍无法达到同步数据库写入的持久性。窗口缩小了,但并未关闭。

写后模式是专业工具:以削弱持久性为代价,换取高吞吐量,适用于高容量、可容忍丢失的数据。将其用于指标、计数器、遥测等吞吐量优先且偶发丢失无害的场景,并远离任何不能丢失的数据。决策完全取决于数据价值,请务必有意识地做出判断。

下一篇文章将介绍预刷新缓存:通过在热点键过期前刷新,避免缓存未命中。

要点总结

  • 写后模式将写入发送到 Redis 并立即确认,数据库更新异步批量进行。
  • 它提供亚毫秒级写入,并通过批处理降低数据库负载,适合高写入量场景。
  • 风险在于持久性:已确认但尚未持久化的写入,在 Redis 崩溃时会丢失。
  • 适用于分析、指标、计数器等吞吐量优先且能容忍最近写入偶发丢失的场景。
  • 绝不能用于金融数据、订单等丢失写入会引发事故的场景;请改用写通。

常见问题

什么是写后(写回)缓存?

写入先到达缓存并立即返回,数据库则在后台异步更新(通常批量),从而实现快速写入,但会引入已确认写入仅存在于缓存中的窗口。

写后与写通有何不同?

写通同步更新缓存和数据库,写入持久但慢;写后仅同步更新缓存,数据库稍后更新,写入快但可能在缓存崩溃前丢失。

何时应使用写后缓存?

适用于高容量、可容忍丢失的数据,如分析、指标、遥测和聚合计数器,写吞吐量重要且能接受极少数崩溃下丢失最近几秒写入。

何时应避免使用写后?

金融交易、订单、支付,或用户被告知已永久保存的任何数据。若丢失写入会引发真实事故,请改用写通或同步数据库写入。

如何使写后更安全?

启用 Redis 持久化(AOF),将待写入数据放在 Redis Stream 等持久化结构中,缩短刷盘间隔,并监控队列深度。这些措施能缩小潜在丢失窗口,但仍无法达到同步数据库写入的持久性。

延伸阅读


本文最初发布于 amanksingh.com/blog/redis-write-behind

关于作者

Aman Kumar Singh 是印度诺伊达的团队负责人兼高级软件工程师,撰写关于 TypeScript、Next.js、NestJS、PostgreSQL 和 Redis 的全栈工程、系统设计和生产级 SaaS 内容。