让 Postgres 实现扩展
你可以让 Postgres 实现扩展
2025 年 3 月 13 日 Lev Kokotov Postgres 可以扩展。 没有任何其他词语像它一样,引发了如此多的争议。 至少在我所处的圈子里是这样的,在公司地下室里,负责基础设施的精灵们让那个 Rails 应用 brrr 地运行。 很多人都相信,尽管有 Big NoSQL 的各种营销活动,但你所掌握的技术仍然比你在工程领导会议上听到的那些吹嘘要好。
公平地说,我理解他们的想法。 让 Postgres 写入更多数据可能具有挑战性。 你需要更多的硬件。 大多数时候,你可以通过点击“升级”按钮来获得它。 一旦你达到了 r5.24xlarge 实例,拥有 5 个同样重要的副本,并且你的 vacuum
仍然滞后,事情就会变得非常可怕。
这就是真正工程师的考验开始的地方。 在边缘。 需要明确的是,我不是在谈论 WebAssembly。 我说的是那种工程精神,它会在执行压力下审视一个问题,而不是跑到最近的销售团队那里,听他们做出美好的承诺(但对你的用例知之甚少),而是使用第一性原理来解决它。
第一性原理告诉我们所需要的东西。 Postgres 的写入能力不足了。 要么是因为 WAL 上的锁竞争,要么是某个东西卡住了 vacuum
。 可能是那个在应用程序进行 Stripe 调用时已经打开 45 秒的空闲事务,但这并不是我们所关心的。 我们是基础设施团队,我们的工作是让数据库正常运行。
因此,我们作为一群“胶带爱好者”共同决定,我们将给予 Postgres 它想要的东西:更多的机器。 我们将把写入工作负载平均分配到 3 个(不,让我们用 6 个,以确保安全)数据库之间。 如果“多多益善”的理论是正确的,那么我们应该获得 6 倍的写入速度。
选择的道路
写下这些很有趣。 互联网上至少有 1 篇(或者可能 2 篇)内容丰富的博客文章,讲述了如何做到这一点。 有趣的是,因为它们描述了一个大约 5 人的团队所做的大量工作。 该团队遍历了每个 ActiveRecord 和 psycopg2 的 _execute_
调用,以确保它包含一个分片键。
我们接手了一个运行失败的数据库,它当时每秒服务大约 100,000 名用户,运行了数十个看起来很可怕(并且大多没有文档记录)的命令,以使其保持该复制槽的打开状态,同时我们对它进行快照,以 6 种不同的方式恢复它,并截断大部分数据以使其与新的分片方案同步。
我们使用逻辑复制同步它,这花了一两天的时间。 这些数据库非常宝贵,一次意外写入可能会破坏数据完整性。 因此,该团队添加了触发器,以验证进入每个表的行。 我们选择了 Postgres 自 10 版本以来就自带的哈希函数,这是我们在邮件列表的深处找到的。 这是一个我们可以调用来手动验证行或重新分片数据的函数。
数据已被分片,但我们没有告诉应用程序如何找到它。 它以前在一个数据库中,现在在 6 个数据库中。 幸运的是,Postgres 是开源的。 我们找到了该哈希函数的代码,用 Ruby 重写了它,并将其插入到 _ApplicationRecord_
类中。 每次有人执行查询时,该代码都会哈希分片键,并将查询发送到 _database.yml_
中配置的实例之一。 如果是 SELECT,它将获得特殊处理,并将针对 5 个副本进行负载平衡。
Python 得到的待遇不太好,它使用了一个我们使用分区外部表创建的特殊协调器。 这也将服务于我们无法及时解决的 1% 的跨分片查询。 Postgres 有时既可怕又多才多艺。
该团队为每个分片创建了 5 个副本。 这次我们可不是在开玩笑。 我们再也不想做这个手动过程了。 这应该可以让他们再坚持...几年。 我们不知道有多少年,因为我们以前从未见过这样的增长,但是我们可以随时添加更多副本。 但是,添加更多分片是以后要解决的问题。 我们知道我们可以,因为我们已经走了这么远,而且我们的工程精神很强大,但我们只是想完成这件事。
如果你和我一起数,我们现在有 36 个数据库。 有足够的空间再次增长 6 倍。
现在是编写 Pgbouncer 配置的时候了。 糟糕的是,它无法为我们进行流量负载平衡,但这没关系。 我们有一个 ActiveRecord 插件可以解决这个问题,而 Python 应用程序可以直接使用 _replica-5-prod_
。 现在是晚上 10:45,距离我们与业务人员协商的维护窗口还有 2 个小时,他们仍然不明白为什么我们需要每隔一周将整个应用程序离线几个小时。
切换计划很简单。 关闭数据库并将应用程序切换到新的数据库。 足够简单。
时间到了。 Datadog、Rollbar 和 CloudWatch 警报熟悉的协奏曲开始响起。 PagerDuty 正在响铃。 不过,没有人被吵醒,我们都已经在这里了。 我们忘记设置该维护窗口了,但是现在没有时间了。 业务已离线,我们有大约 5 分钟的时间来切换配置,手动测试一切正常,然后重新打开流量。
有人编写了一个 Bash 脚本来一次重新加载所有 bouncer。 为那个人点赞,这是个好主意。 基础设施工程师使用 Bash 来做任何事情。 第一性原理。
应用程序已准备就绪,我们运行了一些查询和健全性检查以确保一切顺利。 我们正要重新打开开关,这时一位工程师悄悄地问(毕竟现在是凌晨 2 点):
“嘿,伙计们,我们是否增加了序列?”
糟糕。 逻辑复制不处理这个小细节,但是没关系,我们仍然有上次执行此操作时的 PL/pgSQL 脚本。 快速复制/粘贴到超级用户 psql 会话中,我们又恢复了业务,有足够的空间来发展公司,并且有一个精彩的故事。
Postgres 的理由
好吧,这可能不是最令人愉快的销售宣传。 对于怀疑论者来说,这真的可能走向任何一个方向,也许 DynamoDB 是他们的下一个全新项目的更好选择。 但是,如果你仍在阅读此书,那么你刚刚看到了 Postgres 如何分片的路线图。 这不仅是可能的,而且实际上是相当可行的,而且已经完成了。
我们现在真正需要的是一段可以为我们完成此操作的代码。 最好是在后台自动执行,而我们正忙于编写更多的 ActiveRecord 查询来销售东西。 出于这种信念,并且因为扩展 Postgres 应该在午餐后按一下按钮即可完成,所以我们编写了 PgDog。
为了你,也因为我们可以。 我们相信,这是对“Postgres 可以扩展吗?”这个问题的回答。 是的,它可以。 只要我们这些工程师让它这样做,它就会这样做。
取得联系
如果以上任何内容让你感到熟悉,或者你只是对该项目感到好奇,请与我们联系。 PgDog 是一个用于分片 Postgres 的开源项目。 © 2025 PgDog, Inc. 保留所有权利。