Muhammad Aqeel:为 Postgres 实现撤销,无需耗费你大半天时间

Muhammad Aqeel:为 Postgres 实现撤销,无需耗费你大半天时间

💡 原文英文,约2200词,阅读约需8分钟。
📝

内容提要

pgVolvra 是纯 SQL 实现的 Postgres 行级撤销工具,无需扩展或超级用户,可在托管数据库运行。它记录指定表的每次增删改,支持查看历史、预览并撤销误操作,并拒绝覆盖事后被他人修改的行。配合外部归档进程,历史可脱离数据库保存,并能在备份恢复后重放丢失的变更。代价是写入吞吐下降约 11% 至 41%。

🔎

延伸解读

纯 SQL 实现带来的部署便利与边界

pgVolvra 完全用 SQL 和 PL/pgSQL 写成,不需要编译扩展、修改 shared_preload_libraries 或获取超级用户权限,因此能在托管 Postgres 上直接运行。作者在 Supabase、Neon 和 pgEdge Starfleet 上做了验证,其中 Supabase 免费版无需额外配置,Neon 需在控制台开启逻辑复制且该操作不可逆。不过,Amazon RDS、Aurora 和 Google Cloud SQL 尚未验证,文章没有声称支持。

撤销不是回滚快照,冲突检查是安全核心

pgVolvra 的撤销只反向应用记录中的行变更,而不是把整张表恢复成某个时间点的快照。因此,在误操作之后发生的真实用户修改不会被覆盖。如果某行在误操作后又被他人改动,撤销会停止并指出冲突行,用户可以选择 skip_conflicts 跳过这些行,但无法强制覆盖。这种设计避免了修复一个错误时破坏另一个正确操作,代价是撤销可能无法一次完成。

归档与重放:让历史脱离数据库生存

默认历史保存在数据库内的普通表中,会随数据库一起丢失。可选的伴生进程在数据库外读取每次提交的变更,写成带 SHA-256 清单的 JSON 文件。当数据库从备份恢复后,pgVolvra 能读取归档并重放备份之后丢失的变更。作者用真实的 pg_dump、DROP DATABASE 和恢复流程测试,在 Postgres 14 到 19 上恢复后的数据库与丢失前逐行哈希一致。但重放只恢复行数据,不处理模式变更、序列或未覆盖的表。

写入开销与适用场景的权衡

记录历史不是免费的:写入吞吐下降约 11% 至 41%,窄 UPDATE 最严重,简单 INSERT 最轻;每次记录变更约增加 300 至 400 字节,WAL 体积约增加 2.2 倍。因此作者建议只覆盖那些丢失数据会真正造成损失的表,而不是全库启用。此外,pgVolvra 不是备份替代品,只能恢复启用之后发生的变更,且要求表有主键。它适合作为误操作后的精确撤销工具,而非时间点恢复方案。

❓

Q&A

pgVolvra 是什么?它主要解决 Postgres 的什么问题?

pgVolvra 是一个纯 SQL 实现的 Postgres 行级撤销工具,用于精确回滚误操作(如漏写 WHERE 的 UPDATE)。它记录指定表的每次增删改,支持查看历史、预览并撤销误操作,且无需扩展或超级用户权限。

pgVolvra 如何安装和启用?需要什么权限?

安装只需运行一个 SQL 文件(如 psql -f sql/volvra.sql),然后对目标表执行 SELECT volvra.enable('public.orders') 启用。它不需要编译扩展、shared_preload_libraries、超级用户或文件系统访问,因此可在托管 Postgres 上运行。

pgVolvra 的撤销操作如何避免覆盖他人后续修改?

撤销前会检查每一行:如果某行在误操作后已被他人修改(值不再匹配),整个操作会停止并指出冲突行。可传 skip_conflicts 跳过冲突行修复其余行,但没有强制覆盖选项,设计上故意不提供 force 标志。

pgVolvra 的归档与重放功能有什么作用?

通过外部 companion 进程将变更以 NDJSON 格式归档到数据库外,历史可脱离数据库保存。当数据库丢失并从备份恢复后,可读取归档重放丢失的变更,将恢复的数据库推进到最新状态。重放使用与撤销相同的机制,并经过真实 pg_dump、DROP DATABASE 等测试验证。

使用 pgVolvra 对数据库性能有什么影响?

写入吞吐下降约 11% 至 41%,窄 UPDATE 最差,简单 INSERT 约 11-12%。每次记录变更约 300-400 字节,WAL 量增加约 2.2 倍。无变化的更新会被跳过,默认只存储实际更改的列。建议只覆盖真正重要的表。

pgVolvra 有哪些使用限制?

它不能替代备份;只能恢复启用覆盖之后发生的变更;重放只恢复行数据,不重放模式变更、序列或未覆盖的表;不是时间点恢复;表必须有主键(唯一索引不行)才能被覆盖。

🏷️

标签

➡️

继续阅读