PostgreSQL 与 CockroachDB 的多区域可用性比较
PostgreSQL 与 CockroachDB 比较:在一致性、延迟、模式变更以及过早走向多区域时的真实运维成本方面的实用对比。

你真正要解决的是什么问题?
“多区域可用性”被用来表示好几种不同的目标。把这些目标混在一起就是团队选错数据库的常见原因。
在比较 PostgreSQL 和 CockroachDB 之前,先写下 (1) 你想要在故障中幸存的具体类型和 (2) 在故障发生时用户应当体验到的情况。
大多数团队在追求下面这些目标的某种组合:
- 当某个区域宕机时更高的可用性(故障切换)
- 让远离主区域的用户响应更快(更低延迟)
- 与地理位置相关的数据规则(本地性或数据驻留)
- 在高负载下也有可预测行为,而不仅仅是理想路径下的测试表现
共同目标很直接:另一大洲的客户仍能得到快速且正确的结果。
难点在于“快速”和“正确”在将写入分布到多个区域后可能会冲突。更强的一致性通常意味着更多的跨区域协调,这会增加延迟。为了降低延迟,人们常常从附近的副本读取或使用异步复制,但这会导致读取过期或冲突处理成为你的责任。
一个具体例子:德国的用户更新了收货地址并立即结账。如果结账从落后几秒的美国副本读取,订单可能使用旧地址。有些产品可以通过清晰的 UX 和重试来容忍这种情况,另一些(支付、库存、合规)则不能容忍。
没有放之四海而皆准的最佳选择。正确的答案取决于哪些行为绝对不能出错、哪些可以慢一点,以及你的团队每天能承受多少运维复杂性。
“在多个区域可用”有两种做法
当人们拿 PostgreSQL 和 CockroachDB 比较多区域使用时,很多情况下其实是在比较两种不同的设计。
对于 PostgreSQL,最常见的部署是单主(single-primary)。某一个区域是写入的“家”,其他区域运行从主库复制数据的只读副本。如果主区域故障,你在其他区域提升一个副本并把应用指向它。做得好时这能发挥很好,但系统仍然以一个主要写入位置为中心,并辅以一套有意的故障切换计划。
像 CockroachDB 这样的分布式 SQL 系统从一开始就设计成把数据和责任在各个区域间分布。数据被复制到多个节点,集群就写入顺序达成一致。你通常可以把部分数据放得更靠近不同区域的用户,同时保持一个逻辑上的单一数据库。
对应用团队的改变更多是关于期望而不是 SQL 语法:
- Writes:PostgreSQL 的写入在靠近主库的地方最快。CockroachDB 的写入往往需要多个副本达成一致,这可能包括跨区域确认。
- Reads:PostgreSQL 可以从本地副本提供读取(以可过期性为代价)。CockroachDB 可以提供一致性读取,但根据数据放置情况可能需要协调成本。
- Failures:PostgreSQL 的故障切换是需要你触发和管理的操作。CockroachDB 设计上能在一定范围内在区域故障中保持运行,但这受其复制和法定节点数规则限制。
隐藏的要求是故障期间的正确性。如果你能容忍短暂的过期读取,或在故障切换期间短时间的写入暂停,单主 PostgreSQL 可能非常合适。如果你需要在某个区域宕机时系统仍保持正确且可写,那你就得接受分布式数据库的协调成本。
一致性保证:你能依赖什么
用平实的语言来讲一致性:当有人更新一条记录时,其他人应看到相同的真相。
对于 PostgreSQL,当应用只与一个主库交互时,强一致性最简单。读写发生在同一地点,事务行为可预测。你可以添加副本来加速其他区域的读取,但这时你必须决定何时可以容忍读取稍微过期的数据。
对于 CockroachDB 等分布式 SQL 系统,强一致性同样可以实现,但随着你把数据分布到更远的区域,成本会增加。必须在区域间保持一致的写入需要节点之间的协调。区域越远,协调所需时间越长。你通常会感到写入和事务变慢,尤其是当一个事务触及位于不同区域的行时。
两种系统都可以支持可串行化事务(数据库努力使并发修改看起来像逐个发生)。区别在于工作发生的地方:PostgreSQL 大多把成本集中在一个区域内部,而分布式系统可能把成本分散到多个区域。
几个问题能把权衡具体化:
- 用户是否可能看到过期的读取,即便只有几秒钟?
- 两个区域能否独立接受写入,还是每次写入都必须全局达成一致?
- 如果两个人同时编辑同一条记录会怎样?你是否允许冲突?
- 哪些操作每次都必须正确(支付、权限),哪些可以“最终一致”(分析)?
- 你需要一个全局真相,还是允许某些数据有“本地真相”?
延迟预期:用户会感到什么
一个有用的心理模型:距离会增加时间,协调会再增加时间。距离是物理规律。协调是数据库在等待其它节点达成一致才能安全地宣布“完成”。
在单区域 PostgreSQL 设置中,大部分工作都在靠近的地方完成。读取和写入通常在应用与数据库之间完成一次往返。如果你在另一个区域放了只读副本,读取可以本地完成,但写入仍然要回到主库,副本至少会滞后一段时间。
在像 CockroachDB 这样的分布式系统中,数据分布到各区域。这会让某些当数据恰好在本地时的读取感觉很快。但很多写入必须得到多数副本的确认。如果你的数据跨大陆复制,即使一个简单的写入也可能需要跨区域的确认。
别只看平均延迟,要看 p95 延迟(最慢的 5% 请求)。用户注意到那些暂停。一页通常加载 120 ms,但偶尔几次达到 800 ms,会让体验显得不稳定,即使平均值看起来不错。
“快”的含义取决于工作负载。写密集型应用通常更明显感受到协调成本。读密集型应用在读取本地数据时表现良好。大型事务、多步骤工作流和“热点”行(很多用户同时更新同一记录)会放大延迟问题。
在评估 PostgreSQL 与 CockroachDB 时,把最常见的用户操作(注册、结账、搜索、管理更新)映射到数据所在位置,以及每次事务需要多少个区域达成一致。这个练习比通用基准更能预测用户体验。
运维权衡:你每天要做什么
功能列表不如那些会把你凌晨 2 点叫醒的问题重要。
PostgreSQL 的运维熟悉且可预测。多区域通常意味着你还要运维一系列配套组件:副本、故障切换工具或带应用级路由的独立区域集群。工作常常是证明计划可行(故障演练、恢复),而不是日常的查询调优。
CockroachDB 把更多多区域的责任放入数据库本身。这可以减少数据库周围的额外组件,但也意味着你必须理解分布式系统:节点健康、复制、负载均衡以及集群在压力下的行为。
实际上,无论哪种设置,团队最终都要做相似的核心工作:
- 规划升级并验证驱动、监控和自动化
- 做备份并进行恢复演练(不只是确认备份存在)
- 练习故障切换并把具体的跑步骤写成运行手册
- 调查慢查询并区分“差的查询”和跨区域延迟
- 关注存储增长与长期压缩行为
故障模式感觉不同。对于 PostgreSQL,区域宕机常常触发一次有意的故障切换。你可能接受一段只读模式、更高的延迟或功能受限。在分布式数据库中,更难处理的情况往往是网络分割。系统可能为保护一致性而拒绝某些写入,直到法定节点数恢复。
可观测性也会改变。有单一主库时,你大多数时间在问“这个查询为什么慢?”。在分布式集群中,你还要问“这条数据落在哪里,为什么查询跨区域了?”。
成本既有明显的也有不明显的上升。增加第二个区域会提高节点数,也会增加监控、事故复杂性,以及向产品团队解释延迟和故障行为所需的时间。
分布式环境中的模式变更和迁移
模式变更是指对数据形状的任何修改:添加列以支持一个功能标记、重命名字段、改变类型(int 到 string)、添加索引或引入新表。
在 PostgreSQL 中,迁移可能很快,但风险通常来自锁等待和写入阻塞。有些变更会重写整个表或比预期占用更长的锁,这可能在高峰流量时把一次正常部署变成事故。
在分布式数据库中,风险点有所不同。即便支持在线模式变更,变更也需要节点间达成一致并跨区域复制。“简单”的变更可能需要更长时间部署和验证。你可能完成部署后仍需花时间观察滞后、热点和每个区域的查询计划异常。
一些能让迁移平稳的习惯:
- 优先采用增量式变更(先增加新列、增加新表)。先切换读写,再删除老字段。
- 保持每次迁移都足够小以便快速回滚。
- 能避免就不要就地变更类型。把数据回填到新列中。
- 把索引当作功能发布来对待,而不是快速调整。
- 用真实数据规模练习迁移,而不是空的测试库。
示例:为欧盟用户添加 preferred_language。先添加列,在一个发布里同时写入旧字段和新字段,然后更新 UI 去读取新字段,最后再清理旧字段。在多区域设置中,分阶段发布能减少当各区域赶上进度时出现的意外。
过早走向分布式的真实成本
在早期选择 PostgreSQL 还是 CockroachDB 不只是数据库决策。它会改变你发布速度、生产中意外发生的频率,以及团队把时间花在维持稳定还是构建功能上。
如果你能在单主区域达成目标,保持简单通常在早期更占优。你会有更少的移动部件、更明确的失败路径和更快的调试。招聘也更容易,因为深入的 PostgreSQL 经验更普遍,本地开发和 CI 也更简单。
团队常常先保持集中化,因为这支持更快的迭代、更简单的回滚和更可预测的性能。系统移动部件更少时,值班也更容易。
如果需求是真实且不可协商的(跨区域的严格可用目标、法律数据驻留、或全球用户群体中延迟直接影响收入),那么早期采用分布式仍然可能是正确的选择。
复杂性税会以各种看似小的方式出现并累积:功能工作需要更多时间,因为必须考虑多区域行为;测试需覆盖更多故障模式;事故排查时间变长,因为根因可能是时序、复制或共识机制,而不是“数据库挂了”。即便是基本的模式变更也要更谨慎。
一个有用的经验规则是:先验证需求,再分布式化当痛点可量化时发生。常见触发点包括某区域持续未达 SLO、稳定的用户流失因延迟、或阻碍成交的合规需求。
如果你用像 AppMaster 这样的工具,可以先用更简单的部署来打磨工作流程和数据模型,等产品和流量模式被验证后再迁移到多区域方案会更轻松。
选择它们的逐步方法
把“多区域”转化为几个数字和几个用户流程,会更清晰。
逐步方法
- 写下以明文表达的 RPO 和 RTO。例如:“如果某个区域挂掉,我们可以接受最多 1 分钟的数据丢失(RPO),并且必须在 15 分钟内恢复(RTO)。”如果你不能容忍已提交写入丢失,就明确说明。
- 绘制用户分布并标记写入关键动作。列出你的区域和顶级操作:注册、结账、重置密码、发布评论、查看信息流。不是所有写入都同等重要。
- 为每个功能设定一致性需求。支付、库存和账户余额通常需要严格正确。信息流、分析和“最后在线”常常能接受轻微延迟。
- 设定延迟预算并从目标区域测试。决定“够快”的标准(例如关键操作 200 到 400 ms),然后从你关心的区域测量往返时间。
- 选择团队能支持的运维模型。坦诚评估值班时间、数据库技能和对复杂性的容忍度。
快速示例
如果大多数用户在美国,只有少数在欧盟,你可能把写入保留在一个主区域,收紧恢复目标,并为非关键页面在欧盟添加只读优化。如果确有欧盟写入密集的流程需要更好体验,考虑用欧盟的服务层或队列来让 UI 保持响应。只有当多区域正确性对核心表(账户、计费、权限)变得必需时,再重新评估数据库选择。
示例场景:美区和欧盟客户使用同一产品
想象一个 B2B SaaS 场景,一个账户在纽约和柏林都有团队成员。所有人都能看到相同的工单、发票和使用额度。计费是共享的,所以一次支付事件应立即影响整个账户的访问权限。
在 PostgreSQL 中,常见做法是把主数据库放在美国,欧盟放只读副本。美国用户读取和写入都快。欧盟用户可以本地读取,但凡需要立即正确的内容(当前计划、最新权限、发票状态)通常需要命中美国主库。如果欧盟的读取来自副本,你就得接受它可能滞后。这可能导致柏林的财务管理员付款后刷新页面仍看到“逾期”的情况。
在像 CockroachDB 这样的多区域分布式数据库中,你可以把数据放得更靠近两个区域,同时保持一个逻辑数据库。代价是许多写入和部分读取必须跨区域协调以保证一致性。这个额外的跨区域往返会成为常规路径的一部分,尤其是对共享记录(如账户设置和计费)。
一个常见的分阶段方案:
- 先在一个区域和单一 PostgreSQL 主库开始,测量用户与写入的实际分布。
- 为报告和非关键页面添加欧盟只读副本。
- 如果欧盟写密集流程需要更好 UX,考虑用欧盟本地的服务层或队列来保持界面响应。
- 当核心表(账户、计费、权限)需要多区域正确性时,再重新评估数据库选择。
如果你在 AppMaster 上构建,把逻辑留在可视化业务流程中可以在以后改变部署区域或数据库策略时减少痛苦。
团队常犯的错误
最大的错误是认为“多区域”自动意味着对所有人都很快。分布式数据库无法战胜物理距离。如果事务必须在两个遥远地点确认,往返时间会出现在每一次写入中。
另一个陷阱是混淆正确性预期而不承认它。团队对余额、库存和权限要求严格,但把应用的其他部分当作“差不多就行”。于是用户在一个界面看到一个数值,在下一步看到另一个数值。
需要警惕的模式包括:
- 期望所有写入都感觉本地,即便它们需要跨区域确认
- 把最终一致性当作纯粹的 UI 细节,结果打破了业务规则(退款、配额、访问控制)
- 在第一次事故后才学到运维现实(备份、升级、节点健康、区域故障)
- 低估了在跨区域分布时定位慢事务所需的时间
- 把第一次决策当作不可更改,而不是规划演化路径
迁移特别需要额外关注,因为产品在快速增长时常会做迁移。在单节点上容易的模式变更在跨多个节点和区域保持一致时可能变得有风险。
把第一次数据库选择当作一个步骤,而不是终身宿命。如果你用 AppMaster,可以快速原型工作流和数据模型,然后在提交到复杂分布式架构前验证真实延迟和故障行为。
在做决定前的快速检查清单
在你选定方向前,定义“优秀”的具体含义。多区域设置能解决真实问题,但也会迫使你持续在延迟、一致性和运维上做出选择。
简短而具体的检查清单:
- 确认你的前三个用户关键动作(例如:登录、结账、更新共享记录)以及这些用户在哪里。
- 决定哪些数据必须在区域间强一致,哪些可以容忍延迟。
- 用明文写下你的故障故事:“如果 X 区域宕机 1 小时,Y 区域的用户仍能做 A 和 B,但不能做 C。”
- 指定备份、恢复测试、升级和监控的负责人。
- 起草一个模式变更计划,使应用在分阶段发布期间保持兼容。
如果你用无代码平台如 AppMaster,把这份清单早早写进构建笔记能帮助在需求变化时保持数据模型、业务逻辑和发布步骤的一致性。
下一步:测试假设并选择构建路径
大多数团队并不需要在第一天就使用分布式数据库。他们需要可预测的行为、简单的运维和明确的扩展路径。
这个决定通常归结为一个问题:你的核心工作流是否需要在多个区域都能进行正确的主动写入?
- 如果你能把写入保留在一个主区域,并用副本、缓存或只读副本在其他区域提供服务,PostgreSQL 往往是非常合适的。
- 如果你确实需要在多个区域进行强一致的写入,分布式 SQL 可以满足,但你要接受更高的基线延迟和更多的运维复杂性。
一种实用的压力测试方法是用真实工作流做一次小规模验证。
一个短期验证计划(1–2 天)
- 测量你关心的每个区域的 p95 延迟(读和写)。
- 模拟一种故障模式(杀掉一个节点、阻断某个区域或禁用跨区域流量),记录发生了什么中断。
- 端到端运行 2–3 个关键事务(注册、结账、更新资料),观察重试、超时和用户可见错误。
- 尝试一次你预计会经常做的模式更改(添加列、添加索引)。计时并记录它会阻塞什么。
之后写下数据归属:哪个区域“拥有”客户记录?哪些表必须强一致,哪些可以最终一致(比如分析事件)?还要决定什么情况会触发后续迁移、如何回填和如何回滚。
一个常见的路径是先用 PostgreSQL 开发,保持模式干净(清晰的主键、更少的跨表写热点),并设计区域特定数据以便日后更容易拆分。
如果你使用 AppMaster,可以在 Data Designer 中建模 PostgreSQL 模式并生成可投产的应用,在你验证多区域写入是否真有必要之前部署到选定云上。如果你想探索这种方法,AppMaster 在 appmaster.io 上可以让你在不太早陷入复杂多区域架构的情况下快速原型完整栈(后端、Web 与移动)。
常见问题
先写下你想要在意外时幸存下来的具体故障(整个区域宕机、数据库节点丢失或区域间网络分割),以及在该故障期间用户仍应能做的事情。然后设定可接受的数据丢失量(RPO)和恢复时间目标(RTO)。一旦这些明确,PostgreSQL 与 CockroachDB 的权衡就更容易评估了。
如果你可以把写操作维持在一个主写区域,并能接受区域故障时做一次短暂的故障切换,PostgreSQL 往往是一个很好的默认选择。它更容易运维、招聘门槛低,并且在主区附近写入延迟通常更低。当你需要在其他区域加快读取时,可以添加只读副本,但要能容忍复制延迟。
当你确实需要在某个区域宕机时系统仍保持正确性并继续接受写入,而不需要人工提升与切换时,CockroachDB 更合适。代价是更高的写入基线延迟和额外的运维复杂性,因为数据库必须在副本间协调以保证强一致性。若多区域正确性是强制性要求而非可有可无,分布式 SQL 是合适的选择。
常见模式是一个单一 PostgreSQL 主库用于读写,其他区域部署只读副本以提升本地读取性能。将可容忍短暂过期的屏幕路由到副本,把任何对正确性敏感的请求(例如计费状态或权限)路由到主库。这样可以在不马上承担分布式写复杂性的前提下改善用户体验。
副本延迟会导致用户短时间内看到旧数据,这会在后续步骤假定最新写入已生效时造成问题。为降低风险,把关键读取保留在主库,设计能容忍短暂延迟的非关键界面,并在合适的地方加上重试或刷新提示。关键是在一开始就明确哪些功能可以“最终一致”,哪些不能。
跨大洲写入通常变慢,因为数据库需要在不同区域的副本间确认写入后才认为“完成”。区域越远,协调所需的往返时间越长,这会反映在 p95 延迟上。对于写密集型应用或涉及共享行的多步骤事务,额外的往返会非常明显。
重点看你关键用户操作的 p95 延迟,而不是平均值或合成基准。测量来自用户所在区域的真实读写时延,端到端测试几个关键工作流(注册、结账、权限变更)。还要模拟至少一种故障模式并记录用户可见的表现,因为“正常条件下可用”不等于“故障期间也可用”。
在 PostgreSQL 中,最可怕的通常是某些模式下的锁等待与写入阻塞,尤其是在大表上做会重写表的更改时。在分布式系统中,变更可能是在线的,但需要在节点间达成一致并跨区域复制,完成传播可能更慢,还会在每个区域暴露出延迟、热点或查询计划的变化。无论哪种情况,最安全的做法是分阶段、增量式的迁移,保持应用在变更过程中兼容。
在 PostgreSQL 中,整个区域宕机通常会触发一次计划内的故障切换,你提升一个副本并把应用切到新主库,期间可能会有短暂的写入暂停。在分布式数据库中,网络分割是更棘手的情形:为了保护一致性,系统可能会拒绝部分写入,直到达到法定节点数(quorum)。你的运行手册应该同时涵盖这两种事件,而不是只考虑“数据库挂了”这种情况。
可以,只要你把它当作一个演化路径而非永久决定来对待。先从单区域写模型开始,保持模式干净,按功能显式标注多区域需求,避免在关键工作流上意外依赖过期读取。如果你使用 AppMaster,可以快速迭代数据模型和业务逻辑,在近生产环境的测试中验证真实延迟和故障行为,只有在需求被验证后再迁移到更复杂的多区域方案。


