5

每秒最多 6.5 万个事务,我们的 PostgreSQL 12 迁移之路

 3 years ago
source link: https://www.infoq.cn/article/cFgLEPCtoIADyRqc310j
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2020 年 11 月,我们开始了一次大规模的迁移,将 PostgreSQL 集群从 9.6 版本升级到 12.4 版本。在这篇文章中,我将概要地介绍下我们在 Coffee Meets Bagel 采用的架构,带你看一下我们在累计停机时间不到 30 分钟的情况下完成升级所采取的步骤,并分享一些经验教训。

本文最初发布于 Coffee Meets Bagel 工程 博客,经原作者授权由 InfoQ 中文站翻译并分享。

架构

你可能不了解 Coffee Meets Bagel,我们是一个精心策划的约会应用。每天中午,约会者都会收到一定数量的高质量候选人。我们的负载模式具有非常好的可预见性。在撰写这篇文章的前一周,我们的平均交易数徘徊在每秒 3 万笔左右,而在更大的市场中,我们的最高交易数是每秒 6.5 万笔。

Ir2yIbZ.png!mobile

在升级之前,我们在 AWS i3.8xlarge 实例上运行了 6 个 Postgres 服务器。其中包扩一个主节点,三个服务于只读 Web 流量的副本(通过 HAProxy 实现负载均衡),一个专用于异步工作线程的服务器,还有一个用于 ETL 和 BI 的服务器。

我们使用 Postgres 内置的流复制来保证副本集群是最新的。

MBnMJfv.png!mobile

升级原因

在过去的几年里,在很大程度上,我们忽略了数据层,因此它们已经有点老迈了。特别是我们的主服务器已经积攒了很多问题——它在线已经有大约 3 年半的时间了,各种系统库和服务都打过补丁。

6fi2Enq.png!mobile

我提供给 r/uptimeporn 的数据

因此,它有许多让我们紧张的稀奇古怪的问题:新服务拒绝在 systemd 中运行(我们最后在一个 screen 会话中运行了一个 datadog 代理),有时当 CPU 使用率超过 50%,对 SSH 就完全无响应了(同时继续服务于查询)。

此外,我们的磁盘使用开始变得不稳定。正如我前面提到的,我们在 EC2 i3.8xlarge 实例上运行 Postgres,该实例带有 7.6TB 的 NVMe 存储。和 EBS 不同,它不能动态调整大小,所以有多大就是多大,而且磁盘的 75%已经填满。为了支持未来的增长,我们必须升级实例大小。

我们的需求

  1. 最小化停机时间——我们的目标是累计停机时间 4 小时,包括升级过程中的错误所导致的意外停机。

  2. 在新实例上构建一个新的数据库集群,以取代我们当前老化的服务器集群。

  3. 升级到 i3.16xlarge 实例,留下增长空间。

我们知道三种 Postgres 升级方法:典型的备份和还原过程、pg_upgrade 和逻辑复制。

我们很快就放弃了备份和还原方法,因为我们的数据集有 5.7TB,需要花费的时间太长了。pg_upgrade 虽然快,但它是就地升级,不满足条件 2 和 3。所以,我们下一步是进行逻辑复制。

过程

关于 pglogology 的安装和使用,已经有很多资料,所以我就不重复了,下面这些链接我认为非常有帮助:

我们创建了一个 Postgres 12 服务器,它将成为我们新的主服务器,并使用 pglogical 来同步所有数据。一旦它复制完旧数据并开始复制传入的更改,我们就在它后面添加流副本。当我们每准备好一个新的流副本,就把它添加到 HAProxy 中,同时删除一个旧的 9.6 副本。我们就这样一直做,直到除了主服务器之外的所有 Postgres 9.6 服务器都已退出,此时,我们的状态如下:

IFfAfuM.png!mobile

在这种状态下,我们就会安排一个维护窗口来执行故障切换。同样,关于这个过程,网上有很好的文档,所以这里我只粗略地描述一下我们所做的事情:

  1. 将站点置于维护模式

  2. 将主数据库的 DNS 切换到新主机的 IP

  3. 强制同步所有主键序列

  4. 在旧的主数据库上手动运行 checkpoint

  5. 执行一些数据验证,测试下新的主数据库

  6. 将站点恢复到正常运行状态

总之,对于我们来说,这个过程很顺利。虽然这是一次规模比较大的基础设施迁移,但我们没有遇到任何意外的停机或者错误。

经验教训

虽然这个项目取得了很大的成功,但我们也遇到了一些问题。最可怕的是差点搞挂我们的 Postgres 9.6 Primary…

教训 #1:同步缓慢会很危险

首先是一些关于 pglogical 的背景信息:pglogical 的工作方式是,提供方数据库(在本例中是我们的旧 9.6 Primary)上的发送方进程将解码预写日志,提取逻辑更改,并将它们发送到订阅方数据库。

如果订阅方速度慢了,那么提供方的 WAL 段就会开始累积,这样,当订阅方赶上时就不会丢失任何数据。

当你向副本流添加表时,pglogical 首先需要同步表数据。这是使用 Postgres 的 COPY 命令完成的。然后,在提供方数据库上 WAL 段将开始累积,这样,在 COPY  开始后提交的更改可以在初始同步完成后传输到订阅方,确保数据不会丢失。

也就是说,在实践中,如果你正在一个写入/更新频繁的系统中同步一个较大的表,那么你需要密切关注磁盘使用情况。我们第一次尝试同步最大的表(4TB)时,初始 COPY 语句运行了一天多,在此期间,我们在提供方节点上累积了大约 1TB 的 WAL。

你可能还记得,我们在前面提到过,我们的旧数据库服务器只剩下大约 2TB 的空闲磁盘空间。从用户数据库的磁盘使用情况来看,只有约四分之一的表复制了,我们认定,我们将无法在磁盘空间耗尽之前完成,因此我们迅速取消了同步过程。

EfuuyyF.png!mobile

在初始同步期间,旧主数据库上的可用磁盘空间

然后,我们对订阅方数据库做了以下更改,以加快同步:

  • 删除需要同步的表上的所有索引

  • 关闭 f sync

  • max_wal_size 设为 50GB

  • checkpoint_timeout  设为 1h

这四个更改显著提高了订阅方数据库摄取数据的速度,我们在第二次尝试时用不到 8 小时就完成了表的同步。

教训 #2: 每个更新都被记录为冲突

当 pglologic 断定发生了冲突时,它会发出一条日志消息,比如“ CONFLICT: remote UPDATE on relation PUBLIC.foo. Resolution: apply_remote ”。

然而,我们观察到,订阅方处理的每个更新都会被记录为冲突。仅经过几个小时的复制,订阅方数据库就已经写入了 1GB 的冲突日志。

我们在 postgresql.conf 文件中加入 pglogical.conflict_log_level = DEBUG 将其关闭。

参与 Hacker News 讨论,请点击 这里

查看英文原文Our Journey to PostgreSQL 12


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK