Retool 如何升级主应用 4TB 的 PostgreSQL 数据库
itomcoil 2025-09-03 00:05 3 浏览
本文最初发布于 Retool 官方博客。
Retool 的云托管产品基于一个在微软 Azure 云中运行的 4TB 的 Postgres 数据库。去年秋天,我们把这个数据库从 Postgres 9.6 版升级到 13 版,而且停机时间极短。
我们是如何做到的呢?坦率地说,这不是一条从 A 到 B 的直线。在这篇文章中,我将为你讲述这个故事,并分享一些可以帮助你进行类似升级的技巧。
动机
你可能不了解 Retool,我们是一个快速构建内部工具的平台。你可以使用一个可拖放编辑器来构建 UI,并轻松将它们连接到自己的数据源,包括数据库、API 和第三方工具。你可以把 Retool 作为一个云托管产品(它基于我们在这篇文章中谈到的数据库),或者你可以自己托管它。从 4TB 的数据库可以看出,Retool 的许多客户正在云上构建许多应用。
去年秋天,我们决定升级我们的主 Postgres 数据库。我们有一个令人信服的理由——Postgres 9.6 将在 2021 年 11 月 11 日到达生命周期的终点,这意味着将不再有针对这个版本的 Bug 修复或安全更新。我们不想让客户的数据有任何风险,所以我们不能继续使用这个版本。事情就这么简单。
技术设计
升级涉及一些高级决策:
- 应该升级到什么版本的 Postgres?
- 应该用什么策略进行升级?
- 应该如何测试升级?
在开始之前,我们回顾下我们的一些限制条件和目标,就只有下面这几个。
- 在 2021 年 11 月 11 日之前完成升级。
- 尽量减少停机时间,特别是在全球范围内周一至周五的工作时间里。这是截止日期之外最重要的考量因素,因为 Retool 对我们的许多客户而言至关重要。
- 当在 4TB 的数据库上操作时,停机时间是特别需要考虑的一个因素。在这种规模下,原本简单的事情也会变得比较困难。
- 我们希望维护窗口最多为一小时左右。
- 在我们不得不再次升级之前,最大限度地增加这次升级为我们赢得的时间。
PostgreSQL 13
我们决定升级到 Postgres 13,因为它符合上述所有标准,特别是最后一个标准:在下一次升级之前为我们赢得最多的时间。
当我们开始准备升级时,Postgres 13 是 Postgres 的最新版本,其支持窗口到 2025 年 11 月。我们预计,在这个支持窗口结束时,我们将把数据库分片,并逐步完成下一次重大的版本升级。
Postgres 13 还提供了一些以前版本中没有的功能。这里是完整列表。以下是最让我们兴奋的几个:
- 重大的性能改进,包括在并行查询执行方面。
- 能够安全地添加默认值非空的列,这消除了一个常见的问题。在早期的 Postgres 版本中,添加默认值非空的列会导致 Postgres 执行表重写,并阻塞并发读和写,这可能会导致停机。
- 并行索引清理。(Retool 有几个写流量很大的表,我们非常关心清理问题。)
升级策略
很好,我们已经选定了一个目标版本。现在,我们要如何实现这个目标呢?
一般来说,升级 Postgres 数据库版本最简单的方法是进行pg_dump和pg_restore。关闭应用程序,等待所有的连接终止,然后关闭数据库。在数据库处于冻结状态时,把它的内容转储到磁盘上,然后将其恢复到一个运行 Postgres 目标版本的新数据库服务器上。恢复完成后,把应用程序指向新的数据库,恢复服务。
这种升级方案很吸引人,因为它既简单,又能保证数据在新旧数据库之间完全同步。但我们马上就排除了这个选项,因为我们想尽量减少停机时间——4TB 的数据库完成转储和恢复,停机时间需要几天,而不是几小时或几分钟。
我们选择了一种基于逻辑复制的策略。通过这种方法,可以并行运行两个数据库的副本:要升级的主数据库和运行在 Postgres 目标版本上的从数据库。主数据库将持久性存储的变化(通过解码其预写日志)发布到从数据库上,使得从数据库可以快速复制主数据库的状态。这有效地消除了在 Postgres 目标版本上等待数据库恢复的时间,而且,目标数据库始终是最新的。
值得注意的是,这种方法需要的停机时间比“转储和恢复”策略少得多。我们不需要重建整个数据库,只需要停止应用,等旧的 v9.6 主数据库完成所有事务,再等 v13 主数据库复制完最新数据,就可以将应用指向主数据库。这可以在几分钟内完成,而不是几天。
测试策略
我们维护一个 Retool 云实例的过渡环境。我们的测试策略是在这个过渡环境上运行多次测试,并在这个过程中编制一份详细的操作手册并反复修改完善。
测试运行和操作手册都对我们很有帮助。在下一节中你将看到,我们在维护窗口期间执行了许多手动步骤。在最后切换时,这些步骤基本上都顺利完成,因为我们在前几周进行了多次彩排,这帮助我们创建了一个非常详细的操作手册。
我们的主要疏忽是没有在过渡环境中用一个有代表性的工作负荷进行测试。过渡数据库比生产数据库小,即使理论上讲,我们可以借助逻辑复制策略处理更大的生产工作负载,但我们遗漏了一些细节,导致 Retool 的云服务出现中断。我们将在下面的章节中进行详细介绍,但这里我们希望传达的最重要的信息是:用有代表性的工作负载进行测试非常重要。
实施计划:技术细节
实现逻辑复制
我们最终使用了Warp。值得注意的是,Azure 的单服务器 Postgres 产品不支持Postgres扩展pglogical,而我们的研究使我们相信,这是 Postgres 在 10 版本之前实现逻辑复制的最佳选项。
我们初期走过的一个弯路是尝试Azure的数据库迁移服务(DMS)。DMS 会首先对源数据库做个快照,然后将其恢复到目标数据库服务器。一旦初始转储和恢复完成,DMS 就会启动逻辑解码,这是 Postgres 的一项功能,将数据库的持久性变化发送给外部订阅者。
然而,在我们 4TB 的生产数据库上,最初的转储和恢复一直未能完成:DMS 遇到了一个错误,但没有向我们报告。与此同时,尽管没有取得任何进展,DMS 还是在我们的 9.6 主数据库中保留了事务。这些长期运行的事务反过来又阻碍了 Postgres 的自动清理功能,因为vacuum进程不能清理长期运行的事务开始后产生的死亡元组。随着死亡元组的堆积,9.6 主服务器的性能开始受到影响。这导致了我们上面提到的故障。(我们后来增加了监控,跟踪 Postgres 中未清理元组的数量,使我们能够主动发现危险的情况)。
Warp 的功能与 DMS 类似,但提供更多的配置选项。特别是 Warp 支持并行处理,可以加速初始转储和恢复。
Warp 希望所有的表都有一个单列主键,所以我们不得不做一些小动作来骗过它。我们把复合主键转换成唯一约束,并增加标量主键。除此之外,Warp 非常简单易用。
跳过大表复制
我们进一步优化了我们的方法,让 Warp 跳过两个特别大的表,这两个表左右了转储和恢复的运行时间。我们这样做是因为 pg_dump 不能在单个表上进行并行操作,所以最大的表将决定最短迁移时间。
为了处理在 Warp 中跳过的两个特别大的表,我们写了一个Python脚本,将数据从旧数据库服务器批量转移到新服务器。其中比较大的一个表有 2TB,这是应用程序中一个仅限追加的事件审计表,它很容易迁移:我们等切换后再迁移其中的数据,因为即使该表是空的,Retool 产品的功能也不受影响。我们还选择将非常老的审计事件转移到一个备份存储解决方案中,以减少表的大小。
另一个表是一个仅限追加的大小几百 GB 的日志表,记录了对所有 Retool 应用的所有编辑(即 page_saves),这个表比较棘手。这张表是所有 Retool 应用的真相来源,所以在维护完成的那一刻需要保证最新。为了解决这个问题,我们在维护窗口前的几天里迁移了大部分内容,并在窗口期间迁移了剩余的内容。虽然这个方法很有效,但我们注意到,它确实增加了风险,因为我们现在有更多的工作要在有限的维护窗口内完成。
编制操作手册
以下是我们在维护窗口期间执行的一个大概的步骤:
- 停止 Retool 服务,等待所有未完成的数据库事务提交。
- 等待 Postgres 13 从数据库赶上逻辑解码的进度。
- 同时,把剩余的 page_saves 行复制过来。
- 在所有数据都迁入 Postgres 13 服务器后,启用主键约束(Warp 要求禁用这些约束)。
- 启用触发器(Warp 要求禁用触发器)。
- 重置所有的序列值,这样一旦应用程序重新上线,就可以使用序列分配整数主键了。
- 重新上线 Retool 服务,指向新的数据库,执行健康检查。
启用外键约束
从上面的操作手册中可以看到,我们必须做的一个步骤是禁用然后重启外键约束检查。复杂之处在于,默认情况下,Postgres 在启用外键约束时,会运行一次全表扫描,以验证当前所有的行是否满足新的约束。对于大型数据库来说,这是一个问题:Postgres 根本不可能在一小时的维护窗口内扫描数 TB 的数据。
为了解决这个问题,我们最终选择在几个大表中不启用外键约束。我们认为这可能没什么问题,因为 Retool 的产品逻辑会执行它自己的一致性检查,而且也不会从被引用的表中删除,这意味着我们不太可能留下一个悬空的引用。尽管如此,这也是一种风险;如果我们的推理不正确,那么我们最终会有一堆无效的数据需要清理。
后来,在维护窗口结束后的清理工作中,我们恢复了当时去掉的外键约束。我们发现,Postgres 为我们的问题提供了一个干净的解决方案:ALTER TABLE的NOT VALID选项。添加一个带有 NOT VALID 选项的约束,Postgres 就会只对新数据执行约束验证,对现有数据则不执行,这样就可以绕过昂贵的全表扫描。之后,只需要运行 ALTER TABLE ... VALIDATE CONSTRAINT,它就会运行全表扫描并从约束中删除 NOT VALID 选项。当我们这样做的时候,发现表中并没有无效的数据,这让我们松了一口气。要是我们在维护窗口之前就知道这个选项就好了。
小结
我们把维护窗口安排在 10 月 23 日(星期六)晚些时候,在 Retool 云流量最低的时间段。通过上述配置,我们能够在大约 15 分钟内新建一个 13 版本的数据库服务器,并通过逻辑解码订阅我们 9.6 版本主服务器的变化。
最后,在 Warp 的帮助下,通过逻辑复制策略,以及在测试环境中的彩排,我们编制了一份可靠的操作手册,使我们能够将 4TB 的数据库从 Postgres 9.6 迁移到 13。在这个过程中,我们认识到了在真实的工作负载上进行测试的重要性,创造性地跳过了不那么关键的大表,并且了解到(有点晚),Postgres 允许我们选择只对新数据执行外键约束验证,而不是所有数据。希望你能从我们的经验中学到一些东西。
了解更多软件开发与相关领域知识,点击访问 InfoQ 官网:https://www.infoq.cn/,获取更多精彩内容!
相关推荐
- Postgres是个六边形战士,其他很多工具是没必要的
-
你只是想部署一个简单应用,一个仪表盘、一个聊天工具,或者个人项目。突然之间,你得启动Redis做缓存,上Elasticsearch做搜索,用Firebase实现实时同步,配置无服务器函数的...
- PostgreSQL开发与实战(5)备份管理pg_probackup
-
作者:太阳一、pg_probackup概述pg_probackup是一款免费的postgres数据库集群备份工具,与其他备份工具相比,它主要有如下一些优势:提供增量备份,增量备份一定程度上可以节省磁...
- 了解 PostgreSQL 的 MVCC 可见性基本检查规则
-
1.引言根据VadimMikheev的说法,PostgreSQL的多版本并发控制(MVCC)是一种“在多用户环境中提高数据库性能的高级技术”。该技术要求系统中存在同一数据元组的多个“版本”,...
- PostgreSQL初学者需要知道一些技巧
-
PostgreSQL作为一个优雅而且稳健的数据库越来越被大家所采纳,除了Mysql以外很多人都开始接触并学习PostgreSQL。但是PostgreSQL和传统的一些数据库系统还有有点点小小的鸿沟,有...
- Python - 操作 PostgreSQL 数据库的基本方法与代码
-
本文记录在Python编程中操作PostgreSQL数据库的基本方法与实现代码,包括连接数据库、创建数据表、插入/更新/删除数据表中的记录、函数调用、存储过程调用及事务处理等。获取数据库首先把连接数据...
- 详解PostgreSQL 12.2时间点恢复 (PITR)
-
概述PostgreSQL提供了不同的方法来备份和恢复数据库,可以是某一时刻数据库快照的完整备份或增量备份,可以使用SQL转储或文件系统级别的备份,在增量备份的基础上还可以实现基于时间点恢复。这...
- PostgreSQL 17即将发布,新功能Top 3
-
按照计划,PostgreSQL17即将在2024年9月份发布,目前已经发布了第三个Beta版本,新版本的功能增强可以参考ReleaseNotes。本文给大家分享其中3个重大的新...
- PostgreSQL从入门到精通教程 - 第39讲:数据库完全恢复
-
PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注...
- PostgreSQL建库说明_postgresql能创建多少表
-
1.数据库逻辑结构介绍在一个PostgreSQL数据库系统中,数据库:一个PostgreSQL数据库服务下可以管理多个数据库,当应用连接到一个数据库时,一般只能访问这个数据库中的数据,而不能访问其他...
- 为什么delete后磁盘空间没有释放而truncate会释放?
-
背景因项目需求,需要清理一批旧数据,腾出空间给新数据,让同事负责这件事。料想会很顺利,但很快找到我,并告知在postgresql中把一张大的数据表删除掉了,查询表的size并没有改变。我震惊了,问他怎...
- PostgreSQL技术内幕6:PostgreSQL索引技术
-
0.简介本文主要介绍PG的索引技术,包含PG支持的索引类型,语法,查看方式,以及其中B-Tree索引的原理解析和源码解读。1.PG索引类型介绍PG支持多种索引类型:B-tree、Hash、GiST、...
- PostGreSQL历史_postgresql序列查询
-
1973UniversityINGRES(起源于IBMSystemR的一系列文档,MichaelStonebrakerandEugeneWong)1982INGRES1985...
- Retool 如何升级主应用 4TB 的 PostgreSQL 数据库
-
本文最初发布于Retool官方博客。Retool的云托管产品基于一个在微软Azure云中运行的4TB的Postgres数据库。去年秋天,我们把这个数据库从Postgres9.6...
- ArcGIS Desktop直连PostgreSQL安装及配置图解(windows)
-
目录1PostgreSQL11.0安装及配置2psqlODBC安装及配置3PostGIS安装及配置4pgAdmin4使用入门5空间数据导入5.1将PostgreSQL的bin文件路径添加...
- 基于patroni+etcd打造可自动故障转移的PostgreSQL集群
-
作者:杭州美创科技有限公司得益于PostgreSQL的开源特性,越来越多的第三方集群管理软件填补了PostgreSQL在集群方面的易用性和可靠性,patroni+etcd提供了一系列的集群管理方案。e...
- 一周热门
- 最近发表
-
- Postgres是个六边形战士,其他很多工具是没必要的
- PostgreSQL开发与实战(5)备份管理pg_probackup
- 了解 PostgreSQL 的 MVCC 可见性基本检查规则
- PostgreSQL初学者需要知道一些技巧
- Python - 操作 PostgreSQL 数据库的基本方法与代码
- 详解PostgreSQL 12.2时间点恢复 (PITR)
- PostgreSQL 17即将发布,新功能Top 3
- PostgreSQL从入门到精通教程 - 第39讲:数据库完全恢复
- PostgreSQL建库说明_postgresql能创建多少表
- 为什么delete后磁盘空间没有释放而truncate会释放?
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)