Postgres 事务:分布式系统的一件超能力
文章摘要
这篇来自 DBOS 的文章主张:把「工作流状态(workflow state)」与「应用数据」放进同一个 Postgres 数据库,而不是拆分到不同系统。二者共处一库后,就能用同一个事务原子地更新,从而消除「部分成功、部分失败」这一分布式系统的顽疾,大幅简化系统设计。
文章给出两个关键技术收益。其一是幂等性(idempotency):持久化工作流的每个步骤在失败重试后可能被重复执行,传统做法是额外维护「记账表」来追踪哪些操作已应用。而当工作流检查点(checkpoint)与数据更新处于同一事务中时,系统自然获得「恰好一次(exactly-once)」语义——检查点与数据修改要么同时成功、要么都不发生,于是「事务型步骤不再需要应用层的幂等逻辑」。其二是原子性与「工作流发件箱(outbox)」:跨系统协调更新(如更新数据库的同时通知仓库系统)通常需要事务发件箱模式——单独维护一张表并轮询。共处一库后,可用 Postgres 的用户自定义函数(UDF)在同一事务内直接入队工作流,省掉独立的轮询进程和对账作业。
文章的战略性论点是:把工作流状态与应用数据统一起来,就能避免多系统间的同步问题与数据漂移,在提升可靠性的同时显著降低运维复杂度。
HN 评论精华
评论区总体呈现明显的怀疑与质疑,核心争点在于「这到底算不算分布式系统」以及「标题是否名不副实」。
-
cloudie78(高赞):直接开怼——「恭喜,你发现了互斥锁(mutex)。这到底是分布式系统,还是一堆共享一个中心数据库的服务?」
-
zyngaro:认为文章充满误解——「你们听说过 CAP 定理吗?」并指出标题误导:Postgres 事务本身并不是分布式的。
-
aynyc、bsaul:表示看了几遍仍不理解「分布式」在哪。aynyc 问:你只是把数据一个事务写进 Postgres,那谁在通知消息队列?bsaul 则认为「写一行以便将来某时更新第二个系统」和「往队列里塞个任务」本质上没多大区别。
-
mrkeen:分享了一段经典面试经历。面试官问「如果你有一个数据库和一个消息队列,怎么让更新对两者要么全改要么都不改(即事务性)?」他答「做不到,你也做不到」,随后搬出复制状态机 / 预写日志 / 事件溯源那套并倒向最终一致性。面试官这才引出「发件箱模式」——正是本文所讲。
-
jdw64:给出了较中肯的技术剖析:本质是把「工作流推进单元」与「数据库提交单元」做成一一对应,每个工作流步骤即一次提交,因此发件箱模式被简化;代价是数据库与工作流被紧耦合,日后难以拆分——不过他也承认「实践中我几乎从不需要拆分数据库」。
-
valentynkit:从正面肯定其价值:在资金流转系统里,把检查点与写操作共处一库,能杜绝「双写 bug」,避免中途崩溃留下「改了一半」的不一致状态。Crowberry 也表示自家在主库里做了几乎一模一样的内置 pubsub,这种原子性确实很爽。