不要更新!发布当日叫停:PG也躲不过大翻车

老话说的好,不要在星期五发布代码。前天刚发布的 PostgreSQL 例行小版本虽然特意避开了星期五发布,但却给社区加了一周的活 —— PostgreSQL 社区将于下周四发布一个非常规紧急小版本 PostgreSQL 17.2,16.6, 15.10,14.15,13.20,甚至是刚刚已经 EOL 的 PG 12 也会有 12.22…… 。

在过去十年里这是第一次出现这样的情况:在 PostgreSQL 发布日的当天,新版本就因为社区发现的问题而叫停。紧急发布的原因有两个,第一是修复 CVE-2024-10978 安全漏洞,这个不是大问题,真正的原因是:PostgreSQL 新的小版本修改了 ABI,导致依赖 ABI 的扩展崩溃 —— 比如 TimescaleDB。

关于 PostgreSQL 小版本 ABI 兼容性的问题,在今年六月 PGConf 2024 上,Yuri 在扩展峰会上和《Pushing boundaries with extensions, for extension》的演讲中其实已经抛出过这个问题,但并没有得到过多的关注。结果这次结结实实的爆炸了,我猜 Yuri 看到这个新闻肯定会耸耸肩说:Told you so。

总之,PG 社区强烈建议大家 不要 在最近一周升级 PostgreSQL,Tom Lane 提出的建议是在下周四紧急发布一个非常规小版本集回滚这些变化,然后覆盖老的 17.1,16.5,…… 将这些问题版本视作 “不存在”。所以,原定于这两天的发布,默认使用最新版本的 PostgreSQL 17.1 的 Pigsty 3.1 也会相应延期一周发布。

总体来看,我觉得这件事的影响是正面的。首先这并非内核核心本身质量的问题,其次因为发现的足够早,在发布当天就发现了并及时叫停,没有对用户产生实质影响 —— 不会像其他那些数据库/芯片/操作系统漏洞一发现已经爆炸一大片了。 除了极个别的狂热更新爱好者或者新装机的倒霉蛋,应该不会有多大影响。就好比上次 xz 后门事件,也是 PG 核心开发者 Peter 在PG测试中发现的,从侧面反映出了 PG 生态的活力与洞察力。


发生了什么

11月14号早上,PostgreSQL Hacker 邮件列表中出现了一封邮件,提到新的小版本实际上打破了 ABI 。这对于 PostgreSQL 数据库内核本身并不是什么问题,然而 ABI 的变化打破了 PG 内核与扩展插件的约定,导致像 TimescaleDB 这样的扩展无法在新的 PG 小版本上正确运行。

PostgreSQL 的扩展插件是针对具体操作系统发行版上的大版本提供的。例如,PostGIS ,TimescaleDB,Citus 会针对 PG 12,13,14,15,16,17 这样每年发布的大版本号进行构建。针对 PG 16.0 构建的扩展,大家都默认可以在 PG 16.1,16.2,…… 16.x 上继续使用。 这意味着你可以滚动升级 PG 内核的小版本,而不用担心扩展插件翻车。

然而这并不是一个明确的承诺,而是社区的隐性默契 —— ABI 属于内部实现细节,也不应该有这样的承诺与期待,PG 只是在过去表现的太好了,而大家已经习惯了这一点,将其默认作为了工作假设,并体现在包括 PGDG 仓库包命名,安装脚本等各个方面。

不过这一次,PG 17.1 以及反向移植到 16 - 12 的小版本修改了一个内部结构体的大小,这会导致 —— 针对 PG 17.0 编译的扩展插件在 17.1 上使用时,有概率发生冲突,导致非法写入或程序崩溃。请注意,这个问题对使用 PostgreSQL 内核本身的用户并没有影响,PostgreSQL 在内部有断言来检查这种情况。

然而对于使用 TimscaleDB,这样扩展插件的用户来说,这意味着如果你没有使用针对当前小版本重新编译的扩展插件,将会存在这样的安全隐患。从目前 PGDG 仓库的维护逻辑上来看,扩展插件只会在新的扩展版本出来时,针对当下最新的 PG 小版本进行编译。

关于 PostgreSQL ABI 的问题,来自 CrunchyData 的 Marco Slot 写了一篇详细的推文来解释。供专业读者阅读参考。

https://x.com/marcoslot/status/1857403646134153438


如何规避这样的问题

正如之前我在《PG神功大成,最全PG扩展仓库》中所说,我针对 EL 与 Debian/Ubuntu 维护了一个包含许多 PG 扩展插件的仓库,占了整个 PG 生态近一半的扩展。

PostgreSQL ABI 的问题,其实 Yuri 之前提到过。只要你的扩展插件是针对当前使用小版本的 PostgreSQL 编译的,就不会有问题。所以每当新的小版本发布时,我都会重新编译打包这些扩展插件。


上个月,我刚刚针对 17.0 编译完了所有的扩展插件,这几天正在针对编译 17.1 的版本启动更新,结果看上去不用做了,17.2 回滚 ABI 变化,虽然意味着 17.0 上编译的扩展可以继续用,但我还是会在 17.2 后释出后,重新针对 PG 17.2 与其他主版本重新编译打包。

如果你是习惯于从互联网在线安装 PostgreSQL 与扩展插件,并且没有及时升级小版本的习惯,那么确实会有这样的安全隐患 —— 即你新安装的扩展并非针对老版本的内核编译,遇到 ABI 冲突而翻车。


老实说,我很早就在真实世界见到过这个问题,这也是为什么我在开发 Pigsty 这个开箱即用的 PostgreSQL 发行版时,从 Day 1 就选择了先将所有所需软件包及其依赖下载到本地,构建一个本地软件源,然后给环境中所有需要的节点提供 Yum/Apt 仓库的方式进行安装。这样做能够确保:整个环境中所有的节点安装的都是同样的版本,而且是一个一致性快照 —— 扩展的版本与内核的版本是匹配的。

而且,这样做还可以实现“自主可控”的需求,这意味着当你的部署上线之后,你不会遇到这种SB事情 —— 原本的软件源关停或者挪窝了,或者仅仅是上游仓库发布了一个不兼容的新版本或者新依赖,就会导致你新装机器/实例的时候遇到大翻车卡在这里。这意味着你有进行复制/扩容的完整软件副本,有能力让你的服务运行到地老天荒,而不用担心被人 “真·卡了脖子”。


比如最近 17.1 发布的时候,RedHat 赶在两天前更新了 LLVM 默认的版本,从 17 到 18,而且好死不死的只更新了 EL8 没有更新 EL9,如果用户选择在这个时候从互联网上游安装,就会直接失败。我给 Devrim 提了这个问题后,他花了两个小时修复,把 LLVM-18 加入到 EL9 专用补丁Fix仓库。

PS:如果你不知道这个独立的仓库,那你大概在修复后也会继续遇到翻车,直到 RetHat 自己修复这个问题,但 Pigsty 就会替你处理好所有这些肮脏的细节。


有人说我用 Docker 也能解决这样的版本问题,确实没错。只不过 用 Docker 跑数据库还会有其他的问题,而且,这些 Docker 镜像容器里其实本质上也是在 Dockerfile 里用操作系统的包管理器,从官方软件源给你下载 RPM/DEB 包来安装的。说到底,这些活总是要有人来做的 ……。

当然,适配不同操作系统意味着很大的维护工作量。例如,我维护了 143 个 EL 和 144 个 Debian 中的 PG 扩展插件,每个扩展插件都要针对 10 个操作系统大版本(el 8/9,Ubuntu 22/24,Debian 12,五个大系统,amd64 与 arm64),与 6 个数据库大版本(PG 17-12)进行编译,这些要素的排列组合意味着总共将近有一万个软件包需要构建/测试/分发,其中还有二十个一编译就半小时过去的 Rust 扩展……。不过老实说,反正都是半自动化流水线,从一年跑一次变成3个月跑一次,也不是不能接受。


附:关于 ABI 的问题的解释

关于最新补丁版本(17.1、16.5 等)中的 PostgreSQL 扩展 ABI 问题

PostgreSQL 扩展的 C 代码会包含来自 PostgreSQL 本身的头文件。当扩展被编译时,头文件中的函数在二进制文件中表示为抽象符号。这些符号在扩展加载时根据函数名链接到实际的函数实现。这样,一个针对 PostgreSQL 17.0 编译的扩展通常仍然可以加载到 PostgreSQL 17.1 中,只要头文件中的函数名和签名没有改变(即应用程序二进制接口或 “ABI” 是稳定的)。

头文件还声明了传递给函数的结构体(以指针形式)。严格来说,结构体的定义也是 ABI 的一部分,但其中有更多的细微之处。编译后,结构体主要由其大小和字段的偏移量定义,因此例如名称的改变不会影响 ABI(虽然会影响 API)。大小的改变会稍微影响 ABI。大多数情况下,PostgreSQL 使用一个宏(“makeNode”)在堆上分配结构体,它会查看结构体的编译时大小,并将字节初始化为 0。

在 17.1 中出现的差异是,向 ResultRelInfo 结构体中添加了一个新的布尔值,这增加了其大小。接下来发生的事情取决于谁调用了 makeNode。如果是 PostgreSQL 17.1 的代码,那么它会使用新的大小。如果是一个针对 17.0 编译的扩展,那么它会使用旧的大小。当它使用旧大小分配的指针调用 PostgreSQL 函数时,PostgreSQL 函数仍然假定新的大小,并可能写入超出已分配块的区域。一般来说,这是相当有问题的。它可能导致字节被写入不相关的内存区域,或者程序崩溃。

在运行测试时,PostgreSQL 有内部检查(断言)来检测这种情况并抛出警告。然而,PostgreSQL 使用自己的分配器,总是将分配的字节数向上取整到 2 的幂次方。ResultRelInfo 结构体是 376 字节(在我的笔记本电脑上),因此它会向上取整到 512 字节,变更后也是如此(384 字节在我的笔记本电脑上)。因此,通常这个特定的结构体改变实际上并不影响分配大小。可能存在未初始化的字节,但通常通过调用 InitResultRelInfo 来解决。

这个问题主要在扩展中分配 ResultRelInfo 的测试或启用断言的构建中引发警告,特别是在使用针对旧 PostgreSQL 版本编译的扩展二进制文件运行这些测试时。不幸的是,故事并未就此结束。TimescaleDB 是 ResultRelInfo 的重度用户,并且确实遇到了大小变化带来的问题。例如,在其某个代码路径中,它需要在一个 ResultRelInfo 指针数组中找到索引,为此它进行了指针运算。这个数组是由 PostgreSQL 分配的(384 字节),但 Timescale 二进制文件假定为 376 字节,结果是一个无意义的数字,进而触发断言失败或段错误。 https://github.com/timescale/timescaledb/blob/2.17.2/src/nodes/hypertable_modify.c#L1245…

这里的代码实际上并没有错误,但与 PostgreSQL 的契约并非如预期的那样。这对我们所有人都是一个有趣的教训。其他扩展中也可能存在类似的问题,尽管没有多少扩展像 Timescale 这样高级。另一个高级扩展是 Citus,但我进行了验证,发现 Citus 是安全的。它确实会显示断言警告。建议大家保持谨慎。最安全的做法是确保扩展使用您正在运行的 PostgreSQL 版本的头文件进行编译。

PostgreSQL 12 过保,PG 17 上位

根据 PostgreSQL 的 版本策略,在 2019 年发布的 PostgreSQL12 将于今日(2024-11-14)正式脱离支持生命周期。

PG 12 最后一个小版本为 2024-11-14 发布的 12.21,而这将是 PG 12 的最终版本,而新发布的 PostgreSQL 17.1 则将成为当下合适的新业务选择。

Version Current minor Supported First Release Final Release
17 17.1 Yes September 26, 2024 November 8, 2029
16 16.5 Yes September 14, 2023 November 9, 2028
15 15.9 Yes October 13, 2022 November 11, 2027
14 14.14 Yes September 30, 2021 November 12, 2026
13 13.17 Yes September 24, 2020 November 13, 2025
12 12.21 No October 3, 2019 November 14, 2024

PG12下台

在过去五年中,PG 12 的上一个小版本 PostgreSQL 12.20 相对于五年前发布的 PostgreSQL 12.0 修复了 34 个安全问题,936 个 Bug。

eol.png

这次发布的停产版本 12.1 修复了四个 CVE 安全漏洞,并进行了 17 项 Bug 修复,从此之后,PostgreSQL 12 就停产了,不再提供安全和错误修复

  • CVE-2024-10976:以下 PostgreSQL 行安全性(例如子查询)忽略了用户 ID 更改
  • CVE-2024-10977:PostgreSQL libpq 保留了来自中间人的错误消息
  • CVE-2024-10978:PostgreSQL SET ROLE、SET SESSION AUTHORIZATION 重置为错误的用户 ID
  • CVE-2024-10979:PostgreSQL PL/Perl 环境变量更改执行任意代码

随着时间推移,运行老版本带来的风险将会持续上升, 请仍然在生产环境中使用 PG 12 或更早版本的用户制定升级计划,升级到到受支持的大版本(13-17)

PostgreSQL 12 是五年前发布的版本,我认为是继 PG 10 之后的一个具有里程碑意义的版本。主要是 PG 12 引入了可插拔存储引擎的接口,允许第三方开发新的存储引擎。此外,还有一些重要的可观测性/易用性改进也发生在这个版本 —— 例如实时报告各种任务的进度,使用csvlog格式便于处理分析;此外,分区表也有了显著的性能改善,趋于成熟。

version-map.png

当然,我对 PG 12 印象比较深刻的原因是,当我做 Pigsty 这个开箱即用的 PostgreSQL 数据库发行版时。第一个公开发布支持的大版本就是 PostgreSQL 12。现在一眨眼五年过去了,当时的从 PG 11 适配 PG 12 新特性的回忆还历历在目。

在这五年里,Pigsty 从一个自己用的PG监控系统/测试沙箱,变成了一个被广泛使用的开源项目,在全球社区都有了知名度。回头看看,不禁有些感慨。


PG17上位

一个版本的死去也对应着另一个版本的新生。按照 PG 版本策略,今天的例行季度小版本发布,将会发布 17.1 。

featured.jpg

我的朋友 Qunar 帅龙喜欢在 PG 新版本出来时立刻跟进升级,我自己的习惯则是在大版本出来后,额外观察等待一个小版本。

因为通常来说,新的大版本发布后,大量小瑕疵小修小补都会在 x.1 中得到解决,二来三个月的缓冲区,足够让 PG 生态中的扩展插件跟进并完成适配,对新的大版本提供支持,而这对于 PG 生态用户来说是非常重要的。

从 PG 12 到现在的 PG 17,PG 社区添加了 48 项新功能特性,并提出了 130 项性能改进。特别是 PostgreSQL 17 的写入吞吐,按照官方的说法在一些场景下,相比先前版本有高达两倍的提升,还是很值得升级的。

pg17-qps.png

https://smalldatum.blogspot.com/2024/09/postgres-17rc1-vs-sysbench-on-small.html

之前我对 PostgreSQL 14 进行过一次全方位的 性能评测,但那已经是三年前了,所以我准备针对最新的 PostgreSQL 17.1 重新进行一次评测。

最近我整了台非常牛逼的物理机,128C 256G,配四块 3.2 T Gen4 NVMe SSD 加一块硬件 NVMe RAID 加速卡,准备看看 PostgreSQL,pgvector,以及一系列 OLAP 扩展插件能在这台性能怪兽上表现出什么样的性能,结果敬请期待。

总的来说,我认为 17.1 的推出将会是一个合适的升级时机,我也准备在未来几天里发布 Pigsty v3.1 ,在里面将 PG 17 升级为 Pigsty 默认使用的主要大版本,取代原本的 PG16。

考虑到 PostgreSQL 在 10.0 之后提供了逻辑复制的功能特性,而 Pigsty 提供了使用逻辑复制进行不停机的蓝绿部署升级的完整方案 —— PG 大版本升级的难度早已今非昔比。我也将会在近期推出一个不停机大版本升级教程,帮助用户将现有的 PostgreSQL 16 或更低版本无缝升级到 PG 17


PG17扩展

让我很欣慰的一点是,相比于从 PG 15 到 PG 16 的升级,这一次 PostgreSQL 扩展生态的跟进速度相当之快,体现出了强大的活力。

例如,去年 PG 16 在九月中旬发布,但是主要的扩展插件要到半年后才基本齐全 —— 比如 PG 生态的一个核心扩展 TimescaleDB 就等到二月初的 2.13 才完成 PG 16 支持, 其他的扩展也大体类似。

因此在 PG 16 发布半年后,才到达了一个基本令人满意的状态。Pigsty 也是在那时将 PG 16 提升为 Pigsty 首要使用的默认大版本,替代 PG 15。

而这一次从 PG 16 到 PG 17 的替换,生态适配的速度显著加快了,三个月不到就完成了之前需要半年的活计,比 PG 15 到 16 的速度快了近一倍。

版本 发布时间 摘要 地址
v3.1.0 2024-11-20 PG 17 作为默认大版本,配置简化,Ubuntu 24 与 ARM 支持 WIP
v3.0.4 2024-10-30 PG 17 扩展,OLAP 全家桶,pg_duckdb v3.0.4
v3.0.3 2024-09-27 PostgreSQL 17,Etcd 运维优化,IvorySQL 3.4,PostGIS 3.5 v3.0.3
v3.0.2 2024-09-07 精简安装模式,PolarDB 15支持,监控视图更新 v3.0.2
v3.0.1 2024-08-31 例行问题修复,Patroni 4支持,Oracle兼容性改进 v3.0.1
v3.0.0 2024-08-25 333个扩展插件,可插拔内核,MSSQL,Oracle,PolarDB 兼容性 v3.0.0
v2.7.0 2024-05-20 扩展大爆炸,新增20+强力扩展插件,与多款Docker应用 v2.7.0
v2.6.0 2024-02-28 PG 16 作为默认大版本,引入 ParadeDB 与 DuckDB 等扩展 v2.6.0
v2.5.1 2023-12-01 例行小版本更新,PG16重要扩展支持 v2.5.1
v2.5.0 2023-09-24 Ubuntu/Debian支持:bullseye, bookworm, jammy, focal v2.5.0
v2.4.1 2023-09-24 Supabase/PostgresML支持与各种新扩展:graphql, jwt, pg_net, vault v2.4.1
v2.4.0 2023-09-14 PG16,监控RDS,服务咨询支持,新扩展:中文分词全文检索/图/HTTP/嵌入等 v2.4.0
v2.3.1 2023-09-01 带HNSW的PGVector,PG 16 RC1, 文档翻新,中文文档,例行问题修复 v2.3.1
v2.3.0 2023-08-20 主机VIP, ferretdb, nocodb, MySQL存根, CVE修复 v2.3.0
v2.2.0 2023-08-04 仪表盘 & 置备重做,UOS 兼容性 v2.2.0
v2.1.0 2023-06-10 支持 PostgreSQL 12 ~ 16beta v2.1.0
v2.0.2 2023-03-31 新增 pgvector 支持,修复 MinIO CVE v2.0.2
v2.0.1 2023-03-21 v2 错误修复,安全增强,升级 Grafana 版本 v2.0.1
v2.0.0 2023-02-28 架构大升级,兼容性、安全性、可维护性显著增强 v2.0.0

Pigsty Release Note

而这一次从 PG 16 - PG 17,生态适配的速度显著加快了,这才三个月不到,就完成了之前需要半年的活计。在这一点上,我很自豪地说,我还是做了不少工作的。 比如在《PostgreSQL神功大成!最全扩展仓库》中介绍过的 https://ext.pigsty.io ,这里维护了 PG 生态超过一半的扩展插件。

而我也是在最近刚刚完成这件大活,把自己维护的一百四十个多个扩展针对 PG 17 进行了构建(当然还做了 Ubuntu 24.04 和部分 ARM 支持),并且自己修复或者提请扩展作者修复了几十个有兼容问题的扩展插件。 目前实现的效果是:在 EL 系统上, 334 个可用扩展有 301 个已经在 PG 17 可用,而在 Debian 系统上,326 个扩展也已经有 302 个在 PG 17 上可用。

Entry / Filter All PGDG PIGSTY CONTRIB MISC MISS PG17 PG16 PG15 PG14 PG13 PG12
RPM Extension 334 115 143 70 4 6 301 330 333 319 307 294
DEB Extension 326 104 144 70 4 14 302 322 325 316 303 293

extension.png

Pigsty 实现了 PostgreSQL 扩展生态的大对齐

目前主要的扩展中,还有分布式扩展 Citus 和列存扩展 Hydra 缺位,图数据库扩展 AGE,PGML 也依然还没有提供 PG 17 的支持,不过其他的强力扩展目前均已实现 PG 17 Ready, 其中,特别要强调一下最近在 PG 生态如火如荼的 OLAP DuckDB 扩展缝合大赛,包括 ParadeDB 的 pg_analytics,国内个人开发者李红艳编写的 duckdb_fdw,CrunchyData 的 pg_parquet,MooncakeLab 的 pg_mooncake, Hydra 和 DuckDB 原厂 MotherDuck 亲自下场搞的 pg_duckdb,全部都已经实现了 PG 17 支持,并且在 Pigsty 扩展仓库中可用。

考虑到分布式的 Citus 用户并不多,列存 Hydra 已经有大把全新的 DuckDB 扩展可以替代,我认为 PG17 在扩展生态上已经达到了一个令人满意的状态,可以作为生产环境的首要大版本使用了。而在 PG17 上实现这一点的用时,比 PG 16 快了近一倍


关于 Pigsty v3.1

Pigsty 是一个开源免费,开箱即用的 PostgreSQL 数据库发行版,可以在本地一键拉起企业级 RDS 云数据库服务,帮助用户用好世界上最先进的开源数据库 —— PostgreSQL。

PostgreSQL 已经毫无疑问地即将成为数据库领域的 Linux 内核,而 Pigsty 旨在成为 Linux 内核的 Debian 发行版。我们的 PostgreSQL 数据库发行版有六条关键价值主张:

  • 提供 PostgreSQL 生态中最全面的扩展插件支持
  • 提供 PostgreSQL 生态中最强大全面的监控系统
  • 提供开箱即用,简单易用的工具集合以及最佳实践
  • 提供故障自愈,免运维的丝滑高可用/PITR体验
  • 提供无需容器,直接运行在裸OS上的可靠部署
  • 无供应商锁定,民主化的 RDS 体验,自主可控

顺便一提,我们在 Pigsty v3 中增加了 PG 系内核替换能力,您可以使用衍生版 PG 内核,获取一些独特的能力与特性:

  • 微软 SQL Server 兼容的 Babelfish 内核支持
  • Oracle 兼容的 IvorySQL 3.4 内核支持
  • 阿里云 PolarDB for PostgreSQL / Oracle 国产化信创内核支持
  • 允许用户更方便地自建 Supabase —— 开源 Firebase,一站式后端平台

如果您希望使用原汁原味的 PostgreSQL 体验,欢迎使用我们的发行版,开源免费,没有供应商锁定;同时我们也提供商业咨询支持,为您解决疑难杂症兜底的需求与烦恼。

PostgreSQL神功大成!最全扩展仓库来了!

最近没怎么更新,因为在憋大招。最近功成出关,遂发此文为贺 —— 我做了一个收录PG生态所有能打的340个扩展的仓库,让 PostgreSQL 在成为数据库全能王的道路上又往前迈出了坚实的一步!

自从我在 《PostgreSQL正在吞噬数据库世界》 一文中指出 可扩展性 对于 PostgreSQL 的重要性以来,PG 社区对此进行了热烈的讨论,并且达成了共识。 最终体现在《PostgreSQL 17 发布注记!》中。

但真正重要的事情不是认识世界,而是改变世界。既然大家都已经认清了扩展很重要,那么我们应该做什么,怎么做,就成了真正关键的问题。

那么什么是 PostgreSQL 扩展最关键的问题?在我看来,扩展用得上用不上,是 PG 扩展生态的首要问题。


PG 扩展分发现状

大家知道 PG 生态有很多扩展插件,但这些扩展插件如何安装使用?这第一道门槛就成了许多用户的拦路虎。怎么解决这个问题? PGXN 说,用我的办法,我可以现场下载编译扩展; Tembo 说,我提前帮你打好 docker 镜像; StackGres 和 Omnigres 说,我们可以在线下载编译好的 So 文件; 八仙过海,各显神通。

大家都有很多好想法,唯独没仔细考虑绝大多数用户到底是如何安装扩展的。 作为前 DBA,我只能说什么现场编译,OCI镜像,下载so文件,在实战中都有些离谱了 —— 使用最广泛且最可靠的扩展安装方式,依然是用操作系统的包管理器安装签名二进制包。 而 yum / dnf / apt 在解决这个问题上已经做的足够好了!所以真的问题其实是,谁来把这几百个扩展插件打成开箱即用的软件包?

TIME: timescaledb timescaledb_toolkit timeseries periods temporal_tables emaj table_version pg_cron pg_later pg_background GIS: postgis postgis_topology postgis_raster postgis_sfcgal postgis_tiger_geocoder address_standardizer address_standardizer_data_us pgrouting pointcloud pointcloud_postgis h3 h3_postgis q3c ogr_fdw geoip pg_polyline pg_geohash mobilitydb earthdistance RAG: vector vectorscale vectorize pg_similarity smlar pg_summarize pg_tiktoken pgml pg4ml FTS: pg_search pg_bigm zhparser hunspell_cs_cz hunspell_de_de hunspell_en_us hunspell_fr hunspell_ne_np hunspell_nl_nl hunspell_nn_no hunspell_pt_pt hunspell_ru_ru hunspell_ru_ru_aot fuzzystrmatch pg_trgm OLAP: citus citus_columnar columnar pg_analytics pg_duckdb pg_mooncake duckdb_fdw pg_parquet pg_fkpart pg_partman plproxy pg_strom tablefunc FEAT: age hll rum pg_graphql pg_jsonschema jsquery pg_hint_plan hypopg index_advisor plan_filter imgsmlr pg_ivm pgmq pgq pg_cardano rdkit bloom LANG: pg_tle plv8 pllua hstore_pllua plluau hstore_plluau plprql pldbgapi plpgsql_check plprofiler plsh pljava plr pgtap faker dbt2 pltcl pltclu plperl bool_plperl hstore_plperl jsonb_plperl plperlu bool_plperlu jsonb_plperlu hstore_plperlu plpgsql plpython3u jsonb_plpython3u ltree_plpython3u hstore_plpython3u TYPE: prefix semver unit md5hash asn1oid roaringbitmap pgfaceting pg_sphere country currency pgmp numeral pg_rational uint uint128 ip4r uri pgemailaddr acl debversion pg_rrule timestamp9 chkpass isn seg cube ltree hstore citext xml2 FUNC: topn gzip zstd http pg_net pg_smtp_client pg_html5_email_address pgsql_tweaks pg_extra_time timeit count_distinct extra_window_functions first_last_agg tdigest aggs_for_vecs aggs_for_arrays arraymath quantile lower_quantile pg_idkit pg_uuidv7 permuteseq pg_hashids sequential_uuids pg_math random base36 base62 pg_base58 floatvec financial pgjwt pg_hashlib shacrypt cryptint pguecc pgpcre icu_ext pgqr envvar pg_protobuf url_encode refint autoinc insert_username moddatetime tsm_system_time dict_xsyn tsm_system_rows tcn uuid-ossp btree_gist btree_gin intarray intagg dict_int unaccent ADMIN: pg_repack pg_squeeze pg_dirtyread pgfincore pgdd ddlx prioritize pg_checksums pg_readonly safeupdate pg_permissions pgautofailover pg_catcheck pre_prepare pgcozy pg_orphaned pg_crash pg_cheat_funcs pg_savior table_log pg_fio pgpool_adm pgpool_recovery pgpool_regclass pgagent vacuumlo pg_prewarm oid2name lo basic_archive basebackup_to_shell old_snapshot adminpack amcheck pg_surgery STAT: pg_profile pg_show_plans pg_stat_kcache pg_stat_monitor pg_qualstats pg_store_plans pg_track_settings pg_wait_sampling system_stats meta pgnodemx pg_proctab pg_sqlog bgw_replstatus pgmeminfo toastinfo explain_ui pg_relusage pg_top pagevis powa pageinspect pgrowlocks sslinfo pg_buffercache pg_walinspect pg_freespacemap pg_visibility pgstattuple auto_explain pg_stat_statements SEC: passwordcheck_cracklib supautils pgsodium supabase_vault pg_session_jwt anon pg_tde pgsmcrypto pgaudit pgauditlogtofile pg_auth_mon credcheck pgcryptokey pg_jobmon logerrors login_hook set_user pg_snakeoil pgextwlist pg_auditor sslutils noset sepgsql auth_delay pgcrypto passwordcheck FDW: wrappers multicorn odbc_fdw jdbc_fdw mysql_fdw oracle_fdw tds_fdw db2_fdw sqlite_fdw pgbouncer_fdw mongo_fdw redis_fdw redis kafka_fdw hdfs_fdw firebird_fdw aws_s3 log_fdw dblink file_fdw postgres_fdw SIM: orafce pgtt session_variable pg_statement_rollback pg_dbms_metadata pg_dbms_lock pg_dbms_job babelfishpg_common babelfishpg_tsql babelfishpg_tds babelfishpg_money pgmemcache ETL: pglogical pglogical_origin pglogical_ticker pgl_ddl_deploy pg_failover_slots wal2json wal2mongo decoderbufs decoder_raw test_decoding mimeo repmgr pg_fact_loader pg_bulkload

PostgreSQL 的 PGDG 官方仓库中,提供了大约 100 个左右的扩展,但存在各种问题:有的扩展在 Debian/Ubuntu 的 APT 仓库里有,在 EL 系统的 YUM 仓库里没有; 有的扩展在 EL8 上有,EL9 没有;有的扩展在 Ubuntu 22 上有,在 24 上没有;有的扩展针对 PostgreSQL 12 - 15 提供,PG 16,17 不提供;有的扩展只有 x86_64 架构,没有 arm 架构;有时候碰上这种问题确实蛮让人头疼。


怎么办?我行我上!

作为一个 PostgreSQL 发行版维护者,我曾经寄希望于 PG 生态的其他人来解决这个问题。 每当我看见 PGDG 仓库有出现错漏缺失,我都会第一时间反馈给仓库维护者 Devrim 和 Cris 。

有的时候这种模式挺管用,比如去年当我发现 pgvector 这个强力向量数据库扩展还没有二进制软件包制成品时,我第一时间提给 Devrim将其放入 PGDG 仓库,然后 pgvector 遂成为 PG 生态中的向量数据库事实标准,进入到各家云厂商 RDS 中。

但有的时候,事情并不能总能如意。例如,Devrim 表示,他绝对不会接受任何 Rust 扩展插件进入 PGDG YUM 仓库。 但我确实有二十多个用 Rust 编写的 PostgreSQL 扩展需要分发(例如自建 Supabase 就需要 pg_graphql, pg_jsonschema, wrappers 三个 Rust 扩展),怎么办呢?

再比如说,最近 PG 生态非常火热的 DuckDB 缝合大赛,大家都在密集地更新跟进 DuckDB 系扩展 ,这些扩展插件我第一时间 打好了 RPM/DEB 包,但是如何分发呢?

思来想去,我决定还是我行我上,自己维护一个 PostgreSQL 扩展插件的 APT / YUM 仓库,分发 PG 扩展。


PG 扩展大全

在过去的半年中,我的工作重心放在 PG 扩展生态的整合上。而最近,这项工作终于达到了一个让我自己感到满意的里程碑。我建设了一个 PG Yum/APT 仓库,收录了 340 个可用 PG 扩展的元数据,以及二进制制成品。

Entry / Filter All PGDG PIGSTY CONTRIB MISC MISS PG17 PG16 PG15 PG14 PG13 PG12
RPM Extension 334 119 139 70 4 6 301 330 333 319 307 294
DEB Extension 326 104 143 70 5 14 302 322 325 316 303 293
RPM Package 251 107 138 1 4 1 220 247 250 239 229 216
DEB Package 241 90 142 1 5 1 218 237 240 234 223 213

以上是这个仓库的一些统计数字:总共有 340 个可用 Extension,去除 PG 自带的 70 个,总共 270 个第三方扩展插件。这 270 个扩展插件中,有小一半是 PGDG 官方仓库维护的(126个RPM扩展,102个DEB扩展),另外的大一半(131个RPM,143个DEB)都是由我维护,修复,编译,打包,测试,分发的。

每一个扩展,我都针对最新的 PostgreSQL 12 - 17 这六个生命周期大版本分别打包构建,针对 EL8,EL9,Ubuntu 22.04,Ubuntu 24.04,以及 Debian 12 这五个绝对主流 Linux 发行版构建。此外也对 EL7,Debian 11, Ubuntu 20.04 这些过保系统提供部分有限支持。

这个仓库还解决了扩展对齐的问题,例如,原本在 APT 和 YUM 仓库中的扩展,APT 有一小半几十个扩展 YUM 仓库没有,YUM 仓库有一小半 APT 仓库没有。我把两者独有的扩展都尽可能移植到另一个操作系统生态中,现在只有 7 个 APT 扩展在 YUM 仓库中缺失,16 个扩展在 APT 仓库缺失,只占总数的 6%。很多 PGDG 扩展版本缺失的问题,也在这里得到了一并修复。

我提供了一个完整的目录,列出了支持的扩展,并且对每一个扩展,都给出了详情,依赖安装说明与注意事项。

我想,用户吭哧吭哧抱怨扩展编译失败的问题,应该能在这里得到最终的解决。

当然题外话是广告时间,安装这些扩展,使用这个仓库的最简单的方式是什么?当然是开箱即用的 PostgreSQL 数据库发行版 —— Pigsty —— 但这并非必选项。 你依然可以用简单的一行 shell 在任何 EL/Debian/Ubuntu 系统上启用此仓库。

使用Pigsty一次性配置好并拉起用于自建Supabase的PostgreSQL集群,只要简单地声明要安装哪些扩展插件即可!

一键自建 Supabase 所需的 PostgreSQL 集群,请参考样例配置文件: conf/dbms/supabase.yml

# pg-meta, the underlying postgres database for supabase
pg-meta:
  hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
  vars:
    pg_cluster: pg-meta
    pg_users:
      # supabase roles: anon, authenticated, dashboard_user
      - { name: anon           ,login: false }
      - { name: authenticated  ,login: false }
      - { name: dashboard_user ,login: false ,replication: true ,createdb: true ,createrole: true }
      - { name: service_role   ,login: false ,bypassrls: true }
      # supabase users: please use the same password
      - { name: supabase_admin             ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: true   ,roles: [ dbrole_admin ] ,superuser: true ,replication: true ,createdb: true ,createrole: true ,bypassrls: true }
      - { name: authenticator              ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin, authenticated ,anon ,service_role ] }
      - { name: supabase_auth_admin        ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin ] ,createrole: true }
      - { name: supabase_storage_admin     ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin, authenticated ,anon ,service_role ] ,createrole: true }
      - { name: supabase_functions_admin   ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin ] ,createrole: true }
      - { name: supabase_replication_admin ,password: 'DBUser.Supa' ,replication: true ,roles: [ dbrole_admin ]}
      - { name: supabase_read_only_user    ,password: 'DBUser.Supa' ,bypassrls: true ,roles: [ dbrole_readonly, pg_read_all_data ] }
    pg_databases:
      - name: postgres
        baseline: supabase.sql
        owner: supabase_admin
        comment: supabase postgres database
        schemas: [ extensions ,auth ,realtime ,storage ,graphql_public ,supabase_functions ,_analytics ,_realtime ]
        extensions:
          - { name: pgcrypto  ,schema: extensions  } # 1.3   : cryptographic functions
          - { name: pg_net    ,schema: extensions  } # 0.9.2 : async HTTP
          - { name: pgjwt     ,schema: extensions  } # 0.2.0 : json web token API for postgres
          - { name: uuid-ossp ,schema: extensions  } # 1.1   : generate universally unique identifiers (UUIDs)
          - { name: pgsodium        }                # 3.1.9 : pgsodium is a modern cryptography library for Postgres.
          - { name: supabase_vault  }                # 0.2.8 : Supabase Vault Extension
          - { name: pg_graphql      }                # 1.5.9 : pg_graphql: GraphQL support
          - { name: pg_jsonschema   }                # 0.3.3 : pg_jsonschema: Validate json schema
          - { name: wrappers        }                # 0.4.3 : wrappers: FDW collections
          - { name: http            }                # 1.6   : http: allows web page retrieval inside the database.
          - { name: pg_cron         }                # 1.6   : pg_cron: Job scheduler for PostgreSQL
          - { name: timescaledb     }                # 2.17  : timescaledb: Enables scalable inserts and complex queries for time-series data
          - { name: pg_tle          }                # 1.2   : pg_tle: Trusted Language Extensions for PostgreSQL
    # supabase required extensions
    pg_libs: 'pg_stat_statements, pgaudit, plpgsql, plpgsql_check, pg_cron, pg_net, timescaledb, auto_explain, pg_tle, plan_filter'
    pg_extensions: # extensions to be installed on this cluster
      - supa-stack
      - timescaledb pg_cron pg_timetable
      - postgis pg_geohash
      - pgvector pgvectorscale pg_similarity smlar pg_summarize pg_tiktoken
      - pg_search pg_bigm zhparser hunspell
      - pg_analytics pg_parquet pg_duckdb
      - pg_hint_plan hll rum pg_graphql pg_jsonschema index_advisor pg_plan_filter hypopg pg_ivm pgmq pg_cardano
      - pg_tle plv8 plpgsql_check #pljava
      - pgunit md5hash asn1oid roaringbitmap pgfaceting pgsphere pg_country pg_currency pgmp numeral pg_rational pguint pg_uint128 ip4r pg_uri pgemailaddr acl timestamp9
      - pg_gzip pg_zstd pg_http pg_net pg_html5_email_address pgsql_tweaks pg_extra_time pg_timeit count_distinct extra_window_functions first_last_agg tdigest aggs_for_arrays aggs_for_vecs pg_arraymath quantile lower_quantile
      - pg_idkit pg_uuidv7 permuteseq pg_hashids sequential_uuids pg_math pg_random pg_base36 pg_base62 pg_base58 floatvec pg_financial pgjwt pg_hashlib shacrypt cryptint pg_ecdsa pgpcre icu_ext pgqr envvar pg_protobuf url_encode
      - pg_repack pg_squeeze pg_dirtyread ddlx pg_readonly safeupdate pg_permissions pg_savior pg_fio
      - pg_profile pg_show_plans pg_stat_kcache pg_stat_monitor pg_qualstats pg_track_settings system_stats pg_meta pgnodemx pg_sqlog bgw_replstatus toastinfo pg_explain_ui pg_relusage
      - passwordcheck supautils pgsodium pg_vault anonymizer pgsmcrypto pgaudit pgauditlogtofile pg_auth_mon credcheck logerrors login_hook set_user pgextwlist pg_auditor sslutils noset
      - wrappers mysql_fdw redis_fdw pg_redis_pubsub aws_s3 log_fdw
      - pglogical wal2json decoder_raw pg_fact_loader
    pg_parameters:
      cron.database_name: postgres
      pgsodium.enable_event_trigger: off
    pg_hba_rules: # supabase hba rules, require access from docker network
      - { user: all ,db: postgres  ,addr: intra         ,auth: pwd ,title: 'allow supabase access from intranet'    }
      - { user: all ,db: postgres  ,addr: 172.17.0.0/16 ,auth: pwd ,title: 'allow access from local docker network' }
    pg_vip_enabled: true
    pg_vip_address: 10.10.10.2/24
    pg_vip_interface: eth1

这个仓库里有什么?

在 Pigsty 的扩展仓库中,所有的扩展都已经被预先分为了十五类之一:TIME,GIS,RAG,FTS,OLAP,FEAT,LANG,TYPE,FUNC,ADMIN,STAT,SEC,FDW,SIM,ETL,如下所示。

请移步 ext.pigsty.io 查看完整详情。

TIME: timescaledb timescaledb_toolkit timeseries periods temporal_tables emaj table_version pg_cron pg_later pg_background GIS: postgis postgis_topology postgis_raster postgis_sfcgal postgis_tiger_geocoder address_standardizer address_standardizer_data_us pgrouting pointcloud pointcloud_postgis h3 h3_postgis q3c ogr_fdw geoip pg_polyline pg_geohash mobilitydb earthdistance RAG: vector vectorscale vectorize pg_similarity smlar pg_summarize pg_tiktoken pgml pg4ml FTS: pg_search pg_bigm zhparser hunspell_cs_cz hunspell_de_de hunspell_en_us hunspell_fr hunspell_ne_np hunspell_nl_nl hunspell_nn_no hunspell_pt_pt hunspell_ru_ru hunspell_ru_ru_aot fuzzystrmatch pg_trgm OLAP: citus citus_columnar columnar pg_analytics pg_duckdb pg_mooncake duckdb_fdw pg_parquet pg_fkpart pg_partman plproxy pg_strom tablefunc FEAT: age hll rum pg_graphql pg_jsonschema jsquery pg_hint_plan hypopg index_advisor plan_filter imgsmlr pg_ivm pgmq pgq pg_cardano rdkit bloom LANG: pg_tle plv8 pllua hstore_pllua plluau hstore_plluau plprql pldbgapi plpgsql_check plprofiler plsh pljava plr pgtap faker dbt2 pltcl pltclu plperl bool_plperl hstore_plperl jsonb_plperl plperlu bool_plperlu jsonb_plperlu hstore_plperlu plpgsql plpython3u jsonb_plpython3u ltree_plpython3u hstore_plpython3u TYPE: prefix semver unit md5hash asn1oid roaringbitmap pgfaceting pg_sphere country currency pgmp numeral pg_rational uint uint128 ip4r uri pgemailaddr acl debversion pg_rrule timestamp9 chkpass isn seg cube ltree hstore citext xml2 FUNC: topn gzip zstd http pg_net pg_smtp_client pg_html5_email_address pgsql_tweaks pg_extra_time timeit count_distinct extra_window_functions first_last_agg tdigest aggs_for_vecs aggs_for_arrays arraymath quantile lower_quantile pg_idkit pg_uuidv7 permuteseq pg_hashids sequential_uuids pg_math random base36 base62 pg_base58 floatvec financial pgjwt pg_hashlib shacrypt cryptint pguecc pgpcre icu_ext pgqr envvar pg_protobuf url_encode refint autoinc insert_username moddatetime tsm_system_time dict_xsyn tsm_system_rows tcn uuid-ossp btree_gist btree_gin intarray intagg dict_int unaccent ADMIN: pg_repack pg_squeeze pg_dirtyread pgfincore pgdd ddlx prioritize pg_checksums pg_readonly safeupdate pg_permissions pgautofailover pg_catcheck pre_prepare pgcozy pg_orphaned pg_crash pg_cheat_funcs pg_savior table_log pg_fio pgpool_adm pgpool_recovery pgpool_regclass pgagent vacuumlo pg_prewarm oid2name lo basic_archive basebackup_to_shell old_snapshot adminpack amcheck pg_surgery STAT: pg_profile pg_show_plans pg_stat_kcache pg_stat_monitor pg_qualstats pg_store_plans pg_track_settings pg_wait_sampling system_stats meta pgnodemx pg_proctab pg_sqlog bgw_replstatus pgmeminfo toastinfo explain_ui pg_relusage pg_top pagevis powa pageinspect pgrowlocks sslinfo pg_buffercache pg_walinspect pg_freespacemap pg_visibility pgstattuple auto_explain pg_stat_statements SEC: passwordcheck_cracklib supautils pgsodium supabase_vault pg_session_jwt anon pg_tde pgsmcrypto pgaudit pgauditlogtofile pg_auth_mon credcheck pgcryptokey pg_jobmon logerrors login_hook set_user pg_snakeoil pgextwlist pg_auditor sslutils noset sepgsql auth_delay pgcrypto passwordcheck FDW: wrappers multicorn odbc_fdw jdbc_fdw mysql_fdw oracle_fdw tds_fdw db2_fdw sqlite_fdw pgbouncer_fdw mongo_fdw redis_fdw redis kafka_fdw hdfs_fdw firebird_fdw aws_s3 log_fdw dblink file_fdw postgres_fdw SIM: orafce pgtt session_variable pg_statement_rollback pg_dbms_metadata pg_dbms_lock pg_dbms_job babelfishpg_common babelfishpg_tsql babelfishpg_tds babelfishpg_money pgmemcache ETL: pglogical pglogical_origin pglogical_ticker pgl_ddl_deploy pg_failover_slots wal2json wal2mongo decoderbufs decoder_raw test_decoding mimeo repmgr pg_fact_loader pg_bulkload


一些感想与体会

PG 每个大版本都会引入一些变动,因此维护一百多个扩展插件并不是一件轻松的事情。特别是一些扩展的作者都好几年没动静了,那还真就只能自己上。我自己修复了十几个扩展插件,提供了最新的 PG 大版本支持。能联系上作者的,我也提交了一堆 PR 或者 Issue,推动解决。

在这个过程中,我和许多扩展作者都建立了联系。例如,我手把手帮助 ParadeDB 的老板与作者 解决了 RPM / DEB 包打包与分发的问题。我说动了 duckdb_fdw 的作者使用一个单独的 libduckdb,并发布了 v1.0.0 ,我给一些PG扩展的作者发邮件/Issue,国产机器学习框架 PG4ML 的作者也找到了我希望能够通过这个渠道进行分发。

再比如说,最近 PG 生态 OLAP 缝合 DuckDB 的竞赛如火如荼,但不管是ParadeDB 的 pg_analytics,国内个人开发者李红艳编写的 duckdb_fdw,CrunchyData 的 pg_parquet,MooncakeLab 的 pg_mooncake, Hydra 和 DuckDB 原厂 MotherDuck 亲自下场搞的 pg_duckdb ,都被我在第一时间编译打包收录整合其中,做到了 —— 你卷你的,反正我全都要。

言归正传,我希望这个仓库能设立起 PostgreSQL 扩展安装分发的标准,解决让人头大的分发难题。目前最让我感到高兴的进展是,流行的开源 PostgreSQL高可用集群搭建项目 postgresql_cluster 的作者 Vitaliy Kukharik 已经将这个仓库作为默认启用的仓库来安装 PostgreSQL 扩展。

目前这个仓库 (repo.pigsty.io) 托管在 Cloudflare 上,所以没有什么流量成本。国内有一个镜像站点 repo.pigsty.cc,方便墙内用户使用,每个有小几百块流量费,不是什么大问题。两个仓库加起来,过去一个月的下载流量大概 200GB ,考虑到扩展平均几十KB到几MB的大小,总下载量小几十万是有了。

因为赛博菩萨 Cloudflare 不收流量费,所以总的来说,我觉得做一个永久免费的声明与承诺并不困难,所以 So be it。我承诺这个仓库将持续维护并永久免费。如果有国内开源软件站点的朋友愿意赞助或提供镜像服务,欢迎联系我。

我相信我的工作可以帮助到全球PG用户,并对 PostgreSQL 生态的繁荣贡献一份力量。我也希望我的工作可以帮到您,Enjoy PostgreSQL

PostgreSQL 规约(2024版)


0x00背景

没有规矩,不成方圆。

PostgreSQL的功能非常强大,但是要把PostgreSQL用好,需要后端、运维、DBA的协力配合。

本文针对PostgreSQL数据库原理与特性,整理了一份开发/运维规约,希望可以减少大家在使用PostgreSQL数据库过程中遇到的困惑:你好我也好,大家都好。

本文第一版主要针对 PostgreSQL 9.4 - PostgreSQL 10 版本 ,当前最新版本针对 PostgreSQL 15/16/17 进行更新与调整。


0x01 命名规范

计算机科学只存在两个难题:缓存失效和命名

通用命名规则(Generic)

  • 本规则适用于所有数据库内对象,包括:库名、表名、索引名、列名、函数名、视图名、序列号名、别名等。
  • 对象名务必只使用小写字母,下划线,数字,其中首字母必须为小写字母。
  • 对象名长度不得超过63个字符,命名统一采用 snake_case 风格。
  • 禁止使用SQL保留字,使用select pg_get_keywords(); 获取保留关键字列表。
  • 禁止出现美元符号 $ ,禁止使用中文,不要以 pg 开头。
  • 提高用词品味,做到信达雅;不要使用拼音,不要使用生僻冷词,不要使用小众缩写。

集群命名规则 (Cluster)

  • PostgreSQL 集群的命名将作为集群资源的命名空间,必须为有效的 DNS 域名,不包含任何点号与下划线。
  • 集群名应当由小写字母开头,仅包含小写字母、数字、减号,符合正则表达式:[a-z][a-z0-9-]*
  • PostgreSQL 数据库集群命名通常以三段式结构:pg-<biz>-<tld>。数据库类型 / 业务名称 / 业务线或环境
  • biz 为最能代表业务特征的英文词语,应当仅由小写字母与数字组成,不得包含连字符 -
  • 使用备份集群搭建某一个现有集群的延迟从库时,biz 名应当为 <biz>delay,例如 pg-testdelay
  • 分支一个现有集群时,可以在 biz 尾部添加数字:例如从 pg-user1 可以分支出 pg-user2pg-user3
  • 水平分片集群,biz命名中应当包含 shard,并缀以分片号,例如 pg-testshard1pg-testshard2,……
  • <tld> 为顶层业务线,也可用于区分不同环境:例如 -tt-dev-uat-prod 等。无此需要可以省略。

服务命名规则(Service)

  • 每一套 PostgreSQL 集群会对外提供 2~6 种不等的服务,这些默认使用固定命名规则。
  • 服务名以集群名作为前缀,服务类型作为后缀,例如 pg-test-primarypg-test-replica
  • 读写服务统一以 primary 后缀命名,只读服务统一以 replica 后缀命名,这两个为必选服务。
  • ETL拉取/个人用户查询以 offline 后缀命名,直连主库/ETL写入以 default 后缀命名,为选配服务。
  • 同步读取服务以 standby 后缀命名,延迟从库服务以 delayed 后缀命名,少量核心库可提供此服务。

实例命名规则(Instance)

  • 一套 PostgreSQL 集群由至少一个实例组成,每个实例都有集群内从零或一开始唯一分配的实例号。
  • 实例名由集群名 + 实例号通过连字符 - 拼接而成,例如: pg-test-1pg-test-2
  • 实例号一经分配不得修改,持续到实例下线销毁,不得重新分配使用。
  • 实例名将作为监控系统数据的 ins 标签,附加到该实例的所有数据上。
  • 如果是用主机/数据库 1:1 独占式部署,节点 Hostname 可以使用数据库实例名。

数据库命名规则(Database)

  • 数据库库名应当与集群、应用保持一致,必须为具有高区分度的英文单词。
  • 命名以 <tld>_<biz> 的形式构建,<tld> 为顶层业务线,也可用于区分不同环境,不用可以省略。
  • <biz>为具体业务名称,例如 pg-test-tt 集群可以使用库名 tt_testtest。这一点不强制,即允许创建不同于集群 <biz> 名称的其他数据库。
  • 对于分片库,<biz> 部分必须以shard结尾,但不应当包含分片号,例如 pg-testshard1pg-testshard2 都用 testshard 即可。
  • 多个部分使用-连接。例如:<biz>-chat-shard<biz>-payment等,总共不超过三段。

角色命名规范(Role/User)

  • 数据库超级用户 dbsu 有且仅有一个:postgres,用于流复制的用户命名为replicator
  • 用于监控的用户统一命名为 dbuser_monitor,用于日常管理的超级用户为:dbuser_dba
  • 程序/服务使用的业务用户默认使用 dbuser_<biz> 作为用户名,例如 dbuser_test。来自不同服务的访问应当使用独立的业务用户区分访问。
  • 个人用户申请的数据库用户同意使用 dbp_<name>,其中 name 为 LDAP 中的标准用户名。
  • 默认权限组命名固定为: dbrole_readonlydbrole_readwritedbrole_admindbrole_offline

模式命名规则(Schema)

  • 业务统一使用一个全局的 <prefix> 作为模式名,尽可能简短,默认设置为search_path 首位元素。
  • <prefix> 不得使用 publicmonitor ,不得与任何 PostgreSQL 扩展使用的模式名冲突,例如: timescaledbcitusrepackgraphqlnetcron,…… 不宜使用特殊名称:dbatrash
  • 分片模式命名规则采用:rel_<partition_total_num>_<partition_index>。中间为总分片数,目前固定使用 8192 ,后缀是分片号,从0开始计数。如 rel_8192_0,…… ,rel_8192_11,等等。
  • 创建额外的模式,或者使用 <prefix> 之外的模式名需要由研发解释其必要性。

关系命名规则(Relation)

  • 关系命名以表意清晰为第一要义,不要使用含混的缩写,也不应过分冗长,遵循通用命名规则。
  • 表名应当使用复数名词,并与历史惯例保持一致,应尽量避免带有不规则复数形式的单词。
  • 视图以v_作为命名前缀,物化视图使用mv_作为命名前缀,临时表以tmp_作为命名前缀。
  • 继承或分区表应当以父表表名作为前缀,并以子表特性(规则,分片范围等)作为后缀。
  • 时间范围分区使用起始区间作为命名后缀,首个分区如果无上界则由研发指定一个足够久远的时间点:年级分区:tbl_2023,月级分区 tbl_202304,天级分区 tbl_20230405,小时级分区 tbl_2023040518 ,默认分区以 _default 结尾。
  • 哈希分区命名以余数作为分区表名的后缀,列表分区由研发手工指定与列表项对应的合理分区表名。

索引命名规则(Index)

  • 创建索引时,应当显式指定索引名称,并与PostgreSQL默认命名规则保持一致。
  • 索引名称以表名作为前缀,主键索引以 _pkey 结尾,唯一索引以 _key 结尾,普通索引以 _idx 结尾,用于EXCLUDED约束的索引以_excl结尾。
  • 使用条件索引/函数索引时,应当在索引名称中体现使用的函数与条件内容。例如 tbl_md5_title_idxtbl_ts_ge_2023_idx,但不可超出长度限制。

字段命名规则(Attribute)

  • 禁止使用系统列保留字段名:oidxminxmaxcmincmaxctid
  • 主键列通常命名为id,或以id作为后缀。
  • 创建时间字段惯用名为created_time,最后修改时间惯用名为 updated_time
  • 布尔型字段建议使用is_has_ 等作为前缀。
  • 额外的灵活 JSONB 字段固定使用 extra 作为列名。
  • 其余各字段名需与已有表命名惯例保持一致,任何打破惯例的字段命名都应当做出书面设计说明与解释。

枚举项命名 (Enum)

  • 枚举项默认应当使用 camelCase,但也允许其他风格。

函数命名规则(Function)

  • 函数命名以动词起头: selectinsertdeleteupdateupsertcreate ,……。
  • 重要参数可以通过_by_ids_by_user_ids的后缀在函数名中体现。
  • 避免函数重载,同名函数尽量只保留一个。
  • 禁止通过 BIGINT/INTEGER/SMALLINT 等整型进行函数签名重载,调用时可能产生歧义。
  • 存储过程与函数中的变量使用命名参数,避免位置参数($1$2,…)。
  • 如果参数名与对象名出现冲突,在参数前添加 _,例如_user_id

注释规范(Comment)

  • 尽最大可能为各种对象提供注释(COMMENT),注释使用英文,言简意赅,一行为宜。
  • 对象的模式或内容语义发生变更时,请务必一并更新注释,并与实际情况保持同步。

0x02 设计规范

Suum cuique

建表注意事项

  • 建表 DDL 语句需要使用标准格式,SQL 关键词大写,其他小写。
  • 在字段名/表名/别名中统一使用小写,尽量不要区分大小写。如果遇到大小写混用的情况,或者与 SQL 关键词冲突的名称,需要使用双引号扩起进行引用。
  • 能使用专有类型的,不使用字符串。(数值,枚举,网络地址,货币,JSON,UUID等):使用正确的数据类型,能显著提高数据存储,查询,索引,计算的效率,并提高可维护性。
  • 优化列的布局,对齐类型可以有额外的性能/存储空间收益。
  • 唯一约束须由数据库保证,任何唯一列须有对应的唯一约束。EXCLUDE约束是泛化的唯一约束,可以在低频更新场景下用于保证数据完整性。

分区表注意事项

  • 如果单表超过百TB量级,或者每月增量数据超过十几GB量级,可以考虑进行表分区。
  • 分区的指导原则是,让每个分区的大小尽可能落在 1GB ~ 64GB 的舒适范围内。
  • 有条件按照时间范围分区的表优先按时间范围分区,通常使用的粒度包括: decade,year,month,day,hour,应当至少提前三个月创建好未来所需的分区。
  • 对于极端倾斜的数据分布,可以组合使用不同的时间粒度,例如: 1900 - 2000 一个大分区,2000 - 2020 按年分区,2020 后按月分区。使用时间分区时,表名使用分区下限界的值(无穷大则选用一个足够久远的值)。

宽表注意事项

  • 宽表(例如有几十个字段的表)可以考虑进行纵向拆分,通过相同的主键与主表相互引用。
  • 因为PostgreSQL MVCC机制,宽表的写放大现象更为明显,减少对宽表的频繁更新。
  • 互联网场景中,允许适当降低规范化等级,减少多表连接以提高性能。

主键注意事项

  • 每个表都必须身份列,原则上必须有主键,最低要求为拥有非空唯一约束
  • 身份列用于唯一标识表中的任一元组,逻辑复制与诸多三方工具有赖于此。
  • 主键如果包含多列,应当在建表DDL的字段列表之后,使用 PRIMARY KEY(a,b,...) 单列指定。
  • 主键原则上建议使用整型,可以谨慎使用 UUID 与长度受限的文本类型,使用其他类型需要显式说明与评估。
  • 主键通常使用单一整型列,原则上建议使用 BIGINT,审慎使用 INTEGER,不允许使用 SMALLINT
  • 主键应使用 GENERATED ALWAYS AS IDENTITY 生成唯一主键;SERIALBIGSERIAL 仅当需要兼容 PG 10 以下版本时允许使用。
  • 主键可以使用 UUID 类型作为主键,建议用 UUID v1/v7;审慎使用 UUIDv4 作为主键,随机 UUID 的局部性较差且存在碰撞概率。
  • 使用字符串列作为主键时,应当添加长度限制。通常使用 VARCHAR(64),使用更长的字符串时应当进行说明与评估。
  • INSERT/UPDATE 时原则上禁止修改主键列的值,INSERT RETURNING 可用于返回自动生成的主键值。

外键注意事项

  • 定义外键时引用必须显式设置相应的动作:SET NULLSET DEFAULTCASCADE,慎用级联操作。
  • 外键引用的列,需要为其他表/本表上的主键列。
  • 互联网类业务,特别是分区表、水平分片库慎用外键,可以在应用层解决。

空值/默认值注意事项

  • 字段语义上没有零值与空值区分的,不允许空值存在,须为列配置NOT NULL约束。
  • 字段语义上带有默认值的,应当配置 DEFAULT 默认值。

数值类型注意事项

  • 常规数值字段使用 INTEGER 。容量拿不准的数值列使用 BIGINT
  • 无特殊理由不要用 SMALLINT ,性能与存储提升甚小,但会有很多额外的问题。
  • 注意SQL标准不提供无符号整型,超过 INTMAX 但没超过 UINTMAX 的值需要升格存储。不要存储超过 INT64MAX 的值到 BIGINT 列中,会溢出为负数,有此需求请使用 uint 扩展插件。
  • REAL 表示 4 字节浮点数,FLOAT 表示 8 字节浮点数。浮点数仅可用于末尾精度无所谓的场景,例如地理坐标。切记不要对浮点数使用等值判断,零值除外
  • 精确数值类型使用 NUMERIC,如果可行,请用 NUMERIC(p)NUMERIC(p,s) 设置有效数字位数以及小数部分的有效位数。例如摄氏气温(37.0)可以用 NUMERIC(3,1) 类型来存储3位有效数字与1位小数。
  • 货币数值类型使用 MONEY

文本类型注意事项

  • PostgreSQL的文本类型包括 char(n)varchar(n)text。默认情况下,可以使用 text 类型 ,不限制字符串长度,但受字段最大长度1GB限制。
  • 如果条件许可,优先使用 varchar(n) 类型来设置一个最大字符串长度,这会引入极微小的额外检查开销,但能规避一些脏数据与极端情况。
  • 避免使用char(n),该类型为了与SQL标准兼容,存在不合直觉的行为表现(补齐空格与截断),且并没有存储和性能优势。

时间类型注意事项

  • 时间只有两种存储方式:带时区的 TIMESTAMPTZ,不带时区的 TIMESTAMP
  • 建议使用带时区的 TIMESTAMPTZ,如果使用 TIMESTAMP 存储,必须使用0时区标准时。
  • 生成0时区时间请使用 now() AT TIME ZONE 'UTC' ,不能直接截断时区 now()::TIMESTAMP
  • 统一使用 ISO-8601 格式输入输出时间类型:2006-01-02 15:04:05,避免DMY与MDY问题。
  • 中国区域用户可以统一使用 Asia/Hong_Kong +8 时区,因为上海时区缩写 CST 有歧义。

枚举类型注意事项

  • 较稳定的,取值空间较小(几十到几百内)的字段应当使用枚举类型,不要使用整型与字符串表示。
  • 枚举内部使用动态整型实现,相比整型有可读性优势,相比字符串有性能、存储、可维护性上的优势。
  • 枚举项只能添加,无法删除,但是可以重命名现有枚举值。ALTER TYPE <enum_name> 用于修改枚举。

UUID类型注意事项

  • 请注意,全随机的 UUIDv4 用作主键时局部性太差,尽可能考虑用 UUIDv1 / v7 代替。
  • 一些 UUID 生成/处理函数需要额外的扩展插件,例如 uuid-ossppg_uuidv7 等,有此需求请在配置时指明。

JSON类型注意事项

  • 如无特殊原因,总是使用二进制存储的 JSONB 类型与相关函数,而非文本版本的 JSON
  • 请注意 JSON 中的原子类型与 PostgreSQL 对应类型的细微差别:与 JSON 字符串对应的text 类型中不允许出现 \u0000 零字符,与 JSON 数值类型对应的 numeric 中不允许出现 NaNinfinity。布尔值只接受小写的 truefalse 字面值。
  • 请注意JSON标准中的null对象和 SQL 标准中的空值 NULL 并非同一个概念。

数组类型注意事项

  • 当存储元素数量较少时,可以使用数组字段代替单独。
  • 适合用于存储元素数量相对较少且变化不频繁的数据。如果数组中的元素数量非常大或经常变化,考虑使用单独的表来存储数据,并使用外键关联。
  • 高维度的浮点数组,可以考虑使用 pgvector 扩展提供的专用数据类型。

GIS类型注意事项

  • GIS 类型默认使用 srid=4326 参考坐标系。
  • 经纬度坐标点应当使用 Geography 类型,使用此类型时默认无需显式指定参考系坐标 4326

触发器注意事项

  • 触发器会提高数据库系统的复杂度与维护成本,原则上不鼓励使用。禁止使用规则系统,此类需求应当使用触发器替代。
  • 触发器的典型场景是,在修改某一行后自动修改 updated_time 为当前时间戳,或者将表的增删改动作记录到另一张日志表中,或者维持两张表在业务上的一致性。
  • 触发器中的操作是事务性的,意味着如果触发器或触发器中的操作失败,整个事务都会回滚,所以请充分测试并证明触发器的正确性。对于递归调用、执行复杂查询死锁,多个触发器执行顺序等情况需要特别关注。

存储过程/函数注意事项

  • 函数/存储过程适用于封装事务,减少并发冲突,减少网络往返,减少返回数据量,执行少量自定义逻辑。

  • 存储过程不适合进行复杂计算,不适合进行平凡/频繁的类型转换与包装。在关键高负载系统中,应当移除数据库中不必要的计算密集型逻辑,例如在数据库中使用SQL进行WGS84到其他坐标系的换算。与数据获取、筛选密切关联的计算逻辑可以使用函数/存储过程:例如PostGIS中的几何关系判断。

  • 不再使用的,被替换的函数与存储过程应当及时移除下线,避免与未来的函数发生冲突。

  • 使用统一的函数创建语法格式,签名单独占用一行(函数名与参数),返回值单启一行,语言为第一个标签。一定要标注函数易变性等级:IMMUTABLE, STABLE, VOLATILE。添加属性标签,如:RETURNS NULL ON NULL INPUTPARALLEL SAFEROWS 1 等。

    CREATE OR REPLACE FUNCTION
      nspname.myfunc(arg1_ TEXT, arg2_ INTEGER)
      RETURNS VOID
    LANGUAGE SQL
    STABLE
    PARALLEL SAFE
    ROWS 1
    RETURNS NULL ON NULL INPUT
    AS $function$
    SELECT 1;
    $function$;
    

使用合理的Locale选项

使用合理的字符编码与本地化配置

  • 必须使用 UTF8 字符编码,严格禁止使用其他任何字符编码。
  • 必须使用 C 作为 LC_COLLATE 默认排序规则,有特殊需求必须在DDL/查询子句中显式指定来实现。
  • 字符集 LC_CTYPE 默认使用 en_US.UTF8,一些扩展依赖字符集信息方可正常工作,如 pg_trgm

索引相关注意事项

  • 所有在线查询必须针对其访问模式设计相应索引,除极小表外不允许全表扫描。
  • 索引有代价,不允许创建不使用的索引,应当及时清理不再使用的索引。
  • 建立联合索引时,应当将区分度,选择率高的列放在前面,例如 ID,时间戳等。
  • GiST索引可用于解决近邻查询问题,传统B树索引无法提供对KNN问题的良好支持。
  • 对于值与堆表的存储顺序线性相关的数据,如果查询为范围查询,建议使用BRIN索引。最典型场景如仅追加写入的时序数据,BRIN索引相比Btree更为高效。
  • 针对 JSONB / 数组字段进行检索时,可以使用 GIN 索引加速查询。

明确B树索引空值的顺序

  • 如在可空列上有排序需求,需要在查询与索引中明确指定 NULLS FIRST 还是 NULLS LAST
  • 注意,DESC 排序的默认规则是 NULLS FIRST,即空值会出现在排序的最前面,通常这不是期望行为。
  • 索引的排序条件必须与查询匹配,如:CREATE INDEX ON tbl (id DESC NULLS LAST);

禁止在大字段上建立索引

  • 被索引字段大小无法超过2KB(1/3的页容量),在文本类型上创建索引需要谨慎,被索引的文本应当使用带有长度约束的 varchar(n) 类型。
  • 文本类型用作主键时,必须设置最大长度。原则上长度不应当超过 64 个字符,特殊情况需显式说明评估。
  • 如有大字段索引需求,可以考虑对大字段取哈希,并建立函数索引。或使用其他类型的索引(GIN)。

充分利用函数索引

  • 任何可以由同一行其他字段推断得出的冗余字段,可以使用函数索引替代。
  • 对于经常使用表达式作为查询条件的语句,可以使用表达式或函数索引加速查询。
  • 典型场景:建立大字段上的哈希函数索引,为需要左模糊查询的文本列建立 reverse 函数索引。

充分利用部分索引

  • 查询中查询条件固定的部分,可以使用部分索引,减小索引大小并提升查询效率。
  • 查询中某待索引字段若只有有限几种取值,也可以建立几个相应的部分索引。
  • 如果部分索引中的列会被频繁更新,请关注这些索引的膨胀情况

0x03 查询规范

The limits of my language mean the limits of my world.

—Ludwig Wittgenstein

使用服务接入

  • 生产数据库接入必须通过域名接入服务,严禁使用 IP 地址直连。
  • 服务与接入使用 VIP,LVS/HAProxy 屏蔽集群实例成员的角色变化,主从切换无需应用重启。

读写分离

  • 互联网业务场景:写请求必须走主库,通过 Primary 服务访问。
  • 读请求原则上走从库,通过 Replica 服务访问。
  • 例外情况:需要读己之写的一致性保证,且检测到显著的复制延迟时,只读请求可以访问主库;或向DBA申请提供 Standby 服务。

快慢分离

  • 生产中1毫秒以内的查询称为快查询,生产中超过1秒的查询称为慢查询。
  • 慢查询必须走离线从库 —— Offline 服务/实例,应当在执行时设置超时。
  • 生产中的在线普通查询执行时长原则上应当控制在 1ms 内。
  • 生产中的在线普通查询执行时长,超过10ms需修改技术方案,优化达标后再上线。
  • 在线查询应当配置10ms 数量级或更快的 Timeout,避免堆积造成雪崩。
  • 禁止从 Primary 上 ETL 数据,应当使用 Offline 服务从专用实例取数。

使用连接池

  • 生产应用必须通过连接池访问数据库,通过 1:1 部署的 Pgbouncer 代理访问 PostgreSQL 数据库。Offline 服务,个人用户严禁直接使用连接池。
  • Pgbouncer 连接池默认使用 Transaction Pooling 模式,一些会话级别的功能可能无法使用(比如Notify/Listen),需要特别注意。在此模式下,1.21 以前的 Pgbouncer 不支持使用 Prepared Statements。特殊场景可以使用 Session Pooling 或绕开连接池直接访问数据库,需要 DBA 审核特批。
  • 使用连接池时禁止修改连接状态,包括修改连接参数,修改搜索路径,更换角色,更换数据库。万不得已修改后必须彻底销毁连接,将状态变更后的连接放回连接池会导致污染扩散。严禁使用 pg_dump 通过 Pgbouncer 转储数据。

为查询语句配置主动超时

  • 应用应当为所有的语句配置主动超时,超时后主动取消请求,避免雪崩。(Go context)
  • 周期性执行的语句,必须配置小于执行周期的超时 Timeout,避免雪崩。
  • HAProxy 配置有 24 小时连接默认超时,用于滚动过期长连接。请不要在离线实例上运行执行时间超过1天的 SQL,有此需求由DBA特批调整。

关注复制延迟

  • 应用必须意识到主从之间的同步延迟,并妥善处理好复制延迟超出合理范围的情况。
  • 常规情况下,复制延迟在 100µs / 几十KB 的数量级,但是在极端情况下,从库可能会出现分钟/小时级的复制延迟,应用应当知晓这种现象,并有相应的降级方案 —— 选择从主库读取,稍后重试,或直接报错。

重试失败的事务

  • 查询可能因为并发争用,管理员命令等原因被杀死,应用需要意识到这一点,并在必要时重试。
  • 应用在数据库大量报错时可以触发断路器熔断,避免雪崩。但要注意区分错误的类型与性质。

掉线重连

  • 数据库连接可能因为各种原因被中止,应用必须有掉线重连机制。
  • 可以使用SELECT 1作为心跳包查询,检测连接的有消息,并定期保活。

在线服务应用代码禁止执行DDL

  • 生产应用严禁执行 DDL,不要在应用代码里搞个大新闻。
  • 例外场景:为分区表创建新的时间分区,可由应用谨慎管理。
  • 特殊例外:办公系统使用的数据库,例如 Gitlab / Jira/ Confluence 等可以授予应用 DDL 权限。

SELECT语句显式指定列名

  • 避免使用 SELECT *,或在 RETURNING 子句中使用 *。请使用具体的字段列表,不要返回用不到的字段。当表结构发生变动时(例如,新值列),使用列通配符的查询很可能会发生列数不匹配的错误。
  • 一些表的字段经过维护之后,顺序会发生变化,例如:将 INTEGER 主键 id 升级为 BIGINT 后,id 的列顺序会到最后一列。此问题只能在维护迁移时择机修复,研发应当克制调整列顺序的强迫症,并在 SELECT 语句中显式指定列的顺序
  • 例外:当存储过程返回具体的表行类型时,允许使用通配符。

禁止在线查询全表扫描

  • 例外情况:常量极小表,极低频操作,表/返回结果集很小(百条记录/百KB内)。
  • 在首层过滤条件上使用诸如!=, <>的否定式操作符会导致全表扫描,必须避免。

禁止在事务中长时间等待

  • 开启事务后必须尽快提交或回滚,超过10分钟的IDEL IN Transaction将被强制杀死。
  • 应用应当开启 AutoCommit,避免BEGIN之后没有配对的ROLLBACKCOMMIT
  • 尽量使用标准库提供的事务基础设施,不到万不得已不要手动控制事务。

使用 count 计数时的注意事项

  • count(*)统计行数的标准语法,与空值无关。
  • count(col)统计的是col列中的非空记录数。该列中的NULL值不会被计入。
  • count(distinct col)col列除重计数,同样忽视空值,即只统计非空不同值的个数。
  • count((col1, col2))对多列计数,即使待计数的列全为空也会被计数,(NULL,NULL)有效。
  • a(distinct (col1, col2))对多列除重计数,即使待计数列全为空也会被计数,(NULL,NULL)有效。

使用聚合函数的注意事项

  • 除了count之外的所有聚合函数都会忽略空值输入,因此当输入值全部为空时,结果是NULL。但count(col)在这种情况下会返回 0,是一个例外。
  • 如果聚集函数返回空并不是期望的结果,使用 coalesce 来设置缺省值。

谨慎处理空值

  • 明确区分零值与空值,空值使用IS NULL进行等值判断,零值使用常规的=运算符进行等值判断。
  • 空值作为函数输入参数时应当带有类型修饰符,否则对于有重载的函数将无法识别使用何者。
  • 注意空值比较逻辑:任何涉及到空值比较运算结果都是unknown,需要注意unknown参与布尔运算的逻辑:
    • andTRUE or UNKNOWN会因为逻辑短路返回TRUE
    • orFALSE and UNKNOWN会因为逻辑短路返回FALSE
    • 其他情况只要运算对象出现UNKNOWN,结果都是UNKNOWN
  • 空值与任何值的逻辑判断,其结果都为空值,例如NULL=NULL返回结果是NULL而不是TRUE/FALSE
  • 涉及空值与非空值的等值比较,请使用``IS DISTINCT FROM 进行比较,保证比较结果非空。
  • 空值与聚合函数:聚合函数当输入值全部为NULL时,返回结果为NULL。

注意序列号空缺

  • 当使用 Serial 类型时,INSERTUPSERT等操作都会消耗序列号,该消耗不会随事务失败而回滚。
  • 当使用整型 INTEGER 作为主键,且表存在频繁插入冲突时,需要关注整型溢出的问题。
  • 不推荐使用序列号生成身份列,考虑使用 GENERATED ALWAYS AS IDENTITY 代替。

使用游标后必须及时关闭

重复查询使用准备语句

  • 重复的查询应当使用准备语句(Prepared Statement),消除数据库硬解析的CPU开销。低于 1.21 版本的 Pgbouncer 无法在事务池化模式中支持此功能,请特别注意。
  • 准备语句会修改连接状态,请注意连接池对于准备语句的影响。

选择合适的事务隔离等级

  • 默认隔离等级为读已提交,适合大多数简单读写事务,普通事务选择满足需求的最低隔离等级。
  • 需要事务级一致性快照的写事务,请使用可重复读隔离等级。
  • 对正确性有严格要求(例如与钱有关)的写入事务,使用可序列化隔离等级。
  • 在RR与SR隔离等级出现并发冲突时,应用应当视错误类型进行积极的重试。

判断结果存在性不要使用count

  • 使用 SELECT 1 FROM tbl WHERE xxx LIMIT 1 判断是否存满足条件的列,要比Count快。
  • 可以使用 SELECT exists(SELECT * FROM tbl WHERE xxx LIMIT 1) 将存在性结果转换为布尔值。

使用RETURNING子句一次性取回修改后的结果

  • RETURNING 子句可以在 INSERTUPDATEDELETE 语句后使用,有效减少数据库交互次数。

使用UPSERT简化逻辑

  • 当业务出现插入-失败-更新的操作序列时,考虑使用 UPSERT 替代。
  • 但请注意,UPSERT 即使没有成功插入,也会消耗序列号。

利用咨询锁应对热点并发

  • 针对单行记录的极高频并发写入(秒杀),应当使用咨询锁对记录ID进行锁定。
  • 如果能在应用层次解决高并发争用,就不要放在数据库层面进行。

优化IN操作符

  • 使用 EXISTS 子句代替IN操作符,性能更佳。
  • 使用 =ANY(ARRAY[1,2,3,4]) 代替 IN (1,2,3,4),效果更佳。
  • 控制参数列表的大小,原则上不要超过1万个,超过时可以考虑分批处理。

不建议使用左模糊搜索

  • 左模糊搜索WHERE col LIKE '%xxx'无法充分利用B树索引,如有需要,可用 reverse 表达式函数索引。

使用数组代替临时表

  • 考虑使用数组替代临时表,例如在获取一系列ID的对应记录时。=ANY(ARRAY[1,2,3]) 要比临时表JOIN好。

0x04 管理规范

使用 Pigsty 搭建 PostgreSQL 集群与基础设施

  • 生产环境统一使用 Pigsty 主干版本,在 x86_64 机器, CentOS 7.9 / RockyLinux 8.8 操作系统上部署数据库。
  • pigsty.yml 配置文件通常包含了高度敏感的重要机密信息,应当使用 git 进行版本化管理,并严格控制访问权限。
  • files/pki 内生成的 CA 私钥与其他证书应当妥善保管,定期将备份至安全区域存储归档,并严格控制访问权限。
  • 所有密码都不允许使用默认值,确保都已经修改为强度足够的新密码。
  • 严格控制管理节点与配置代码仓库的的访问权限,仅限 DBA 登陆与访问。

监控系统是必选项

  • 任何部署必须有一套监控系统,生产环境至少使用两套 Infra 节点以提供冗余。

根据需求合理规划集群架构

  • 任何由DBA管理的生产数据库集群,必须带有至少一个在线从库,用于在线故障切换。
  • 默认使用 oltp 模板,分析类数据库使用 olap 模板,财务库使用 crit 模板,小微虚拟机(四核内)使用 tiny 模板。
  • 年增数据量超过1TB的业务,或者写入 TPS 超过3~5万的集群,可以考虑搭建水平分片集群。

使用 Patroni 与 Etcd 配置集群高可用

  • 生产数据库集群使用 Patroni 作为高可用组件,使用 etcd 作为 DCS。
  • etcd 使用专用虚拟机集群,3 ~ 5 个节点,严格打散分布在不同机柜上。
  • 必须开启 Patroni Failsafe 模式,确保 etcd 故障时集群主库可以继续工作。

使用 pgBackRest 与 MinIO 配置集群PITR

  • 生产数据库集群使用 pgBackRest 作为备份恢复/PITR方案,使用 MinIO 作为备份存储仓库。
  • MinIO 使用多节点多盘集群,亦可使用 S3 / OSS / COS 服务代替,冷备份必须设置密码加密。
  • 所有数据库集群每天进行一次本地全量备份,保留最近一周的备份与WAL,每隔一月存留一个全备。
  • 出现 WAL 归档错误时,应当及时检查备份仓库并排查问题。

核心业务数据库配置注意事项

  • 核心业务集群至少需要配置两个在线从库,其中一个为专用离线查询实例。
  • 核心业务集群需要搭建一套延迟24小时的延迟从库集群,用于应急数据恢复。
  • 核心业务集群通常采用异步提交,与钱有关则采用同步提交。

财务数据库配置注意事项

  • 财务数据库集群需要至少两个在线从库,其中一个为专用同步 Standby 实例,并启用 Standby 服务接入。
  • 与钱有关的库必须使用 RPO = 0 的 crit 模板,启用同步提交确保数据零丢失,视情况启用 Watchdog。
  • 与钱有关的库必须强制打开数据校验和,视情况打开全量 DML 日志。

使用合理的字符编码与本地化配置

  • 必须使用 UTF8 字符编码,严格禁止使用其他任何字符编码。
  • 必须使用 C 作为 LC_COLLATE 默认排序规则,有特殊需求必须在DDL/查询子句中显式指定来实现。
  • 字符集 LC_CTYPE 默认使用 en_US.UTF8,一些扩展依赖字符集信息方可正常工作,如 pg_trgm

业务数据库管理注意事项

  • 同一个集群内允许创建多个不同的数据库,必须使用 Ansible 剧本新建业务数据库。
  • 所有业务数据库都必须同步存在于 Pgbouncer 连接池中。

业务用户管理注意事项

  • 不同的业务/服务必须使用不同的数据库用户,必须使用 Ansible 剧本新建业务用户。
  • 所有生产业务用户都必须同步存在于 Pgbouncer 连接池的用户列表文件中。
  • 个人用户应当设置默认有效期为 90 天的密码并定时更换。
  • 个人用户只允许从跳板机访问有权限的集群 Offline 实例,或带有 pg_offline_query 的从库。

扩展插件管理注意事项

  • 安装新扩展时,必须先在集群所有实例中使用 yum/apt 安装对应大版本的扩展插件二进制软件包。
  • 启用扩展前,需要确认扩展是否需要加入 shared_preload_libraries ,如果需要应当安排滚动重启。
  • 注意 shared_preload_libraries 优先级顺序, citustimescaledbpgml 通常要放在最前面。
  • pg_stat_statementsauto_explain 是必选插件,必须在所有集群中启用。
  • 安装扩展统一使用 dbsu 进行,在业务数据库中 CREATE EXTENSION 执行创建。

数据库XID与年龄注意事项

  • 关注数据库与表的年龄,避免XID事务号用尽。使用超过 20% 应当关注,超过 50% 应当立即介入处理。
  • 处理 XID 时,按年龄从大到小顺序挨个对表执行 VACUUM FREEZE

数据库表与索引膨胀注意事项

  • 关注表与索引的膨胀率,避免索引性能劣化,使用 pg_repack 在线处理表/索引膨胀问题。
  • 一般情况下,膨胀率超过 50% 的索引与表可以考虑进行重整处理。
  • 处理超过 100GB 的表膨胀时,应当特别注意,并挑选业务低谷时进行。

数据库重启注意事项

  • 重启数据库前,执行 CHECKPOINT 两次,强制脏页刷盘,可加速重启过程。
  • 重启数据库前,执行 pg_ctl reload 重载配置确认配置文件正常可用。
  • 重启数据库可使用 pg_ctl restart 或 patronictl 同时重启整个集群。
  • 严禁使用 kill -9 关闭任何数据库进程。

复制延迟注意事项

  • 监控复制延迟,使用复制槽时更必须十分留意。

新从库数据预热

  • 高负载业务集群添加新从库实例时,应当对新数据库实例进行预热,逐步调整并应用 HAProxy 实例权重,分梯度上量:4,8,16,32,64,100。可以使用 pg_prewarm 将热数据加载至内存。

数据库发布流程

  • 线上数据库发布需要经过研发自测,主管审核,QA审核(可选),DBA审核几个评估阶段。
  • 研发自测阶段,应当由研发确保变更在开发、预发环境执行正确无误。
    • 如果是新建表,应当给出记录数量级,数据日增量预估值,读写吞吐量级预估。
    • 如果是新建函数,应当给出平均执行时间与极端情况说明。
    • 如果是模式变更,必须梳理清楚所有上下游依赖。
    • 如果是数据变更,记录订正,必须给出回滚 SQL。
  • 研发 Team Leader 需要对变更进行评估与审核,对变更内容负责。
  • DBA对发布的形式与影响进行评估与审核,提出审核意见,打回或统一执行

数据工单格式

  • 数据库变更通过平台进行,每个变更一个工单。
  • 标题清晰:某某业务需在 xx 库执行 yy 动作。
  • 目标明确:每个步骤需要在哪些实例上执行哪些操作,结果如何校验。
  • 回滚方案:任何变更都需要提供回滚方案,新建也需要提供清理脚本。
  • 任何变更都需要记录归档,有完善的审批记录,首先由研发上级TL Review 审批通过后由 DBA 审批。

数据库变更发布注意事项

  • 使用统一的发布窗口,每天 16:00 统一收集当日变更依次执行;16:00点后TL确认的需求将顺延至第二天执行。19:00 后不允许数据库发布,紧急发布请TL做特殊说明,抄送CTO审批同意后执行。

  • 数据库 DDL 变更 DML 变更统一使用管理员用户 dbuser_dba 远程执行,确保默认权限正常工作。

  • 业务管理员在自行执行 DDL 时,必须SET ROLE dbrole_admin 后再执行发布,确保默认权限。

  • 任何变更都需要有回滚预案方可执行,极个别无法回滚的操作需要特别谨慎处理(例如枚举加值)

  • 数据库变更使用 psql 命令行工具,连接到集群主库执行,使用 \i 执行脚本或 \e 手工分批执行。

删除表注意事项

  • 生产数据表 DROP 应当首先重命名,冷却 1~3 天确认没有访问后再移除。
  • 清理表时必须梳理所有依赖,包括直接间接依赖的对象:触发器,外键引用等。
  • 待删除的临时表通常放置于 trash Schema 中,通过 ALTER TABLE SET SCHEMA 修改模式名。
  • 高负载业务集群中,移除特别大的表 (> 100G) 时挑选业务低谷进行,避免抢占 I/O 。

创建与删除索引注意事项

  • 必须使用 CREATE INDEX CONCURRENTLY 并发创建索引,使用 DROP INDEX CONCURRENTLY 并发移除索引。
  • 重建索引时,总是先创建新索引,再移除旧索引,并修改新索引名与旧索引保持一致。
  • 创建索引失败后,应当及时移除 INVALID 的索引,修改索引后,使用 analyze 重新收集表上的统计数据。
  • 业务空闲时,可以启用并行索引创建,并设置 maintenance_work_mem 为更大的值加速索引创建。

审慎地进行模式变更

  • 尽可能避免整表重写式的变更,1GB 以内的表允许全表重写,DBA 应当在变更时告知所有相关业务方。
  • 向现有表中添加新列时,应当避免在默认值中使用 VOLATILE 的函数,避免全表重写。
  • 变更列类型时,必要时应当重建所有依赖该类型的函数,视图,并 ANALYZE 刷新统计信息。

控制数据写入的批次规模

  • 大批量写入操作应当切分为小批量进行,避免一次产生大量WAL或占用 I/O。
  • 大批量 UPDATE 后,执行 VACUUM 回收死元组占用的空间。
  • 执行 DDL 语句本质是对系统目录的修改,同样需要控制一个批次内的DDL语句数量。

数据加载注意事项

  • 使用COPY加载数据,如有需要可以并行执行。
  • 加载数据前可以临时关闭autovacuum,按需禁用触发器,并在加载完后再建立约束与索引。
  • 调大 maintenance_work_mem,增大max_wal_size
  • 加载完成后执行vacuum verbose analyze table

数据库迁移、大版本升级注意事项

  • 生产环境统一使用标准迁移搭建剧本逻辑,通过蓝绿部署实现不停机集群迁移、大版本升级等需求。
  • 对于停机时间没有要求的集群,可以使用 pg_dump | psql 逻辑导出导入的方式停机升级。

数据误删/误更新处理流程

  • 事故发生后,立即评估是否需要停机止血,评估影响规模,决定处理手段。
  • 研发侧如有办法恢复,优先由研发自行通过 SQL 发布进行订正;否则使用 pageinspectpg_dirtyread 从坏表中抢救数据。
  • 若有延迟从库,从延时从库中抽取数据进行修复。首先确认误删时间点,推进延迟从库至该 XID 后抽取数据。
  • 大面积误删误写,经与业务沟通同意后,执行原地 PITR 回滚至特定时间。

数据腐坏处理流程

  • 确认从库数据是否可用于恢复,若从库数据无恙可先 Switchover 至从库。
  • 临时关闭 auto_vacuum ,定位错误根因,替换故障磁盘并补充新从库。
  • 若系统目录损坏,或使用 pg_filedump 从表二进制文件中恢复数据。
  • 若 CLOG 损坏,使用 dd 生成仿造提交记录。

数据库连接打满注意事项

  • 出现连接打满现象(雪崩)时,立即使用杀连接查询治标止损:pg_cancel_backendpg_terminate_backend
  • 使用 pg_terminate_backend 中止所有普通后端进程,从每秒一次(psql \watch 1)开始。并从监控系统确认连接情况,如果继续堆积,则不断提高杀连接查询的执行频次,例如每 0.1 秒一次,直到不再堆积为止。
  • 从监控系统确认止血后,尝试停止杀连接,若重新出现堆积则立即恢复杀连接。立即分析根因并进行相应处理(升配,限流,加索引等)

PostgreSQL 17 发布:摊牌了,我不装了!

一年一度的 PostgreSQL 大版本发布来了!这次的 PostgreSQL 17 ,又给我们带来了什么惊喜呢?

在这次大版本发布注记中, PostgreSQL 全球社区直接摊牌了 —— 不好意思,我不装了 —— “现在PG就是世界上最先进的开源数据库,已经是各种规模组织的首选开源数据库了”。虽然没有指名道姓,但官方已经无限接近喊出“干翻顶级商业数据库”(Oracle)的口号了。

news.webp

在年初发表的 《PostgreSQL 正在吞噬数据库世界》中,我提出 可扩展性 是 PostgreSQL 独一无二的核心优势。 很高兴地看到这一点在短短半年中,就成为了 PostgreSQL 社区的关注焦点与共识,并在 PGCon.Dev 2024 与本次 PostgreSQL 17 发布中得到充分的体现。

关于新特性,我先前在 《PostgreSQL 17 Beta1 发布!牙膏管挤爆了!》中已经有过介绍,在此就不再赘述了。 这个大版本有很多新特性,当然要说最让我印象深刻的是, PG竟然能在原本就已经非常强悍的性能 基础上让写入吞吐再次翻倍 —— 朴实无华的强悍

但比起具体的功能特性,我认为 PG 社区最大的转变发生在心态与精神上 —— 在这次发布通告中,PostgreSQL 去掉了原本 Slogan “世界上最先进的开源关系型数据库” 中的 “关系型” 三个字定语,直接变成了 “世界上最先进的开源数据库”。 并且在最后 “关于PostgreSQL” 的部分说到:“PG 的功能集,高级特性,可扩展性,安全性,稳定性已经比肩甚至超越了顶级商业数据库”。所以我想 “开源” 这个定语用不了多久也许就可以一同去掉,变成 “世界上最先进的数据库” 了。

PostgreSQL 这头巨兽已经觉醒了 —— 它不再是过去那种佛系与世无争的样子,精神面貌焕然一新,转换为一种积极进取的姿态 —— 它已经做好了接管与征服整个数据库世界的心理建设与动员准备。 而无数资本也已经涌入 PostgreSQL 生态,PG 系的 Startup 几乎拿走了数据库领域融资的全部 New Money。PostgreSQL 势必成为数据库领域一统天下的 “Linux 内核,DBMS 的纷争也许在未来会内化为 PostgreSQL 发行版内战,就让我们拭目以待吧。

eco.png


原文:PostgreSQL 17 发布注记

PostgreSQL 全球开发组 今天正式(2024-09-26)宣布了 PostgreSQL 17 的正式发布,这是世界上最先进的开源数据库的最新版本。

备注:是的,“关系型”定语已经去掉了,就是世界上最先进的开源数据库

PostgreSQL 17 建立在数十年的开源开发模式基础上,在不断提升性能与可伸缩性的同时,也在不断适应数据访问与存储的新兴模式。 本次 PostgreSQL 发布带来了显著的整体性能提升,例如,VACUUM 内存管理的彻底改进、存储访问优化、高并发工作负载改进、批量加载与导出加速、以及索引查询执行的改进等。 PostgreSQL 17具备能够同时惠及新型工作负载和关键核心系统的特性,例如:新增的 SQL/JSON 的 JSON_TABLE 命令改善了开发者体验;而对逻辑复制的改进,则简化了高可用架构与大版本升级的管理负担。

PostgreSQL 核心团队成员 Jonathan Katz 表示:“PostgreSQL 17 展现了全球开源社区如何协同构建,改善功能,帮助位于数据库旅途中不同阶段的用户”。“无论是针对大规模数据库运维的改进,还是基于卓越开发者体验的新特性,PostgreSQL 17 都将为您带来更好的数据管理体验。”

PostgreSQL 是一款以可靠性、稳健性和可扩展性著称的创新型数据管理系统,受益于全球开发者社区超过 25 年的开源开发,已成为各类组织的首选开源关系型数据库


系统性能的全面提升

PostgreSQL 的 vacuum 进程对于系统健康运行至关重要,然而 vacuum 操作是需要消耗服务器实例资源的。PostgreSQL 17 引入了一种新的 vacuum 内部内存结构,内存消耗量降至原本的 1/20。这不仅提高了 vacuum 的速度,还减少了对共享资源的占用,为用户的工作负载释放出更多可用资源。

PostgreSQL 17 也继续在 I/O 层面上不断优化性能。由于对预写日志(WAL)处理的改进,高并发工作负载的写入吞吐量可能有高达两倍的提升。此外,新的流式 I/O 接口加快了顺序扫描(读取表中所有数据)以及 ANALYZE 更新 Planner 所需统计信息的速度。

PostgreSQL 17 也在查询执行方面改善了性能。对于使用 B-tree 索引(PostgreSQL 默认的索引方法)的 IN 子句查询,性能有所提高。此外,BRIN 索引现在支持并行构建。 PostgreSQL 17 在查询规划方面进行了多项改进,包括对 NOT NULL 约束的优化,以及对 CTE(WITH 查询)处理的改进。本次发布中,使用 SIMD(单指令多数据)加速计算得到了更广泛地应用,例如在 bit_count 函数中使用 AVX-512 指令。


进一步丰富的开发者体验

PostgreSQL 是 第一个添加 JSON 支持的关系型数据库(2012年),而 PostgreSQL 17 进一步完善了对 SQL/JSON 标准的实现。JSON_TABLE 特性现已在 PostgreSQL 17 中可用 —— 它允许开发者将 JSON 数据转换为标准的 PostgreSQL 表。 PostgreSQL 17 现在支持 SQL/JSON 标准的 构造函数JSONJSON_SCALARJSON_SERIALIZE)和 查询函数JSON_EXISTSJSON_QUERYJSON_VALUE),为开发者提供了更多种类的与 JSON 数据交互的方式。 本次发布添加了更多种类的 jsonpath 表达式,重点是将 JSON 数据转换为原生的 PostgreSQL 数据类型,包括数值、布尔值、字符串和日期/时间类型。

PostgreSQL 17 为 MERGE (带条件版本的 UPDATE)添加了更多功能,包括 RETURNING 子句,和更新 视图 的能力。 此外,PostgreSQL 17 中批量加载与导出数据的能力得到加强,例如,在使用 COPY 命令导出大量数据时,性能提升高达两倍。当源端和目标编码匹配时,COPY 性能也有所提升,而且 COPY 命令包含一个新选项 ON_ERROR,允许在插入错误时继续导入。

此次发布还扩展了对分区数据和分布在远端 PostgreSQL 实例上的数据的管理功能。PostgreSQL 17 支持在分区表上使用标识列(Identity Columns)和 EXCLUDE 约束。 用于在远程 PostgreSQL 实例上执行查询的 PostgreSQL 外部数据源包装器(postgres_fdw)现在可以将 EXISTSIN 子查询下推到远程服务器,以实现更高效的处理。

PostgreSQL 17 还包含一个内置的、平台无关的、不可变的排序规则提供者,以确保排序规则的不可变性,并提供了类似于 C 排序规则的排序语义,但使用 UTF-8 编码而非 SQL_ASCII。使用这个新的排序规则提供者,可以保证您的文本查询无论在哪里的 PostgreSQL 上运行,都能返回相同的排序结果。


针对高可用与大版本升级的逻辑复制改进

在许多用例中,逻辑复制 用于实时传输数据。 然而,在 17 版本之前,想要执行大版本升级的用户必须先删除掉逻辑复制槽,并需要在升级后将数据重新同步到订阅者。 从 PostgreSQL 17 开始,用户不需要先删除逻辑复制槽了,因而简化了使用逻辑复制时的大版本升级过程。

PostgreSQL 17 现在包含了针对逻辑复制的故障切换能力,使其在高可用环境中部署时更为可靠。 此外,PostgreSQL 17 引入了命令行工具 pg_createsubscriber,用于将物理从库转换为一个新的逻辑从库。


更多面向安全与运维的管理选项

PostgreSQL 17 进一步扩展了用户对数据库系统全生命周期的管理能力。PostgreSQL 提供了一个新的 TLS 选项 sslnegotiation,允许用户在使用 ALPN(在 ALPN 目录中注册为 postgresql)时执行直接 TLS 握手。 PostgreSQL 17 还添加了 预置角色 pg_maintain,赋予普通用户执行维护操作的权限。

PostgreSQL 自带的备份工具 pg_basebackup 现在支持增量备份,并添加了命令行功能程序 pg_combinebackup 用于重建全量备份。 此外,pg_dump 新增了一个名为 --filter 的选项,允许您在生成转储文件时,选择要包含的对象。

PostgreSQL 17 还增强了监控和分析功能。EXPLAIN 命令现在会显示本地块读写I/O耗时,并包含两个新选项:SERIALIZEMEMORY,可以显示用于网络传输的数据转换耗时以及使用的内存量。 PostgreSQL 17 现在还会报告 索引 VACUUM 的进度, 并添加了新的系统视图 pg_wait_events,在与 pg_stat_activity 视图结合使用时可以更深入地了解活动会话的等待原因。


其他功能

PostgreSQL 17 中还添加了许多其他新功能和改进,可能会对您的用例有所帮助。请参阅 发行注记 以查阅新功能和变更的完整列表。


关于 PostgreSQL

PostgreSQL 是全世界最先进的开源数据库,拥有着一个由成千上万的用户、贡献者、公司和组织组成的全球社区。它始于加州大学伯克利分校,有着超过 35 年的工程与开发历史。 PostgreSQL 以无与伦比的开发速度持续发展:PostgreSQL 提供成熟的功能集不仅比肩能顶级的专有商业数据库系统,在高级数据库功能、可扩展性、安全性和稳定性方面上甚至超越了它们。

译注:说的就是你呀,Oracle


关于 Pigsty

顺带一提,紧随 PostgreSQL 17 发布的 Pigsty v3.0.3 已经正式支持使用 PostgreSQL 17 内核,欢迎试用。

Pigsty 是开源免费,本地优先,开箱即用的 PostgreSQL RDS,允许用户在本地一键拉起生产级的 PG 云数据库服务,并带有开箱即用的 340 个PG扩展插件,故障自愈的高可用,顶级监控系统,PITR备份恢复,IaC命令行工具,SOP管理预案的完整解决方案。

pigsty-home.png


其他参考阅读

德哥在他的博客中已经解读了许多关于 PostgreSQL 17 的新功能特性,是进一步了解 PostgreSQL 17 新功能特性的好资源:

PostgreSQL 17 正式发布, 要不要升?

支持块级别增量备份与恢复:

  • 《PostgreSQL 17 preview - 内置块级别物理增量备份(INCREMENTAL backup/pg_combinebackup)功能》
  • 《PostgreSQL 17 preview - Add new pg_walsummary tool》
  • 《PostgreSQL 17 preview - Add new function pg_get_wal_summarizer_state() 分析为聚合入 pg_wal/summaries 的pid内存中的wal片段信息》
  • 《PostgreSQL 17 preview - 增量备份patch: Add the system identifier to backup manifests》

支持逻辑复制failover、switchover:

  • 《PostgreSQL 17 preview - pg_upgrade大版本升级支持保留逻辑订阅全部信息 (preserve the full subscription’s state)》
  • 《PostgreSQL 17 preview - 主库视图 pg_replication_slots.conflict_reason 支持逻辑复制冲突原因跟踪》
  • 《PostgreSQL 17 preview - 支持逻辑复制槽failover to 流复制standby节点. pg_create_logical_replication_slot(... failover = true|false ...)
  • 《PostgreSQL 17 preview - preparation for replicating unflushed WAL data》
  • 《PostgreSQL 17 preview - sync logical replication slot LSN, Failover & Switchover》
  • 《PostgreSQL 17 preview - Add a new slot sync worker to synchronize logical slots》
  • 《PostgreSQL 17 preview - 增加GUC standby_slot_names , 保证这些standby已接收并flush所有逻辑slot向下游发送逻辑数据对应的WAL》
  • 《PostgreSQL 17 preview - pg_createsubscriber支持将物理从库转换为逻辑从库》
  • 《PostgreSQL 17 preview - 跟踪slot断联时间戳pg_replication_slots.inactive_since

支持COPY错误处理:

  • 《PostgreSQL 17 preview - Add new COPY option SAVE_ERROR_TO (copy跳过错误行)》
  • 《PostgreSQL 17 preview - pg_stat_progress_copy Add progress reporting of skipped tuples during COPY FROM》
  • 《PostgreSQL 17 preview - COPY LOG_VERBOSITY notice ERROR信息》

JSON类型处理能力增强:

  • 《PostgreSQL 17 preview - Implement various jsonpath methods》
  • 《PostgreSQL 17 preview - JSON_TABLE: Add support for NESTED paths and columns》

vacuum性能改进:

  • 《PostgreSQL 17 preview - 增加index vacuum 进度打印》
  • 《PostgreSQL 17 preview - Optimize vacuuming of relations with no indexes 降低wal产出》
  • 《PostgreSQL 17 preview - 解除vacuumdb,clusterdb,reindexdb的某些options组合限制》
  • 《PostgreSQL 17 preview - 使用TidStore数据结构存储dead tupleids, 提升vacuum效率, 为什么PG单表不建议超过8.9亿条记录?》
  • 《PostgreSQL 17 preview - vacuum_buffer_usage_limit调大默认值, 减少vacuum造成的wal flush, 提升vacuum速度》

index 性能优化:

  • 《PostgreSQL 17 preview - Allow Incremental Sorts on GiST and SP-GiST indexes》
  • 《PostgreSQL 17 preview - btree index backward scan (order by desc 场景)优化》
  • 《PostgreSQL 17 preview - Allow parallel CREATE INDEX for BRIN indexes》

高并发锁竞争优化:

  • 《PostgreSQL 17 preview - 优化wal insert lock, 提升高并发写入吞吐性能》
  • 《PostgreSQL 17 preview - Reduce rate of walwriter wakeups due to async commits》
  • 《PostgreSQL 17 preview - WAL锁竞争优化 - reading WAL buffer contents without a lock, Additional write barrier in AdvanceXLInsertBuffer()》

性能优化:

  • 《PostgreSQL 17 preview - 函数parser阶段优化, 函数guc into lists避免parser》
  • 《PostgreSQL 17 preview - 删除snapshot too old特性, 将引入新实现方式》
  • 《PostgreSQL 17 preview - postgres_fdw 支持semi-join pushdown (exists (…))》
  • 《PostgreSQL 17 preview - 将unstable hashfunc剥离, 提升in-memory场景哈希计算性能和算法自由度》
  • 《PostgreSQL 17 preview - 优化器增强, group by支持Incremental Sort, GUC: enable_group_by_reordering》
  • 《PostgreSQL 17 preview - 引入新的smgr, 优化bulk loading》
  • 《PostgreSQL 17 preview - Add --copy-file-range option to pg_upgrade
  • 《PostgreSQL 17 preview - 减少分区表partitionwise join内存消耗》
  • 《PostgreSQL 17 preview - 使用 Merge Append 提升 UNION 性能》
  • 《PostgreSQL 17 preview - pg_restore --transaction-size=N 支持N个对象封装为一个事务提交》

新增GUC参数:

  • 《PostgreSQL 17 preview - Add GUC: event_triggers . for temporarily disabling event triggers》
  • 《PostgreSQL 17 preview - Allow ALTER SYSTEM to set unrecognized custom GUCs.》
  • 《PostgreSQL 17 preview - XX000 内部错误 backtrace, add GUC backtrace_on_internal_error》
  • 《PostgreSQL 17 preview - allow_alter_system GUC控制 是否允许alter system 修改postgresql.auto.conf
  • 《PostgreSQL 17 preview - 新增 GUC: or_to_any_transform_limit 控制OR to ANY转换》
  • 《PostgreSQL 17 preview - 新增 GUC trace_connection_negotiation : 跟踪客户端 SSLRequest or GSSENCRequest packet》

SQL语法、函数功能增强:

  • 《PostgreSQL 17 preview - plpgsql 支持定义 %TYPE %ROWTYPE 数组变量类型》
  • 《PostgreSQL 17 preview - 支持修改生成列表达式 alter table ... ALTER COLUMN ... SET EXPRESSION AS (express)
  • 《PostgreSQL 17 preview - Support identity columns in partitioned tables》
  • 《PostgreSQL 17 preview - 简化exclude约束用法, 对primary key,unique约束增加without overlaps可选项》
  • 《PostgreSQL 17 preview - Add RETURNING support to MERGE》
  • 《PostgreSQL 17 preview - 增加uuid功能函数: 提取UUID值里面的时间戳 和 生成UUID值的函数版本》
  • 《PostgreSQL 17 preview - 新增返回某个范围内的随机数的随机函数random(min, max)
  • 《PostgreSQL 17 preview - Add support for MERGE ... WHEN NOT MATCHED BY SOURCE
  • 《PostgreSQL 17 preview - 使用pg_basetype 获得domain类型的基础类型》
  • 《PostgreSQL 17 preview - Implement ALTER TABLE ... MERGE|SPLIT PARTITION … command》

管理手段增强:

  • 《PostgreSQL 17 preview - 内置支持login event trigger》
  • 《PostgreSQL 17 preview - Add tests for XID wraparound》
  • 《PostgreSQL 17 preview - pgbench工具新增meta语法syncpipeline, pgbench: Add \syncpipeline
  • 《PostgreSQL 17 preview - 引入MAINTAIN权限及pg_maintain预制角色》
  • 《PostgreSQL 17 preview - 新增 “builtin” collation provider》
  • 《PostgreSQL 17 preview - 通过pg_wal_replay_wait()支持读写分离pool实现跨实例的读写一致性》
  • 《PostgreSQL 17 preview - transaction_timeout》

内部统计信息、系统视图增强:

  • 《PostgreSQL 17 preview - Add new parallel message type to progress reporting.》
  • 《PostgreSQL 17 preview - Add system view pg_wait_events》
  • 《PostgreSQL 17 preview - Add JIT deform_counter》
  • 《PostgreSQL 17 preview - 添加checkpoint delay等待事件》
  • 《PostgreSQL 17 preview - Add local_blk_{read|write}_time I/O timing statistics for local blocks》
  • 《PostgreSQL 17 preview - Introduce pg_stat_checkpointer》
  • 《PostgreSQL 17 preview - improve range type pg_stats》
  • 《PostgreSQL 17 preview - 增强standby节点检查点统计信息》
  • 《PostgreSQL 17 preview - Add EXPLAIN (MEMORY) to report planner memory consumption》

table access method 接口增强:

  • 《PostgreSQL 17 preview - Add support for DEFAULT in ALTER TABLE .. SET ACCESS METHOD
  • 《PostgreSQL 17 preview - 支持修改分区表access method》
  • 《PostgreSQL 17 preview - 寻找undo-based table access methods的蛛丝马迹》
  • 《PostgreSQL 17 preview - 频繁提交table access method相关patch, undo-based table access methods真的快来了吗?》
  • 《PostgreSQL 17 preview - table AM增强: Custom reloptions for table AM》

扩展接口能力增强:

  • 《PostgreSQL 17 preview - 增加alter table部分属性hook, 未来可定制化审计功能》
  • 《PostgreSQL 17 preview - 支持自定义等待事件》
  • 《PostgreSQL 17 preview - Introduce the dynamic shared memory registry (DSM 注册器)》
  • 《PostgreSQL 17 preview - 新增代码注入功能(enable-injection-points), 类似hook.》
  • 《PostgreSQL 17 preview - 引入读写原子操作函数接口with full barrier semantics》
  • 《PostgreSQL 17 preview - 支持在申请时指定动态共享内存区域初始、最大段size》
  • 《PostgreSQL 17 preview - 代码注入(injection_points)功能增强, Introduce runtime conditions》

libpq协议增强:

  • 《PostgreSQL 17 preview - libpq: Add support for Close on portals and statements , 释放绑定变量语句入口(prepared statements)》
  • 《PostgreSQL 17 preview - 增加wire protocol头文件》
  • 《PostgreSQL 17 preview - libpq新增PQchangePassword()接口, 防止alter user修改密码时明文被记录在SQL活跃会话、log、pg_stat_statements中》

PostgreSQL可以替代微软SQL Server吗?

许多人对于 PostgreSQL 生态已经发展到什么阶段并没有一个直观的印象 —— 除了 吞噬数据库世界,囊括万物的扩展生态之外,PostgreSQL 还可以直接从内核层面,替换掉 Oracle,SQL Server 与 MongoDB,当然 MySQL 就更不在话下了。

当然要说主流数据库中,暴露风险最高的是谁,那毫无疑问是微软的 SQL Server 了。MSSQL 被替代的是最彻底的 —— 直接在 WireProtocol 层面被替代了。而主导这件事的是 AWS,亚马逊云服务。


Babelfish

虽然我一直吐槽云厂商白嫖开源,但我承认这种策略是极为有效的 —— AWS 拿着开源的 PostgreSQL 和 MySQL 内核,一路杀穿数据库市场,拳打 Oracle ,脚踢微软,成为数据库市场份额毫无争议的一哥。 而这两年 AWS 更是玩了一招釜底抽薪,开发整合了一个 BabelfishPG 的扩展插件,提供“线缆协议”级别的兼容性。

marketshare.png

所谓线缆协议兼容,就是指客户端什么都不用改,依然访问 SQL Server 1433 端口,使用 MSSQL 的驱动与命令行工具(sqlcmd)访问加装 BabelfishPG 的集群就可以了。 而且更神奇的是,你依然可以使用 PostgreSQL 的协议语言语法,从原来的 5432 端口访问,和 SQL Server 的客户端并存 —— 这就给迁移带来了极大的便利条件。


WiltonDB

当然 Babelfish 并不是一个单纯的 PG 扩展插件,它对 PostgreSQL 内核进行了少量修改与适配。并通过四个扩展插件分别提供了 TSQL 语法支持,TDS 线缆协议支持,数据类型以及其他函数支持。

wiltondb.png

在不同的平台上编译打包这样的内核与扩展并不是轻松容易的一件事,因此 WiltonDB —— 一个 Babelfish 的发行版就做了这件事,将 BabelfishPG 编译打包为 EL 7/8/9 与 Ubuntu 系统,甚至 Windows 下可用的 RPM / DEB / MSI 包。


Pigsty v3

当然,只有 RPM / DEB 包,距离提供生产级的服务还依然差得太远,而在最近发布的 Pigsty v3 中,我们提供了将原生 PostgreSQL 内核替换为 BabelfishPG 的能力。

创建这样一套 MSSQL 集群,所需的不过是在集群定义中修改几个参数。然后依然是一件傻瓜式拉起 —— 类似主从搭建, 扩展安装,参数优化,用户配置,HBA规则设定,甚至是服务流量分发,都会自动根据配置文件一键拉起。

pigsty-conf.png

在使用实践上,你完全可以把 Babelfish 集群当作一套普通的 PostgreSQL 集群来使用与管理。唯一的区别就是客户端在使用 5432 PGSQL 协议的基础上,还可以选择是否要使用 1433 端口上的 TSQL 协议支持。

sqlcmd.png

例如,您可以轻松通过配置,将原本固定指向主库连接池 6432 端口的 Primary 服务重定向到 1433 端口,从而实现故障切换下的无缝 TDS / TSQL 流量切换。

mssql.png

这意味着原本属于 PostgreSQL RDS 的能力 —— 高可用,时间点恢复,监控系统,IaC管控,SOP预案,甚至无数的扩展插件都可以嫁接融合到 SQL Server 版本的内核之上。


如何迁移?

PostgreSQL 生态除了有Babelfish这样给力的内核与扩展,还有着繁荣的工具生态。如果要想从 SQL Server 或 MySQL 迁移到 PostgreSQL ,我强烈推荐一款杀手级迁移工具:PGLOADER

这款迁移工具傻瓜化到了离谱的程度,在理想的情况下,你只需要两个数据库的连接串,就可以完成迁移了。对,真的是一行多余的废话都没有。

pgloader mssql://user@mshost/dbname pgsql://pguser@pghost/dbname

有了 MSSQL 兼容内核扩展,又有了迁移工具,存量的 SQL Server 搬迁会变的非常容易。


除了 MSSQL,还有……

除了 MSSQL,PostgreSQL 生态还有旨在替代 Oracle替代:PolarDB O 与 IvorySQL,旨在替代 MongoDB 的 FerretDB 与 PongoDB。以及三百多个提供各式各样功能的扩展插件。实际上,几乎整个数据库世界都在受到 PostgreSQL 的冲击 —— 除了那些与 PostgreSQL 错开生态位(SQLite,DuckDB,MinIO),或者干脆就是 PostgreSQL 套壳(Supabase,RDS,Aurora/Polar)的数据库。

我们最近发布的开源 RDS PostgreSQL 方案 —— Pigsty 最近就支持了这些 PG 替换内核,允许用户在一套 PostgreSQL 部署中提供 MSSQL,Oracle,MongoDB,Firebase,MongoDB 的兼容性替代能力。不过限于篇幅,那就是后面几篇要介绍的内容了。

除了 MSSQL,PostgreSQL 生态还有旨在替代 Oracle替代:PolarDB O 与 IvorySQL,旨在替代 MongoDB 的 FerretDB 与 PongoDB。以及三百多个提供各式各样功能的扩展插件。

实际上,几乎整个数据库世界都在受到 PostgreSQL 的冲击 —— 除了那些与 PostgreSQL 错开生态位(SQLite,DuckDB,MinIO),或者干脆就是 PostgreSQL 套壳(Supabase,RDS,Aurora/Polar)的数据库。

dbengine.png

我们最近发布的开源 RDS PostgreSQL 方案 —— Pigsty 最近就支持了这些 PG 替换内核,允许用户在一套 PostgreSQL 部署中提供 MSSQL,Oracle,MongoDB,Firebase,MongoDB 的兼容性替代能力。

不过限于篇幅,那就是后面几篇要介绍的内容了。

谁整合好DuckDB,谁赢得OLAP世界

在 《PostgreSQL正在吞噬世界中》 一文中,我曾经抛出过这个问题:谁会最终统一数据库世界?。我认为是 PostgreSQL 生态与各种各样的扩展插件 —— 而我的判断是,要想征服 OLAP 这个最大也是最显著的数据库独立王国,这个分析扩展一定与 DuckDB 有关。

PostgreSQL 一直以来都是我最喜欢的数据库,然而我第二喜欢的数据库在这两年中从 Redis 变为了 DuckDB。DuckDB 是一个非常小巧且强大的 嵌入式 OLAP 分析数据库,在分析性能、易用性上都做到了极致水平,并且在所有数据库中有着仅次于 PostgreSQL 的可扩展性。

extensibility.webp

正如两年前开展的向量数据库扩展插件赛马一样,当下 PG 生态进行的扩展竞赛已经开始围绕 DuckDB 进行 —— “谁更好地在PG中整合DuckDB,谁就赢得OLAP世界的未来”。尽管已经有许多玩家在摩拳擦掌,但 DuckDB 官方亲自下场,毫无疑问宣告着这场竞争即将进入白热化。


DuckDB:OLAP的新兴挑战者

DuckDB 是由 Mark Raasveldt 和 Hannes Mühleisen 两位数据库研究员在荷兰阿姆斯特丹的国家数学与计算机科学研究所(Centrum Wiskunde & Informatica, CWI)开发的。CWI 不仅仅是一个研究机构,可以说是分析型数据库领域发展背后的幕后推手与功臣,是列式存储引擎与向量化查询执行的先驱。现在你能看到的各种分析数据库产品 ClickHouse,Snowflake,Databricks 背后,都有 CWI 的影子。顺便一提,Python之父龟叔也是在 CWI 时创建 Python 语言的。

然而,现在这些分析领域的先锋们自己亲自下场来做分析数据库了,他们选择了一个非常好的时机与生态位切入,搞出了 DuckDB 来。

DuckDB 的起源来自作者们对数据库用户痛点的观察:数据科学家主要使用像 Python 与 Pandas 这样的工具,不怎么熟悉经典的数据库。经常被如何连接,身份认证,数据导入导出这些工作搞的一头雾水。那么有没有办法做一个简单易用的嵌入式分析数据库给他们用呢? —— 就像 SQLite 一样。

DuckDB 整个数据库软件源代码就是一个头文件一个c++文件,编译出来就是一个独立二进制,数据库本身也就一个简单的文件。使用兼容 PostgreSQL 的解析器与语法,简单到几乎没有任何上手门槛。尽管 DuckDB 看上去非常简单,但它最了不起的一点在于 —— 简约而不简单,分析性能也是绝冠群雄。例如,在 ClickHouse 自己的主场 ClickBench 上,有着能够吊打东道主 ClickHouse 的表现。

另外非常值得称道的一点是,因为作者的薪水由政府税收支付,他们认为将自己的工作成果免费提供给任何人是他们对社会的责任。因此,DuckDB 是在非常宽松的 MIT 许可证下发布的。


我认为 DuckDB 的崛起是必然的:一个有着顶尖性能表现,而使用门槛低到地板,还开源免费的数据库,想不火都难。在 StackOverflow 2023 年的开发者调研中,DuckDB 以 0.61% 的使用率第一次进入“最流行的数据库” 榜单中(第29名,倒数第四),结果仅仅一年过去,在 2024 年度开发者调研中,它就实现了 2.3 倍的流行度增长,前进到(1.4%)与 ClickHouse (1.7%)非常接近的流行度。

used-prof-2023-2024.png

同时,DuckDB 也在用户中攒下的极好的口碑,在开发者中受欢迎与喜爱的程度(69.2%)在主要数据库中仅次于 PostgreSQL (74.5%)。如果我们观察 DB-Engine 的热度趋势,更是不难看出它在 2022 年中开始一飞冲天的狂飙增长态势 —— 虽然没法跟 PostgreSQL 这种数据库比,但目前甚至已经超过了所有 NewSQL 数据库产品的热度分了。

db-engine-duckdb.png


DuckDB的短板与其中的机遇

DuckDB 是一个可以独立使用的数据库,但更是一个嵌入式的分析数据库。嵌入式有好处也有坏处 —— DuckDB 尽管有着最强分析性能,但它最大的短板就在于薄弱的数据管理能力 —— 也就是数据科学家们不喜欢的那些东西 —— ACID,并发访问,访问控制,数据持久性,高可用,数据库导入导出,等等等,而这恰好是经典数据库的长处,也是企业级分析系统的核心痛点之一。

可以预期的是,市面上一定会很快出现一系列的 DuckDB 套壳产品来解决这里的摩擦与GAP。正好比当年 Facebook 开源了 KV 数据库 RocksDB ,无数 “新的数据库” 给 RocksDB 套了一层 SQL 解析器,就号称自己是新一代数据库去圈钱了 —— Yet another SQL Sidecar for RocksDB。 向量检索库 hnswlib 开源后,无数 “专用向量数据库” 给它套了薄薄一层皮,就去市场上圈钱了。然后搜索引擎 Lucene 和下一代替代 Tantivy 开源之后,又有无数“全文检索数据库”来给他们套壳贩卖。

ecosystem.jpg

实际上,这样的事情已经在 PostgreSQL 生态中发生了。在其他数据库产品和公司还没来得及反应之前,PG 生态已经有五个玩家下场赛马了,包括 ParadeDB 的 pg_lakehouse,国内个人开发者李红艳编写的 duckdb_fdw,CrunchyData 的 crunchy_bridge, Hydra 出品的 pg_quack;以及目前 MotherDuck 原厂也跑过来做 PG 扩展了 —— pg_duckdb


第二届PG扩展竞速比赛

这不禁让我想起了过去一年中,PG生态里向量数据库扩展的例子。AI爆火之后,PG 生态里就涌现出了至少六款向量数据库扩展( pgvectorpgvector.rspg_embeddinglaternpasepgvectorscale),并在你追我赶的赛马中卷出了新高度。最后 pgvector 在以 AWS 为代表的厂商大力投入加持之下,在其他数据库比如 Oracle / MySQL / MariaDB 姗姗来迟的糊弄版本出来之前,就已经把整个专用向量数据库细分领域给摧毁荡平了。

那么,谁会成为 PG OLAP 生态的 PGVECTOR 呢?我个人的判断还是原厂吊打同人,尽管 pg_duckdb 才刚刚新鲜出炉,甚至连 v0.0.1 版本都还没发布。但从其架构设计上,已经不难判断,它大概率会是最后的赢家。实际上这个生态赛道才刚刚展开,就立即有收敛的趋势了:

原本 Fork Citus 列存扩展的 Hydra (YC W22),在尝试构建 pg_quack 感受到 DuckDB 震撼后,立刻抛弃原有的引擎和 MotherDuck 合作,搞出来了 pg_duckdb。融合了 PG 生态经验的 Hydra 与 DuckDB 原厂弄的扩展,可以直接在数据库内丝滑地读取 PG 数据表,并使用 DuckDB 引擎进行计算,并且可以直接从文件系统/S3 上读取 Parquet / IceBerg 格式的文件,实现湖仓的效果。

hydra-pg-quack.png

同样是 YC 投的初创数据库公司 ParadeDB (YC S23),在尝试了自己用 Rust 构建类似的分析产品 pg_analytics 并取得了不俗的成绩之后,也选择改换了路线,基于 DuckDB 打造 pg_lakehouse 扩展。当然,创始人 Phillipe 在 pg_duckdb 刚刚官宣之后也立刻宣布投降,准备在 pg_duckdb 的基础上进行进一步的开发而不是当竞品。

paradedb.png

国内个人开发者李红艳开发的 duckdb_fdw 是另一条另辟蹊径的道路。不是直接利用 PG的存储引擎接口,而是直接用外部数据源包装器(FDW)的基础设施,将 PG 和 DuckDB 对接到了一起。这引发了官方亲自下场吐槽,将其作为反例批判,也许是 MotherDuck 亲自下场的一个动机:“我还在构思伟大蓝图,如何融合PG与Duck的力量,你小子动作也太快了,得给你一点官方震撼看看”。

至于 CrunchyData 搞的 cunchy_bridge ,或者其他数据库公司搞的闭源套壳扩展,我个人感觉是很难有出息的。


当然,作为 PostgreSQL 发行版 Pigsty 的作者,我的策略始终是 —— 你们赛你们的马,反正所有这些扩展我都会打包并分发给用户,让用户自己选择与决策。就好比当初向量数据库崛起的时候一样,我就把 pgvectorpg_embeddingpasepg_sparse 等等这几个最有前途的扩展打包分发出去。不管谁是最后的胜利者,反正 PG 和 Pigsty 都是摘桃子的赢家。

天下武功,唯快不破,在 Pigsty v3 中已经实装了这三个最有前途的扩展插件: pg_duckdbpg_lakehouse,以及 duckdb_fdw,当然还有 duckdb 二进制本体,开箱即用,让用户体验一个 PostgreSQL 包打天下,OLTP / OLAP 双冠全能王合体,真正 HTAP 的快乐。

StackOverflow 2024调研:PostgreSQL已经杀疯了

2024 年 StackOverflow 全球开发者调研结果已经新鲜出炉, 来自 185 个国家与地区的 6 万名开发者给出了高质量的问卷反馈。当然,作为数据库老司机,我最关注的还是 “Database” 这一项调研结果:


流行度

首先是数据库流行度:专业开发者中的数据库使用率

一项技术使用者占总体的比例,就是流行度。它的含义是:过去一年有多少比例的用户使用了这项技术。流行度代表过去一年的积累使用,是存量指标,也是最核心的事实指标。

database-used-prof.png

在使用率上,PostgreSQL 在专业开发者中以 51.9% 的惊人使用率连续三年蝉联榜首,首次过半!相比第二名的 MySQL (39.4%) 的差距进一步拉开到了 12.5 个百分点(去年这个差距是 8.5 个百分点)。

如果我们考虑全体开发人员的数据库使用情况,那么 PostgreSQL 是第二年成为世界上最流行的数据库,以 48.7% 的使用率拉开第二名 MySQL (40.3%) 8.4 个百分点(去年为 4.5 个百分点)

如果我们综合过去八年的问卷数据调查结果,将流行度画在一张散点图上,不难看出 PostgreSQL 几乎一直保持着高速线性增长。

trend-used-prof.png

在这个榜单上,有显著增长的数据库除了 PostgreSQL 还有 SQLite,DuckDB,Supabase,BigQuery,Snowflake,Databricks SQL。 这里面,BigQuery,Snowflake,以及 Databricks 属于大数据分析领域的当红炸子鸡。SQLite 和 DuckDB 属于独特的,不与关系型数据库冲突的嵌入式数据库生态位,Supabase 则是封装 PostgreSQL 作为底层核心的后端开发平台。

used-prof-2023-2024.png

而其他的的数据库,或多或少都受到了 PostgreSQL 崛起带来的冲击。


喜爱度与需求度

其次是数据库的喜爱度(红色)与需求度(蓝色):全体开发者在过去一年最喜爱与最想要使用的数据库,按需求度排序。

database-admire-desire.png


所谓“口碑”(红点),喜爱度(Loved)或欣赏度(Admired),指的是有多少比例的用户愿意继续使用此项技术,这是一个年度的“留存率”指标,可以反映用户对一项技术的看法与评价,代表了未来的增长空间。

trend-loved.png

在口碑上,PostgreSQL 依然以 74.5% 的喜爱比例第二年蝉联榜首,这里特别值得注意的是两个数据库,在过去一年中,SQLite 与 DuckDB 的喜爱度出现显著上涨,而 TiDB 的喜爱度则出现了惊人的下滑(64.33 到 48.8)。


而需求者占总体的比例,就是需求率(Wanted),或渴望度(Desired),在上图中用红点表示。它的含义是,接下来一年有多少比例的用户会实际选择使用此项技术,代表了未来一年的实际增长动能。因此在 SO 这张图上,也是按照需求度来排序的。

loved-2023-2024.png

在这一项上,PostgreSQL 是第三年蝉联榜首了,而且以惊人的优势与后来者拉开距离。也许是最近两年因为受到向量数据库需求的拉动,PostgreSQL 的需求量出现了一个非常惊人的激增,从 2022 年的 19% 飙升至 2024 年的 47%。而 MySQL 的需求度,则甚至被 SQLite 反超,从2023年的第二名跌落至第三。

trend-wanted.png

需求量较为精确地反应着明年的增量(用户显式回答:“下一年中我计划使用此种数据库”),因此这里突增的需求度会很快反应到明年的流行度上来。


小结

PostgreSQL 已经连续第二年以无可争议的碾压性优势,成为了全世界最流行,最受喜爱,需求量最高的数据库。

并且根据过去八年的趋势,以及未来一年的需求预测来看,已经没有其他力量能够撼动这一点。

曾经是 PostgreSQL 最大竞争对手的 MySQL 已然颓势尽显,而其他数据库也都在不同程度上受到了 PostgreSQL 的冲击。 能继续保持增长的数据库要么与 PostgreSQL 错开了生态位,要么干脆就是改头换面或者协议兼容的 PostgreSQL。

PostgreSQL 将成为数据库世界的 Linux 内核,而 PostgreSQL 世界的发行版内战即将拉开序幕。

使用Pigsty,PG,PGVector自建Dify -- AI工作流平台

Dify 是一个生成式 AI 应用创新引擎,开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力,帮助用户轻松构建和运营生成式 AI 原生应用。

当然,像这样的一个 AI 工作流编排软件,在底下也少不得用到数据库 —— Dify 便是用 PostgreSQL 存储数据的,当然还有 Redis 缓存,与一个专用的向量数据库。Docker 镜像拉起来本地玩玩可以,生产环境部署的话,数据库肯定不能这么搞,高可用,备份,监控啥都没有。 好在 Pigsty 就提供了开箱即用的生产级高可用 PostgreSQL 集群,也正好提供了 Dify 需要用到的 Redis 与 S3 (MinIO) 服务,也提供了 Nginx 可以对外暴露 Web 服务,堪称 Dify 最佳拍档。

docker-compose.png

有了 Pigsty,你只需要用 docker compose 拉起无状态的蓝圈部分就好了,状态放在由外部服务由 Pigsty 管理。

这里我不得不吐槽一下 Dify 模板的设计,元数据都已经用 PostgreSQL 存储了,你直接加个 pgvector 不就能拿来当向量数据库了?更让人想吐槽的是 pgvector 竟然还是一个单独的镜像与容器,你直接用一个带 pgvector 的 PG 镜像不就行了? Dify “支持” 了一堆花里胡哨的向量数据库,但你既然已经选定了 PostgreSQL 了,向量数据库默认也用 pgvector 就是自然而然地选择了。同理,我觉得 Dify 官方应该考虑一下把 Redis 去掉,Celery 任务队列又不是不能用 PostgreSQL 作为后端存储,弄那么多数据库纯属吃饱了撑着。如无必要,勿增实体。

所以 Pigsty 提供的 Dify Docker Compose 模板 也对官方的样例做了一些修改,把 dbredis 两个数据库镜像给去掉了,使用由 Pigsty 管理的实例,向量数据库固定使用 pgvector,复用同一个 PostgreSQL 实例。

最后上面那个架构就被简化为无状态的:dify-apidify-webdify-worker 三个无状态容器,可以随意创建销毁。当然还有两个可选的 ssrf_proxynginx,用于提供代理与些许安全特性。 还有一点状态尾巴是 文件系统卷,存放私钥之类的东西,定期备份一下就好了,也可以使用 MinIO 替代。

参考资料:


Pigsty的准备工作

我们用 单机安装 的 Pigsty 为例,假设你有一台 IP 地址为 10.10.10.10 的机器,已经 安装好了单机 Pigsty

当然,我们需要在 Pigsty 配置文件 pigsty.yml 中定义一下我们所需的数据库集群。 这里定义了一个名为 pg-meta 的集群,其中有一个名为 dbuser_dify 的超级业务用户(它这个实现的有点挫,在 Migration 脚本里面执行了 CREATE EXTENSION ),一个安装了 pgvector 扩展插件的数据库 dify,以及一条特定的防火墙规则,允许用户通过密码从任何地方访问数据库(你也可以将其限制为docker的网段 172.0.0.0/8 之类更精确的范围)。

同时,上面还定义了一个单实例的标准 Redis 集群 redis-dify,设置了密码 redis.dify

pg-meta:
  hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
  vars:
    pg_cluster: pg-meta
    pg_users: [ { name: dbuser_dify ,password: DBUser.Dify  ,superuser: true ,pgbouncer: true ,roles: [ dbrole_admin ] } ]
    pg_databases: [ { name: dify, owner: dbuser_dify, extensions: [ { name: pgvector } ] } ]
    pg_hba_rules: [ { user: dbuser_dify , db: all ,addr: world ,auth: pwd ,title: 'allow dify user world pwd access' } ]

redis-dify:
  hosts: { 10.10.10.10: { redis_node: 1 , redis_instances: { 6379: { } } } }
  vars: { redis_cluster: redis-dify ,redis_password: 'redis.dify' ,redis_max_memory: 64MB }

这里出于演示目的,我们全部使用单实例配置,你可以参考 Pigsty 文档部署 高可用 的 PG 集群与 Redis 集群。总之,在定义完成后,使用以下命令创建 PG 和 Redis 。

bin/pgsql-add  pg-meta                # create the dify database cluster
bin/redis-add  redis-dify             # create redis cluster

当然,您也可以在现有的 PostgreSQL 集群,例如 pg-meta 上新定义业务用户与业务数据库,并通过以下命令创建:

bin/pgsql-user pg-meta dbuser_dify    # create dify biz user
bin/pgsql-db   pg-meta dify           # create dify biz database

您应该可以通过以下的连接串,访问到 PostgreSQL 与 Redis,当然连接信息请根据实际情况进行修改。

psql postgres://dbuser_dify:DBUser.Dify@10.10.10.10:5432/dify -c 'SELECT 1'
redis-cli -u redis://redis.dify@10.10.10.10:6379/0 ping

当你确认这两个连接串可用后,大功告成,你可以开始部署 Dify 了。

这里出于演示方便的原因,使用IP直连的土办法,如果是多节点的高可用 PG 集群,请参考 接入 一节。

当然,上面的部分是假设你已经是 Pigsty 用户,了解如何部署 PostgreSQL 与 Redis 集群。你可以直接跳过下一节,查看 Dify 如何配置


从零开始的一些说明

如果您已经了解如何配置使用 Pigsty,可以略过本节。

从零安装 Pigsty 需要 准备 一台符合要求的机器节点: Linux / x86_64,静态 IP,使用带有免密 sudo 权限的用户,执行以下命令:

curl -fsSL https://repo.pigsty.cc/get | bash

然后依次完成以下步骤:

cd ~/pigsty      # 下载源码包解压后进入 Pigsty 源码目录,完成后续 准备、配置、安装 三个步骤
./bootstrap      # 【可选项】用于确保 Ansible 正常安装,如果 /tmp/pkg.tgz 离线包则使用它
./configure      # 【可选项】执行环境检测,并生成相应的推荐配置文件,如果知道如何配置可以跳过

# …… 这里请修改自动生成的配置 pigsty.yml ,将上面的集群定义填入 all.children 部分内

./install.yml    # 根据生成的配置文件开始在当前节点上执行安装,使用离线安装包大概需要10分钟完成

您应当将上面的 PostgreSQL 集群与 Redis 集群定义填入 pigsty.yml 文件中,然后执行 install.yml 完成安装。

Redis安装问题

Pigsty 默认不会安装 Redis,所以您需要使用 redis.yml 剧本显式完成 Redis 安装:

./redis.yml

Docker安装问题

Pigsty 默认不会在当前节点安装 Docker,所以您需要使用 docker.yml 剧本安装 Docker。

./docker.yml

Docker Hub 被墙问题

请注意,对于中国大陆用户来说,Docker Hub 与各镜像站点目前出于封锁状态,需要 “科学上网” 才能拉取 Dify 所需的镜像,您可以考虑 docker save|load,或者为 Docker Daemon 配置代理。

要为 Docker Daemon 配置代理,您需要在 proxy_env 中指定 http_proxyhttps_proxy 环境变量,该参数会在 docker_config 任务中被写入 /etc/docker/daemon.json 中:

{
  "proxies": {
    "http-proxy": "http://192.168.x.x:8118",
    "https-proxy": "http://192.168.x.x:8118",
    "no-proxy": "localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.myqcloud.com,*.tsinghua.edu.cn"
  }
}

当然您也可以直接在配置文件中填入您的 HTTP/HTTPS 代理地址,并使用 systemctl restart docker 重启生效。

$ docker compose pull
[+] Pulling 5/5
✔ worker Skipped - Image is already being pulled by api
✔ web Pulled
✔ api Pulled
✔ ssrf_proxy Pulled
✔ nginx Pulled

配置代理后,镜像都可以成功拉取了。当然您也可以使用其他可用的镜像站点,例如 quay.io 等。


Dify的配置工作

Dify 的配置参数一如往常地放在 .env 文件中,内容如下所示:

所有参数都顾名思义,已经填入了在 Pigsty默认沙箱环境 中可以直接工作的默认值,数据库连接信息请根据您的真实配置,与上面 PG / Redis 集群配置保持一致即可。 我们建议你随便改一下这个 SECRET_KEY 字段,可以使用 openssl rand -base64 42 生成一个强密钥。

# meta parameter
DIFY_PORT=8001 # expose dify nginx service with port 8001 by default
LOG_LEVEL=INFO # The log level for the application. Supported values are `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
SECRET_KEY=sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U # A secret key for signing and encryption, gen with `openssl rand -base64 42`

# postgres credential
PG_USERNAME=dbuser_dify
PG_PASSWORD=DBUser.Dify
PG_HOST=10.10.10.10
PG_PORT=5432
PG_DATABASE=dify

# redis credential
REDIS_HOST=10.10.10.10
REDIS_PORT=6379
REDIS_USERNAME=''
REDIS_PASSWORD=redis.dify

# minio/s3 [OPTIONAL] when STORAGE_TYPE=s3
STORAGE_TYPE=local
S3_ENDPOINT='https://sss.pigsty'
S3_BUCKET_NAME='infra'
S3_ACCESS_KEY='dba'
S3_SECRET_KEY='S3User.DBA'
S3_REGION='us-east-1'

填好连接信息后,我们就可以使用 Docker Compose 拉起 Dify 服务了:

cd pigsty/app/dify && make up

使用Nginx暴露Web服务

Dify 的 Docker Compose 模板里面已经包含了一个 Nginx Server,占据了宿主机的 80 端口。如果你的这台机器就是拿来专门跑 Dify 的那没问题。如果你用的是 Pigsty 单机安装,那么这台宿主机上的 80 端口已经被 Pigsty 部署的 Nginx Web Portal 占据了。

所以,Pigsty 提供的模板中,DIFY_PORT 默认使用了 8001,并通过宿主机上 Pigsty 部署的 Nginx 转发至此端口。当然我们也提供选项B,你也可以直接在 /etc/nginx/conf.d/dify.conf 里使用样例配置,直接指向 Dify 的 webapi 端口。

pigsty.yml 配置文件中的 infra_portal 参数中新增一行 Dify 的配置

infra_portal:                     # domain names and upstream servers
  home         : { domain: h.pigsty }
  grafana      : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
  prometheus   : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
  alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
  blackbox     : { endpoint: "${admin_ip}:9115" }
  loki         : { endpoint: "${admin_ip}:3100" }
  
  dify         : { domain: dify.pigsty ,endpoint: "10.10.10.10:8001", websocket: true }

执行以下剧本,重新生成 Nginx 配置、证书并应用:

./infra.yml -t nginx

当然如果要通过域名访问,你要把自己的域名 dify.pigsty 添加到域名服务器,或者简单地写入:/etc/hostsC:\Windows\System32\drivers\etc\hosts 之类的静态域名解析文件。

然后,你就可以从浏览器中,通过 http://dify.pigsty 访问 Dify IDE 了。

让PG停摆一周的大会:PGCon.Dev 2024 参会记

PGCon.Dev 的前身是 PGCon —— 最知名的 PostgreSQL Hacker 年度聚会,也可以说是决定 PostgreSQL 未来的一场会。从 2007 年成立以来,一直都是在加拿大渥太华举办至今。 这次会议有些特殊,原来的主办者 Dan 交班给下一届大会组织者,举办地点也转移到了温哥华市的 SFU 港区活动中心,算是新班组开门红第一次大会,自然更为隆重。


全都来参会了,谁还在写代码?

有多隆重呢?PG 核心组的 Peter Eisentraut 在会后做了一个统计,在这次 PGCon.Dev 期间 PostgreSQL 一次代码提交都没有发生,出现了二十年来持续时间最长的停摆 —— 整整六天半!为啥,因为开发者全都来参会啦!

intro.png

考虑到前几次中断都发生在二十年前的项目早期……

虽然我已经拥抱 PostgreSQL 十年了,但线下现场参加全球 PG Hacker 们的会议还是第一次,所以我非常感谢组织团队为组织这次活动所做的工作。

PGCon.Dev 2024 已经于5月31日晚正式结束,理论上本文章本应在大会闭幕时写就,不过在紧接着探索温哥华与班夫国家公园的旅途中,我确实在高密度的旅途中把这件事不厚道地搁置了 那么今天就补上参会的见闻与记录吧。


第零天:扩展生态峰会

大会的第零天是领导层会议,我注册了下午的 Extension Ecosystem Summit 扩展生态峰会。

说起来,这个扩展生态峰会也许跟我还有点关系。两个月前我写了一篇文章《PostgreSQL正在吞噬数据库世界》,主题是 PostgreSQL 的繁荣扩展生态是其独一无二的特点与成功的关键要素。 写完后将其翻译成了英文《Postgres is eating the database world》发到了 Pigsty 博客MediumHackNews 上,总共有几十万的阅览量,基本应该覆盖了整个 PG 社区。

ecosystem.jpg

此前,扩展机制的重要性并没有达成共识,即使在 PG 社区与一些资深成员的眼中,关于扩展他们只是觉得 PostGIS 和 PGVector 好像很不错 —— 前者是地理空间数据库的事实标准,后者是AI领域当红炸子鸡 —— 向量数据库的砸盘掀桌者。 但 PG 生态中强大的扩展绝不仅仅只有这两个,在抛出了这个极为繁荣的 PG 扩展生态 Landscape 后,立即引起了社区成员的极大兴趣与关注,很快关于PG扩展的讨论发酵了起来。

在这次扩展峰会之前,PG 社区已经举办了六次迷你扩展峰会对此事进行了密集的讨论,六位主讲嘉宾兼主持人在最近两个月中,从不同的角度介绍了关于扩展生态的建设工作,并阐述了对 PG 扩展生态发展的愿景,会议的视频回放可以在 Youtube 上看到。

在这次大会中,有许多与扩展生态,可扩展性有关的议题,甚至还有一个专门的扩展峰会,也许确实是有点关系的。这场扩展峰会分了上下两场,每场都有几个 Topic,大家挑选感兴趣的主题参与。 我挑了 David Wheeler 的 Binary Packing 主题分会参与讨论,另外四个参与者是 PGDG Yum 仓库维护者 Devrim,Debian 仓库维护者大法师 Tomasz Rybak,以及 Neon 的 PG 主要贡献者 Andreas Scherbaum。都是些老前辈,好在我也算是 YUM/APT 仓库的建设者/维护者,能实质参与到讨论中。

extension-summit.jpg

在上半场,来自 Temob 的 David 一直想做一个 PGXN v2,作为 PG 生态扩展分发的标准,搞一些 OCI 构建扩展的花活。当然,现有事实标准的维护者 Devrim 和 Tomasz 肯定是不乐意的。我支持这两位老爷子,毕竟我做的是 PG 发行版,内核组的活儿跟我直接关系不大,但 YUM/APT 仓库的负责人跟我的关系最紧密,RPM / Deb 包分发扩展已经是一种相当成熟可靠的方式了,整 OCI 这些我个人觉得意义不大。

下半场,我参加了 Omnigres 创始人 Yurii Rashkovskii 主持的 Extension in Core 分会场,讨论了关于扩展目录结构,元数据,命名冲突,版本控制,二进制分发的一些想法。并且和负责 PG RPM 仓库的 Devrim 老爷子聊了很多关于扩展的问题。

在扩展峰会后,Devrim 打出 “Keith粉丝团” 的 Slogan


第一天:主题分享与酒吧社交

PGCon.Dev 最核心的部分当然是大会议题,在 PG大会2024开幕 中我已经选定了感兴趣的主题,绝大多数分享都没有让我失望 —— 比起国内各种 XX 大会无聊的产品宣介,无关痛痒的技术细节与架构分享,PGConf.Dev 的分享要有趣且扎实得太多了。

5月28号 / 周二举行了 PostgreSQL 开发者与领导层闭门会议,以及 PGCon.Dev 扩展生态峰会。大会正式的议程在周三,也就是 29 号开始。

开场由 Jonathan KatzMelanie Plageman 主持,前者是 PG 核心组七人之一,AWS RDS 的首席产品经理;后者是新近成为 PG 提交者,来自微软的罕有的女性PG开发者。当然,开幕式上最精彩的一幕,就是发现了著名 xz 后门的 “英雄开发者” Andres Freund 被拉上了台,披上了超级英雄的披风。

andres-hero.jpeg

开幕式之后就开始了常规 Session Track,目前还没有会议的视频放出,但我相信以加拿大的办事效率,“用不了多久” 就能在 Youtube 上看到了。大部分的 Session 都是三选一的。我选了几个场次,下面是摘要:


将扩展的边界推向新边疆

第一场来自 Yurii , “将 PG 扩展的边界推向新边疆”。讨论的内容其实是 PostgreSQL 应该提供什么样的扩展 API ? PostgreSQL 有着极佳的可扩展性,但这一套 API 已经是十几年前 9.x 留下的了。尤里的提议旨在解决现有扩展机制的一些问题。比如:如何同时安装两个不同版本的扩展插件?如何避免一部分扩展插件安装后需要重启数据库的问题?如何让PG像管理数据一样管理扩展?如何处理扩展的依赖关系?

Yurii 和 Viggy 创办了 Omnigres ,旨在让 PostgreSQL 直接成为一个应用开发平台(比如直接在数据库里跑 HTTP 服务器等任务)。为了做到这一点,他们为 PostgreSQL 设计的一套新的扩展 API 与管理机制。我认为一些改进很有创新性,是 PG 内核扩展机制的前沿探索实践。PDF地址

yurii-extension.png

我和 Viggy 与 Yurii 聊的非常愉快,Yurii 手把手带我编译安装试用了一把 Omni。而我也准备在下一个 Pigsty 的版本中,就加入对 Omni 系列扩展的支持,让这个强大的应用开发框架开箱即用。


数据库中的无政府状态

第二场分享来自学术界(CMU),师从网红教授 Andy Pavlo 的阿比盖尔·金,主题为:数据库中的无政府状态 —— 数据库管理系统可扩展性的调查与评估。我对这个主题非常感兴趣,因为 Pigsty 将 PG可扩展性为首要价值主张,收录了 255 个扩展插件,而 Kim 的这个研究带来了一些有趣的发现。

例如,PostgreSQL 是可扩展性最强的 DBMS,在全部十项扩展点上支持其中九种(紧随其后的是 DuckDB,和PG同为我最看好的两款DBMS)。根据 Kim 的统计,PG 生态有 375+ 可用扩展,远远甩开其他数据库一个数量级。

kim-extensibility.png

更有价值的是,Kim 定量分析了这些扩展之间的兼容性水平,得到了一个兼容性矩阵,并有一些有趣的发现 —— 例如,最为强大的 TimescaleDB 和 Citus 最容易与其他扩展发生冲突。而这样的信息对于用户与发行版维护者来说是非常有价值的。

我跟 Kim 聊天时打趣说,你的这个研究大大的好 —— 可以有理有据地用数据说话,宣称 PostgreSQL 可扩展性天下无敌了。PDF在线地址


PostgreSQL是如何被误用与滥用的

下午的第一场,我听了来自 CrunchyData 的 Karen Jex 的分享,这是少有的来自用户(一位DBA,还是女性DBA,确实非常罕见),而不是开发者的分享。Karen 分享了一堆 PG 初学者会犯的可笑错误。Karen 的分享并没有什么我不知道的新知识,但确实让我确信了 —— 世界哪儿的初学者都一样,都会犯这些可笑的错误。但这样的视角对于 PG Hacker 来说确实是很新鲜的,很多大佬都听得津津有味。

PostgreSQL与人工智能生态

下午的第二场,Bruce Momjian 分享了这个主题。Bruce 是 PGDG 发起人,从一开始到现在一直都是 PG 核心委员,也是中国各种 PG 会议的老熟人与常客了。

我本来以为分享的内容会是介绍一下PG生态的向量数据库扩展,或者类似 PGML,PG4ML 这样的机器学习扩展插件,结果竟然是如何利用 PostgreSQL 的多维数组与查询,实现神经网络的推理与训练。这样的把戏很好玩儿,但我很早也折腾过,没啥实用价值。

和 Bruce 吃饭聊天的时候也提到这个话题,Bruce 解释说,Jonathan Katz 为了介绍PG生态的向量数据库扩展 PGVector,需要一个话题来作为综述引子,于是就把 Bruce 拉壮丁过来灌水了,哈哈… PDF地址

bruce-ai.png

让我看看,Bruce在偷偷写什么代码?竟然是 ArgParser

会后和 Bruce 聊了很多有趣的的话题,往年国内 PG 技术大会都喜欢请 Bruce 过来,或者远程做一个开幕分享。Bruce 说他很非常想再来中国,不过现在国际形势冲突加剧,美国大使馆发布的中国旅游风险等级太高,他也不敢过来了,让人遗憾。

构建PB级别的PostgreSQL部署

下午的第三场,我听了 Chris Travers 的分享:他们原来用 ElasticSearch 存数据,保留30天,数据量1PB,体验极差,基本上处于不可用状态,也难以维护。于是他们改用了一个水平分片的 PostgreSQL 集群完美地解决了问题 —— 总共存储了 10 PB 的数据。

通常来说出于各种因素,单机 PostgreSQL 的舒适区上界在几十TB ~ 几百TB 的数量级,PB 量级的部署我只听说过一例。即使是水平分片集群,10PB 量级也是极其罕见的了。尽管依然是中规中矩的分表/分布式实践,但数据量级确实让人印象深刻。PDF地址


临时加场:当数据库遇上新硬件

毫不夸张地说,这是本场最佳演讲,没有之一,也是我听过所有现场演讲中最富有激情与感染力的。演讲人 Margo Seltzer 是 UBC 的教授,以前是哈佛的教授,美国国家工程院院士,是数据库石破天祖师爷的亲传弟子,BerkeleyDB 的作者,她的老公也很有名,是 BSD / WiredTiger / nvi 的作者 Keith Bostic。

Margo 的演讲极具激情与感染力,并一针见血地指出了数据库领域面临的几个核心问题。例如,数据库的瓶颈已经不再是磁盘的IO性能,而是主内存的速度瓶颈。而硬件领域的趋势 —— HBMCXL 也许是解决这些问题的答案,但具体怎么做,那就是在座的各位 PG Hacker 需要面对的挑战了。

margo.png

在各种国内会议听多了院士,教授念经一样的分享报告,Margo 院士的演讲风格给我带来了耳目一新的感觉,并极大地感染鼓舞了我。大会视频放出后,我强烈建议各位可以听一听她的演讲。


酒吧社交活动

Margo 的分享结束后便是大会的官方 Social Event,在会场一街之隔的 Waterfront 车站里的 Rogue Kitchen & Wetbar, 位置极好 —— 窗外就是温哥华地标,太平洋海景。

大家可以随意交流,结识新老朋友。我和许多人都聊了很多有趣的话题,比如 Devrim,Tomasz,Yurii,Keith 等等等等。同样作为发行版/RPM维护者,我和 Devrim 老爷子尤其详谈甚欢,许多积存已久的问题也得到了想要的答案。

social-bar.png

很多人都是老朋友了,难得相见一面,都聚在一起聊天。两杯啤酒下肚,许多人都打开心扉。再加上在场的都是 PG 同好,陌生人只要对个眼神也可以很轻松地聊起来。

于是在三四个小时的觥筹交错中,我和在场的 PG Hacker 基本都混了个熟脸。餐后 Melanie 喊我们去玩桌游,但我的英语做个专业演讲还行,但还没好到和 Native Speaker 玩猜词游戏和狼人杀的程度,甚是遗憾。


第二天的主题与活动

PostgreSQL 线程模型

第一天晚上的社交活动预热完,第二天大家就热情熟络得多了。今天的分享主题比较值得一提的是 “多线程 PostgreSQL”,成功做到了座无虚席。大家都很关注 Heikki 发起的这场讨论。Heikki 介绍了PG进程模型与线程模型的利与弊,详细实现路径与当前进展。

线程模型的收益有不少:更便宜的连接(等于内置连接池),共享的关系缓存,计划缓存,动态调整共享内存区域的能力,修改配置无需重启,Vacuum可以更加激进,运行中的 Explain Analyze,方便地限制每条连接的内存使用。但以 Tom Lane 为首的反对的声音也不小:这样可能会引入大量 Bug,丧失多进程模型隔离性的优势,以及 —— 引入大量的不兼容性,许多扩展都需要针对新的模型重写修改。

Heikki 提出了目标与相当详细周密的计划供在座的 Hacker 们评审 —— 在五到七年内,完成到线程模型的转换,最终目标是没有中间状态。有趣的是,Heikki现场在 PPT 里引用了最大反对者 Tom Lane 的一段评论:“从历史上看,我认为这会是一场大灾难,导致大量代码悄无声息的崩坏,让事情脱离我们的控制”。

heikki-multithread.png

虽然被当场揶揄,但这一次 Tom Lane 现场听着也是慈祥微笑,并没有直接表示反对。而最大的反对声音不是来自 Tom Lane,而是扩展维护者,一位维护了好几个扩展插件的老爷子问到扩展兼容性怎么办?(主要是分配/使用内存的方式)Heikki 表示只能要求这些扩展作者在五年左右的过渡阶段中国呢里重写修改适配新的模型。气得这位老爷子直接愤而离场出去了。

鉴于线程模型对现有扩展生态的巨大冲击,我对这件事并不看好。我也与 Heikki 和 Tom lane 以及其他 Hacker 聊了一下关于线程模型的观点,总的来说,社区持谨慎观望态度。目前的进展也仅仅是在 PG 17 中重构了与 fork exec 有关的调用代码,并标记出使用的全局变量以便后续修改。即使真得发生,那也至少是 PG 2x 的事了。


走廊社交与大厅闲聊

第二天议题场次比第一天稍微水了一点点,所以更多的人参加的是 “Hallway Track”,就是在走廊大厅里和别人聊天。作为一个 i 人,我其实蛮不擅长这种场合的,但现场热烈的氛围很快就感染了我。再加上昨天晚上的酒吧社交环节大家也混了个脸熟,所以也算轻车熟路了。

在这样的场合中,想要开启一场和陌生人的对话,其实非常简单。你也不需要主动搭讪或者咋样,就只要眼神接触一下,对话就自然而然地触发了。给我的感觉和打 RPG 游戏一样,按下空格触发 NPC 对话。然后自我介绍一下,说说自己干嘛的,这不是就顺便把 Pigsty 广告到 PG 社区的每一个角落啦?

hallway-track.jpg

作为第一次现场参加 PGCon.Dev 的人,我很惊讶地发现自己有着于与新人不匹配的知名度与关注度。有近半的参会者看到我的胸牌 Vonng / Pigsty 就认识我了 —— 主要还是归功于我之前写的那篇 PG 大爽文《PostgreSQL is eating the Database world》,Jonathan 跟我吐槽到说最近这篇文章天天出现在他的时间线上,整个 PG 社区的人基本上都看过了。

collect.png

拍照基集:Tom, Bruce, Jonathan, Andres, Robert Hass, Devrim, Scherbaum, Heikki, Keith,…

当然说起社交,最简单粗暴的诀窍就是礼多人不怪 —— 我准备了一盒胸针,PostgreSQL 的吉祥物 Logo Slonik,镀金的,还带个小亚克力盒子。我给每一个和我聊天的 PG Hacker 都送了一个,这个胸针成为了本次大会备受欢迎的抢手货。好多人都在胸前或者会牌上别上了,而没拿到的人就在问:咦你们这个在哪里拿的,是贡献者奖励吗?

Bruce 对这个胸针爱不释手,说:“哎呀这个精巧的徽章真是太可爱了,一看就不是那种便宜货”(但其实镀金其实不贵的),然后我就又送了他一个。总之,礼多人不怪,靠着 Pigsty,PG爽文,和 Slonik 小胸针,也算是在 PG 大会上吃开了。


小聚:多国社区会餐

中午,瀚高做东,把美国PG社区,欧洲PG社区,还有日本PG社区的几位头面参会者拉到一起聚餐,一家温哥华的广东菜馆。图中从左前开始逆时针顺序分别是,瀚高北美研究院的 Grant Zhou,瀚高创始人苗健,欧洲PG用户组/ Neon 的 Andreas Scherbaum,有PG核心组/ 美国EDB 的 Bruce Momjiam,荣誉退休的前核心组成员 / pgEdge 的 Jan Wieck,制作各种PG贡献者硬币,社区周边的 Mark Wong,以及日本社区的 Tatsuro Yamada (山田達郎)与 Kyotaro Horiguchi (堀口恭太郎),最后是我。

lunch.jpeg

在饭桌上我们聊了各种各样的话题,坐在我边上的两位日本 PG 社区友人很有趣,堀口桑是一位 PG 核心贡献者,在 WAL 复制 / 多字节字符串处理上有很多贡献,还是 pg_hint_plan 的作者。另一位山田桑也是 PG 贡献者,对 Pigsty 很感兴趣,在本次大会上进行了题为 《索引建议不受待见,但很管用》的分享。

Mark Wong 也是 PG 社区的主要贡献者,PGUS 的组织者,开发了一系列 PG 监控扩展,但更有趣的是他还负责 PG 社区的周边,贡献者硬币,衣服,贴纸,还有这个特别可爱的毛线团小象也是他自己手工缝制的,让人爱不释手。据说他上次做的公仔在 PG Conf US 被人顺走了哈哈,所以这次看得可牢了。

elephant.png

Bruce 是 PG 中文社区的老朋友了,上面介绍过了;来自德国的 Andreas Scherbaum 是欧洲 PG 大会的组织者,我们一起参加了扩展峰会的 Binary Distribution 讨论,也邀请我们到时候去参加;瀚高是唯一一个出现在 PGCon.Dev 的中国数据库厂商,苗总从山东飞过来参加,也跟我们分享了一些国产数据库的故事与密辛。

在回会场的路上,我和 Jan Wieck 聊了很多,他是老一代光荣退休的 PG 核心组成员,也是 PL/pgSQL,PL/TCL,外键,视图,规则系统,TOAST,BGWriter,统计进程这些耳熟能详的PG核心功能的作者,他也是 Slony 的作者。路上我说我也想当一个 Major Contributor,他便与我分享了他参与 PG 贡献的故事:银行职员,工作需要用到数据库,就慢慢开始一步一步变成核心贡献者了 —— 他勉励我更多地参与到 PostgreSQL 贡献来,PG社区的未来就看你们年轻人的了。


让PG社区参与更有包容性

第二天下午与昨天一样,有一个无需三选一的特殊场次,主题是社区建设,由 Robert Hass 主持。3 位新晋的 PG 提交者轮流分享他们成为提交者的历程,与遇到的挑战和问题,分别是:泽田正彦,阿米特·兰格特,梅兰妮·普拉格曼。总体上社区参与面临的几个主要挑战是:非英语母语者的参与问题,时区差异,带有情绪的电子邮件沟通。

Robert Hass 在会后的博客中提到:他真的很想看到更多来自印度与日本的人参与到 PG 的高级职位中来,因为这两个国家有着庞大的开发者社区,却没有核心组成员,高级职位代表性不足

robert-hass.png

说老实话,听着有些五味杂陈,因为在包容性的议题中没有提到中国,而强调的是日本与印度。但这也是确实是没有办法的事情,中国在国际社区参与上,确实做的很拉垮。中国有三四百款国产数据库,其中很多都是基于 PG 魔改换皮套壳的,但这么多公司与用户,总共也只出了一个 PG 贡献者( ——拓数派的 Richard Guo,原来在 Pivotal,今年刚晋为 Committer)。

这次 PGCon.Dev ,中国过来参会的人,除了瀚高的四位就是我了,加起来正好一只手数过来。说来遗憾,中国技术界对 PostgreSQL 的认知水平与采纳程度仍然远远落后于全球,在生态上可能有10到15年的差距。要说语言挑战与障碍的问题,印度日本的英语口音也有不少沟通障碍,要说种族歧视啥的更是无稽之谈 —— 在场的华人面孔可绝对不算少。

那么到底是为什么呢?如果你选择关门自嗨,土法炼钢造手搓数据库,白嫖社区,不参与到全球社区中来,那么别人自然也不会待见你。我希望我的参与能够 “Bootstrap” 并改善这一情况,让更多中国的 PG 用户、开发者,产品、开源项目,被全球社区所熟知,接纳,承认,让中文世界的用户也有更多的社区参与。


闪电演讲

第二天下午的最后一个议程叫做: 闪电演讲。顾名思义,就是一个人只给5分钟,超时就立刻轰下来。大家都很干练,11 个主题整个才花了 45 分钟。在酒吧相谈甚欢的 Keith 分享了一些关于 PG Monitor 的改进,Peter Eisentraut 分享了关于 SQL 标准的跟进。当然,要说我最喜欢的分享,当属 Devrim Gündüz 关于 PG RPMs 的闪电演讲。昨天酒吧喝酒的时候,他神秘兮兮地说明天要放个大招,震撼全场,果不其然,在5分钟里讲完了 75 页的 PPT,气氛非常欢快~。

说起 PostgreSQL,尽管这是个开源软件,但也许 99% 的用户都是直接使用 “官方” 编译好的成品二进制软件而不是从源码编译。Pigsty 作为一个数据库发行版,我自己维护了34个 RPM 扩展插件,但还有一百多个扩展,和各种生态工具都是直接来自 Devrim 老爷子维护的 PGDG 官方仓库的,我深深知道这项工作的不易。 Devrim 在用自己的信用,为这个世界上最先进,最流行的数据库软件质量把好最后一道关口。

devrim.png

Devrim 要是想干坏事,那破坏力说不定比 xz 要大多了

Devrim 老爷子是一个很有意思的人,土耳其人,现居伦敦,还兼职酒吧 DJ 打碟,身上有一个 PostgreSQL Logo 的纹身,是 PGDG RPM 仓库的维护者。我跟他聊了一个多小时,了解了 PGDG 仓库的方方面面,讨论了许多问题。比如,一个扩展想弄进 PGDG 仓库里,一般需要什么条件,是什么流程。Devrim 说他会去关注 PGXN ,以及社区讨论,像最近最近大火的 pgvector 向量数据库扩展就是有人推荐给他,然后就收录进去了。我就白了一眼说:你看看推给你的那个人莫不是我…,哈哈哈哈。

说起来很有趣,在最近发布 Pigsty v2.7 中,我发现我维护的34个扩展里有4个 pgsql-http, pgsql-gzip, pg_net, pg_bigm 被纳入了进入了 PGDG 官方仓库。我一和 Devrim 提起这个事,他就笑眯眯地跟说我:我跑到你的 Pigsty 网站扩展列表上扒拉了一圈,发现有几个不错的,就弄进官方仓库了。我就问,我打包的那些不错的 RUST 扩展有没有机会弄进官方仓库里?他马上义正严辞地表态 —— 这些 Go 和 Rust 异端插件想也别想!但反正你不是自己弄了个 YUM / APT 软件仓库专门放这些扩展吗?

我和 Devrim 聊得非常尽兴。最后我答应当一个 PG扩展猎手,发掘新的 PG 插件。如果觉得不错就交给他,收纳进 PG 的官方仓库里。


第三天:Unconference

如果说 PGCon.Dev 最精髓特色的节目是什么,那一定是 Unconference (自组织会议)。Unconference 没有预设的议程,而是由参与者在现场决定讨论的主题。

大会第三天全天的议程都是 Unconference,Joseph Conway 主持了 Unconference Organization 议程,愿意讲的人上去提交自己想介绍的主题,然后大家投票。当然我也上去提交了一个 Built-in Prometheus Metrics Exporter 的主题。

提交完后,每个议题主讲人上台简介自己的 Topic,并尽可能合并同类项,我的话题不出意料地被合入 Jeremy 发起的 Observability 主题里了。接下来就是大家投票选出感兴趣的演讲。排名前三的主题是: 多线程(42票),可观测性(35票),增强社区参与(35票)。 最后选出的主题如下:

unconference.png

unconference2.png

看得出来,大家都非常重视可观测性上的特性。在 PostgreSQL 可观测性上,我确实是当仁不让的专家,pg_exporter 就是我写的。所以我抛出来的议题是:为 PostgreSQL 添加一个第一方的监控扩展,内置 Prometheus 监控端点,直接通过 HTTP 对外暴露监控指标。

提出这个问题的原因是,pg_exporter 虽好,但毕竟是外部组件,会引入额外的管理复杂度;而且如果 PostgreSQL 处于崩溃恢复无法接受新连接的状态,外部组件也难以知道内部的状态,只有将这个功能做成内核扩展,才能真正完整地提取这些信息。

实现的方案是采用类似 bgw_replstatus 扩展使用的后台工作进程,监听一个额外端口,通过 HTTP 对外暴露监控指标。内容上基本上也以我编写的 pg_exporter 作为蓝本,除了少量系统关键指标外,所有指标都通过一张 Collector 配置表进行定义。

这个想法得到了一些在座 PG Hacker 的关注。EDB ,CloudNativePG 的一些开发者也开始评估 pg_exporter 能否直接用在他们的发行版中,作为监控解决方案的一部分。现场所有对可观测性感兴趣的成员成立了一个 Observability SIG,并通过邮件列表进行后续的讨论。


议题:关于对龙芯提供支持

在大会最后两天中,我还与几位 PG Hacker 讨论了一些关于国产芯片,国产操作系统,中文字符集相关的中国特色数据库议题。

在之前发出的问题征集中,PG分会的类总提出了一个很好的建议,能不能让 PGDG 全球仓库支持龙芯 LoongArch 架构?国产芯片和国产操作系统厂商很乐意赞助这样的构建环境。带着这个问题,我询问了 RPM 仓库维护者 Devrim 老爷子;以及 Debian 侧的 Tomasz Rybak (备注:PGDG APT 仓库的维护者是 Christoph Berg,但 Tomasz 维护了 Debian 仓库中许多 PostgreSQL 相关的软件包),看看有没有可行性。

不过可惜的是,目前龙芯架构对于PG社区构建二进制使用的 OS 还没有提供支持 —— 例如 EL 系的 CentOS 7,Rocky 8,Rocky 9 ,以及 Debian 10/11/12 都无法在龙芯上运行。所以 Devrim 老爷子的回答是 No:而且构建的 Pipeline 必须在他们自己的机器和环境上可以跑起来,所以赞助云服务器的方式可能是走不通的。Tomasz 对于这个问题持开放态度,因为据说后面龙芯可能会支持 Debian13 ,那么就可以考虑把一些 PG 包的支持加进来。

总的来说,让 PG 官方 RPMs 支持龙芯架构估计没戏,但 APT 还是有可能的。但要龙芯支持主流开源社区 Linux OS Distro,这个事才有可能;如果是龙芯 + 一堆国产操作系统,那想都不要想,100% 没戏。


议题:关于服务端中文字符集支持

Jeremy Schneider 在本次大会带来一场关于字符排序规则( Collation) 的分享,我非常关注。这个分享抛出了一系列 Collate 规则变化导致的问题。说实话,我以前也专门写过一篇文章研究过这个问题,最终结论与 Jeremy 高度一致,应该用 C.UTF8 ,我一直是这么做的,并制定开发规约,也在发行版中强制默认配置推行这一点,而 Jeremy 的分享,则详细阐述了不这么做会导致哪些坑爹的结果

会后在大厅里,我和 Jeremy 进一步地讨论了这个问题,核心组的 Peter Eisentraut 也参与了进来。Jeremy 问我中国用户是怎样使用字符集与 Collation 的,我说新应用大体上都用的是 C.UTF8,通常只有一些政企单位和传统行业的老系统才会去折腾服务端中文字符集。

但这里确实有一个略尴尬的问题,例如 2023 年底中国发布的国标 GB18030 对信息系统提出了两条强制性要求:产品可以正确输入、输出、处理 GB18030 强制部分规定的全部汉字字符;产品可以正确识别 GB18030 强制性部分规定的全部汉字字符对应的编码。

PostgreSQL 可以在客户端支持 GB 18030 编码,也提供了 convert_to 将字符串编码为 GB18030 编码字节串的编码方案支持,但是不支持直接在服务端使用此编码(支持的是 EUC_CN),也没有通过 ICU 提供对 GB 18030 的支持。此外,我还向 Peter 提出了 convert_to + gb18030 大概有 20个新增汉字映射有误的问题。Jeremy 和 Peter 都表示会进一步跟进研究,看看怎么解决这些问题。


闭幕式

自组织会议聊完后,大会也进入到了最后的尾声。 Jonathan Katz 与 Melanie Plageman 主持了闭幕式。确实是一场非凡的大会,让人意犹未尽。

closing.jpeg

明年的 PGCon.Dev 2025 也会在加拿大举办,可能在温哥华,多伦多,渥太华或蒙特利尔四者之一。今年摸清楚了大会的调性与流程,我想明年可以去上台分享一个关于 Pigsty 或者 PG 可观测性的话题了。

顺带一提,参会的效果确实很明显,参会后,Pigsty 的海外下载 CDN 流量(这还只是一家云上的一部分)出现显著增长,打掉了我接近大几百G 的流量。更多的国际友人了解到了国内的 PostgreSQL 数据库发行版 Pigsty,哈哈。

pigsty-traffic.png

参会后来自海外的 CDN 流量有了一波暴增


大会的 PPT 一部分已经开放下载。当然,对 PostgreSQL 与 Pigsty 感兴趣的朋友也可以微信搜索 pigsty-cc 加群直接下载或参与讨论。以及,下面是一些 PGCon.Dev 相关的博客与文章:

PostgreSQL 17 beta1 发布!

PostgreSQL 全球开发组宣布,PostgreSQL 17 的首个 Beta 版本现已开放下载。 这一版本包含了 PostgreSQL 17 正式发布时所有功能的预览,但在 Beta 测试期间,某些细节可能会有所调整。

您可以在发布说明中找到关于 PostgreSQL 17 的所有功能和变更的信息:

https://www.postgresql.org/docs/17/release-17.html

秉承 PostgreSQL 开源社区的精神,我们强烈支持您在您的系统上测试 PostgreSQL 17 的新功能,帮助我们发现和修复潜在的错误或其他问题。 虽然我们不建议在生产环境中运行 PostgreSQL 17 Beta 1,但我们希望您能在测试环境中运行此 Beta 版本,并尽可能模拟您的实际工作负载。

社区将持续确保 PostgreSQL 17 作为世界上最先进的开源关系型数据库的稳定性和可靠性,但这离不开您的测试与反馈。 详情请参阅我们的 Beta 测试流程,以及您可以如何作出贡献:https://www.postgresql.org/developer/beta/


PostgreSQL 17 亮点功能

查询和写入性能改善

PostgreSQL 17 最近的版本与构建,持续致力于整体的系统性能优化。负责回收存储空间的 PostgreSQL Vacuum 进程使用了新的内部数据结构,使得垃圾回收过程的内存使用减少,最高可以减少 20 倍,同时减少了执行所需的时间。 此外 Vacuum 进程不再受到 1GB 内存的使用限制,而由 maintenance_work_mem 来控制,这意味着您可以为 Vacuum 进程分配更多资源。

这个版本引入了流式 I/O 接口,使得执行顺序扫描和运行 ANALYZE 的性能有所提高。 PostgreSQL 17 还新增了配置参数,可控制 事务、子事务和 multixact 缓冲区 的大小。

PostgreSQL 17 现在可以同时利用 Planner 的统计信息与 公共表表达式 CTE(即 WITH 查询)结果中的排序顺序,进一步优化这些查询的速度。 此外,这个版本显著提高了带有 IN 子句的查询,在使用 B-tree 索引 时的查询执行时间。 从这个版本开始,对于那些带有 NOT NULL 约束的列,如果查询中带有冗余的 IS NOT NULL 语句,PostgreSQL 会直接把它优化掉,同理,那些带有 IS NULL 的查询也会直接优化掉,PostgreSQL 17 还支持并行构建 BRIN 索引。

高并发写入类的工作负载,可以显著受益于 PostgreSQL 17 的预写日志(WAL)锁管理改进,测试显示,性能提升 最多高达两倍

最后,PostgreSQL 17 添加了更多显式的 SIMD 指令,比如为 bit_count 函数启用 AVX-512 指令支持。


分区和分布式工作负载增强

PostgreSQL 17 的分区管理更为灵活,新增了拆分合并分区的能力,并允许分区表使用 身份列(Identity Column)排它约束(Exclude Constraints)。 此外,PostgreSQL 外部数据包装器postgres_fdw)现在可以将 EXISTSIN 子查询下推到远端服务器,从而提升性能。

PostgreSQL 17 为逻辑复制添加了新功能,使其在高可用架构和大版本升级中更加易用。 从 PostgreSQL 17 使用 pg_upgrade 升级到更高版本时,不再需要删除 逻辑复制槽 了,从而避免了升级后需要重新同步数据的麻烦。 此外,你还可以控制逻辑复制的 Failover 过程,为高可用性架构中管理 PostgreSQL 提供了更好的可控制性。PostgreSQL 17 还允许逻辑复制的订阅者使用 hash 索引进行查找,并引入了 pg_createsubscriber 命令行工具,用于在使用物理复制的副本从库上创建逻辑复制。


开发者体验

PostgreSQL 17 继续深化了对 SQL/JSON 标准的支持,新增了 JSON_TABLE 功能,可以将 JSON 转换为标准的 PostgreSQL 表,以及 SQL/JSON 构造函数(JSONJSON_SCALARJSON_SERIALIZE)和查询函数(JSON_EXISTSJSON_QUERYJSON_VALUE)。 值得注意的是,这些功能最初计划在 PostgreSQL 15 中发布,但出于设计权衡考虑,在 Beta 期间被撤回 —— 这也是我们希望请您在 Beta 期间帮忙测试新功能的原因之一!此外,PostgreSQL 17 为 jsonpath 的实现增添了更多功能,包括将 JSON 类型的值转换为各种不同特定数据类型的能力。

MERGE 命令现在支持 RETURNING 子句了,让您可以在同一条命令中进一步处理修改过的行。 您还可以使用新的 merge_action 函数查看 MERGE 命令修改了哪一部分。 PostgreSQL 17 还允许使用 MERGE 命令更新视图,并新增了 WHEN NOT MATCHED BY SOURCE 子句,允许用户指定当源中的行没有任何匹配时,应该执行什么操作。

COPY 命令用于高效地从 PostgreSQL 批量加载与导出数据。在 PostgreSQL 17 中,导出大行时的性能最多有两倍的提升。 此外,当源编码与目标编码相匹配时,COPY 的性能也有所提升。COPY 新增了一个 ON_ERROR 选项,即使插入行时出现错误也可继续进行。 此外在 PostgreSQL 17 中,驱动程序可以利用 libpq API 使用 异步和更为安全的查询取消方法

PostgreSQL 17 引入了内置的排序规则提供程序,该提供程序提供与 C 排序规则类似的排序语义,但编码为 UTF-8 而非 SQL_ASCII。这种新的排序规则提供了不变性保证,确保您的排序结果在不同系统上都不会改变。


安全功能

PostgreSQL 17 新增了一个新的连接参数 sslnegotation,允许 PostgreSQL 在使用 ALPN 时直接进行 TLS 握手,减少一次网络往返。PostgreSQL 会在 ALPN 目录中注册为 postgresql

这个版本引入了新的 EventTrigger 事件 —— 当用户认证时触发。并且在 libpq 中提供了一个新的名为 PQchangePassword 的 API,可以在客户端侧自动对密码取哈希,以防止在服务器中意外记录下明文密码。

PostgreSQL 17 增加了一个新的 预定义角色,名为 pg_maintain,赋予用户执行 VACUUMANALYZECLUSTERREFRESH MATERIALIZED VIEWREINDEXLOCK TABLE 的权限, 并确保 search_path 对于 VACUUMANALYZECLUSTERREFRESH MATERIALIZED VIEWINDEX 等维护操作是安全的。 最后,用户现在可以使用 ALTER SYSTEM 来设置系统无法识别的未定义配置参数了。


备份与导出管理

PostgreSQL 17 可以使用 pg_basebackup 进行增量备份,并增加了一个新的实用工具 pg_combinebackup,用于备份恢复过程中将备份合并。 该版本为 pg_dump 新增了一个参数项 --filter,允许您指定一个文件来进一步指定在 dump 过程中要包含或排除哪些对象。


监控

EXPLAIN 命令可以提供有关查询计划和执行详情的信息,现在它新增了两个选项:SERIALIZE 会显示将数据序列化为网络传输形式时的耗时;MEMORY 会报告优化器内存使用情况。此外,EXPLAIN 现在还可以显示花费在 I/O 块读写上的时间。

PostgreSQL 17 标准化了 pg_stat_statementsCALL 的参数,减少了频繁调用的存储过程所产生的记录数量。 此外,VACUUM 进度报告 现在会显示索引垃圾回收的进度。 PostgreSQL 17 还引入了一个新视图,pg_wait_events,提供关于等待事件的描述,可以与 pg_stat_activity 共同使用,以便深入了解活动会话出现等待的原因。 此外,pg_stat_bgwriter 视图中的一些信息,现在被拆分到新的 pg_stat_checkpointer 视图中了。


其他功能

PostgreSQL 17 还有许多其他新功能与改进,很多改进都可能会对您的用例有所帮助。请参阅发布说明以获取完整的新功能和变更列表:

https://www.postgresql.org/docs/17/release-17.html


错误和兼容性测试

每个 PostgreSQL 版本的稳定性,在很大程度上依赖于诸位 PG社区用户,您可以用你们的工作负载和测试工具来测试即将发布的版本,以便在 PostgreSQL 17 正式发布前发现错误并完成回归。由于这是一个 Beta 版本,针对数据库行为、功能细节和 API 的小改动仍然可能会发生。您的反馈和测试将有助于调整并敲定这些新功能,因此请在近期进行测试。用户测试的质量有助于我们确定何时可以进行最终发布。

PostgreSQL wiki 中公开提供了开放问题列表。您可以使用 PostgreSQL 网站上的此表单报告错误

https://www.postgresql.org/account/submitbug/


Beta 时间表

这是 PostgreSQL 17 的第一个 Beta 版本。PostgreSQL 项目将根据测试需要发布更多的 Beta 版本,随后是一或多个 RC 版本,最终版本大约会在 2024 年 9 月或 10 月发布。详细信息请参阅 Beta 测试 页面。


链接

为什么PostgreSQL是未来数据库的事实标准?

如今,软件开发中最大的趋势之一,是 PostgreSQL 正在成为事实上的数据库标准。已经有一些博客阐述了如何做到 万物皆用 PostgreSQL,但还没有多少文章能解释这一现象背后的原因。(更重要的是,为什么这件事很重要) —— 所以我写下了这篇文章。

本文作者为 Ajay Kulkarni,TimescaleDB CEO ,原文发表于 TimescaleDB 博客:《Why PostgreSQL Is the Bedrock for the Future of Data》。

译者冯若航,PostgreSQL 专家,开源 RDS PG —— Pigsty 作者。

目录

  • 01 PostgreSQL 正成为事实上的数据库标准
  • 02 万物都开始计算机化
  • 03 PostgreSQL 王者归来
  • 04 解放双手,构建未来,拥抱 PostgreSQL

PostgreSQL 正成为事实上的数据库标准

在过去几个月里,“一切皆可用 PostgreSQL 解决” 已经成为开发者们的战斗口号:

PostgreSQL 并不是一个简单的关系型数据库,而是一个数据管理的抽象框架,具有吞噬整个数据库世界的力量。而这也是正在发生的事情 —— “一切皆用 Postgres” 已经不再是少数精英团队的前沿探索,而是成为了一种进入主流视野的最佳实践。

—— 《PostgreSQL正在吞噬数据库世界》,冯若航(me!)

在初创公司中简化技术栈、减少组件、加快开发速度、降低风险并提供更多功能特性的方法之一就是**“一切皆用 Postgres”**。Postgres 能够取代许多后端技术,包括 Kafka、RabbitMQ、ElasticSearch,Mongo和 Redis ,至少到数百万用户时都毫无问题。

——《技术极简主义:一切皆用Postgres》, Stephan Schmidt

听说 Postgres 被称为“数据库届的瑞士军刀”,嗯…… 是的,听起来很准确! 不确定是谁第一个提出来的,但这是一个非常恰当的观察! —— Gergely Orosz

PostgreSQL 天生自带护城河。它发展稳定,一直保持着对SQL标准的坚实支持,如今已成为数据库的热门选择。它有着极佳的文档质量(是我迄今见过的最好的之一)。与PostgreSQL集成非常容易,最近我看到的每一个数据工具初创公司通常都将 PostgreSQL 作为其第一个数据源连接选择。(我相信这也是因为PG功能丰富并有着强大的社区支持)—— Abhishek

学习 Postgres 无疑是我职业生涯中投资回报率最高的技术之一。如今,像 @neondatabase,@supabase,和 @TimescaleDB 这样的优秀公司都是基于 PostgreSQL 构建的。现在它对我非常重要,足以与 React 和 iOS 开发并驾齐驱 —— Harry Tormey

YouTube视频:等等…PostgreSQL能做什么?

“当我第一次听说 Postgres 时(那时候MySQL绝对是主导者),有人对我说这是“那些数学怪咖弄出来的数据库”,然后我意识到:没错,就是这些人,才适合做数据库。” —— Yuan Gao

“PG实现了惊人的复兴:现在 NoSQL 已经没落,Oracle 又拥有了MySQL,你还有什么选择呢?”

—— Manoj Khangaonkar

*“Postgres不仅仅是一个关系数据库,它是一种生活方式。” —— ilaksh

凭借其坚如磐石的基础,加上其原生功能与扩展插件带来的强大功能集,开发者现在可以单凭 PostgreSQL 解决所有问题,用简洁明了的方式,取代复杂且脆弱的数据架构。

来源:Just Use Postgres for Everything

这也许可以解释为什么去年 PostgreSQL 在专业开发者中,在最受欢迎的数据库排行榜上,从MySQL手中夺得了榜首位置(60,369 名受访者):

在过去一年中,你在哪些数据库环境中进行了大量开发工作,以及在接下来的一年中你想在哪些数据库环境中工作?超过49%的受访者选择了PostgreSQL。 —— 来源:StackOverflow 2023 年度用户调研

这些结果来自 2023 年的 Stack Overflow开发者调查。如果纵观过去几年,可以看到 PostgreSQL 的使用率在过去几年中有着稳步增长的趋势:

在 2020 ~ 2022 年间,根据 StackOverflow 的开发者调查显示,PostgreSQL 是第二受欢迎的数据库,其使用率持续上升。来源: 202020212022

这不仅仅是小型初创公司和业余爱好者里的趋势。实际上,在各种规模的组织中,PostgreSQL 的使用率都在增长。

PostgreSQL 使用率变化,按公司规模划分( TimescaleDB 2023 社区调研

在 Timescale,我们这一趋势对我们并不陌生。我们已经是 PostgreSQL 的信徒近十年了。这就是为什么我们的业务建立在 PostgreSQL 之上,以及为什么我们是 PostgreSQL 的顶级贡献者之一,为什么我们每年举办 PostgreSQL 社区调研(上述提到),以及为什么我们支持 PostgreSQL 的 Meetup 与大会。就个人而言,我已经使用 PostgreSQL 超过 13 年了(当时我从 MySQL 转换过来)。

已经有一些博客文章讨论了 如何 (How)将 PostgreSQL 用于一切问题,但还没有讨论 为什么 (Why)会这样发生(更重要的是,为什么这很重要)。

直到现在。

但要理解为什么会发生这种情况,我们必须先了解一个更为基础的趋势以及这个趋势是如何改变人类现实的基本性质的。


02 一切都变成了电脑

一切都变成了计算机 —— 我们的汽车、家庭、城市、农场、工厂、货币以及各种事物,包括我们自己,也正在变得更加数字化。我们每年都在更进一步地数字化自己的身份和行为:如何购物,如何娱乐,如何收藏艺术,如何寻找答案,如何交流和连接,以及如何表达自我。

二十二年前,这种 “无处不在的计算” 还是一个大胆的想法。那时,我是麻省理工学院人工智能实验室的研究生,还在搞着智能环境的论文。我的研究得到了麻省理工学院氧气计划的支持,该计划有一个崇高而大胆的目标:让计算像我们呼吸的空气一样无处不在。就那时候而言,我们自己的服务器架设在一个小隔间中。

但从那以后,很多事情都变了。计算现在无处不在:在我们的桌面上,在我们的口袋里,在我们的 “云” 中,以及在我们的各种物品中。我们预见到了这些变化,但没有预见到这些变化的二级效应:

无处不在的计算导致了无处不在的数据。随着每一种新的计算设备的出现,我们收集了更多关于我们现实世界的信息:人类数据、机器数据、商业数据、环境数据和合成数据。这些数据正在淹没我们的世界。

数据的洪流引发了数据库的寒武纪大爆炸。所有这些新的数据源需要新的存储地点。二十年前,可能只有五种可行的数据库选项。而如今,有数百种,大多数都是针对特定的数据而特别设计的,且每个月都在涌现新的数据库。

更多的数据和数据库导致了更多的软件复杂性。正确选择适合你软件工作负载的数据库已不再简单。相反,开发者被迫拼凑复杂的架构,这可能包括:关系数据库(因其可靠性)、非关系数据库(因其可伸缩性)、数据仓库(因其分析能力)、对象存储(因其便宜归档冷数据的能力)。这种架构甚至可能会有更为专业特化的组件,例如时序数据库或向量数据库。

更多的复杂性意味着留给构建软件的时间越短。架构越复杂,它就越脆弱,就需要更复杂的应用逻辑,并且会拖慢开发速度,留给开发的时间就越少。复杂性不是一项优点,而是一项真正的成本。

随着计算越来越普遍,我们的现实生活越来越与计算交织在一起。我们把计算带入了我们的世界,也把我们自己带入了计算的世界。我们不再仅仅有着线下的身份,而是一个线下与线上所作所为的混合体。

在这个新现实中,软件开发者是人类的先锋。正是我们构建了那些塑造这一新现实的软件。

但是,开发者现在被数据淹没,被淹没在数据库的复杂性中

这意味着开发者 —— 花费越来越多的时间,在管理内部架构上,而不是去塑造未来。

我们是如何走到这一步的?


第一部分:逐波递进的计算浪潮

无处不在的计算带来了无处不在数据,这一变化并非一夜之间发生,而是在几十年中逐波递进:

  • 主机/大型机 (1950 年代+)
  • 个人计算机 (1970 年代+)
  • 互联网 (1990 年代+)
  • 手机 (2000 年代+)
  • 云计算 (2000 年代+)
  • 物联网 (2010 年代+)

每一波技术浪潮都使计算机变得更小、更强大且更普及。每一波也在前一波的基础上进行建设:个人计算机是小型化的主机;互联网是连接计算机的网络;智能手机则是连接互联网的更小型计算机;云计算民主化了计算资源的获取;物联网则是将智能手机的组件重构为连接到云的其他物理设备。

但在过去二十年中,计算技术的进步不仅仅出现在物理世界中,也体现在数字世界中,反映了我们的混合现实:

  • 社交网络 (2000 年代+)
  • 区块链 (2010 年代+)
  • 生成式人工智能 (2020 年代+)

每一波新的计算浪潮,我们都能从中获取有关我们混合现实的新信息源:人类的数字残留数据、机器数据、商业数据和合成数据。未来的浪潮将创造更多数据。所有这些数据都推动了新的技术浪潮,其中最新的是生成式人工智能,进一步塑造了我们的现实。

计算浪潮不是孤立的,而是像多米诺骨牌一样相互影响。最初的数据涓流很快变成了数据洪流。接着,数据洪流又促使越来越多的数据库的创建。


第二部分:数据库持续增长

所有这些新的数据来源,都需要新的地方来存储 —— 即数据库。

大型机从 Integrated Data Store(1964 年)开始,以及后来的 System R(1974 年) —— 第一个 SQL 数据库。个人计算机推动了第一批商业数据库的崛起:受 System R 启发的 Oracle(1977 年);还有 DB2(1983 年);以及微软对 Oracle 的回应: SQL Server(1989 年)。

互联网的协作力量促进了开源软件的崛起,包括第一个开源数据库:MySQL(1995 年),PostgreSQL(1996 年)。智能手机推动了 SQLite(2000 年)的广泛传播。

互联网还产生了大量数据,这导致了第一批非关系型(NoSQL)数据库的出现:Hadoop(2006 年);Cassandra(2008 年);MongoDB(2009 年)。有人将这个时期称为 “大数据” 时代。


第三部分:数据库爆炸式增长

大约在 2010 年,我们开始达到一个临界点。在此之前,软件应用通常依赖单一数据库 —— 例如 Oracle、MySQL、PostgreSQL —— 选型是相对简单的。

但 “大数据” 越来越大:物联网带来了机器数据的大爆炸;得益于 iPhone 和 Android,智能手机使用开始呈指数级增长,排放出了更多的人类数字 “废气”;云计算让计算和存储资源的获取变得普及,并加剧了这些趋势。生成式人工智能最近使这个问题更加严重 —— 它拉动了向量数据。

随着被收集的数据量增长,我们看到了专用数据库的兴起:Neo4j 用于图形数据(2007 年),Redis 用于基础键值存储(2009 年),InfluxDB 用于时序数据(2013 年),ClickHouse 用于大规模分析(2016 年),Pinecone 用于向量数据(2019 年),等等。

二十年前,可行的数据库选项可能只有五种。如今,却有数百种,它们大多专为特定用例设计,每个月都有新的数据库出现。虽然早期数据库已经承诺 通用的全能性,这些专用的数据库提供了特定场景下的利弊权衡,而这些权衡是否有意义,取决于您的具体用例。


第四部分:数据库越多,问题越多

面对这种数据洪流,以及各种具有不同利弊权衡的专用数据库,开发者别无选择,只能拼凑复杂的架构。

这些架构通常包括一个关系数据库(为了可靠性)、一个非关系数据库(为了可扩展性)、一个数据仓库(用于数据分析)、一个对象存储(用于便宜的归档),甚至更专用的组件,如时间序列或向量数据库,用于那些特定的用例。

但是,越复杂的架构就越脆弱,就需要更复杂的应用逻辑,并且会拖慢开发速度,留给开发的时间就越少。

这意味着开发者 —— 花费越来越多的时间,在管理内部架构上,而不是去塑造未来。

有更好的办法解决这个问题。


PostgreSQL王者归来

故事在这里发生转折,我们的主角不再是一个崭新的数据库,而是一个老牌数据库,它的名字只有 核心开发人员才会喜欢:PostgreSQL。

起初,PostgreSQL 在 MySQL 之后居于第二位,且与其相距甚远。MySQL 使用起来更简单,背后有公司支持,而且名字朗朗上口。但后来 MySQL 被 Sun Microsystems 收购(2008年),随后又被 Oracle 收购(2009年)。于是在那时,软件开发者们开始重新考虑使用什么数据库 —— 他们原本视 MySQL 为摆脱昂贵的 Oracle 专制统治的自由软件救星。

与此同时,一个由几家小型独立公司赞助的分布式开发者社区,正在慢慢地让 PostgreSQL 变得越来越好。他们默默地添加了强大的功能,例如全文检索(2008年)、窗口函数(2009年)和 JSON 支持(2012年)。他们还通过流复制、热备份、原地升级(2010年)、逻辑复制(2017年)等功能,使数据库更加坚固可靠,同时勤奋地修复缺陷,并优化粗糙的边缘场景。


PostgreSQL 已经成为一个平台

在此期间,PostgreSQL 添加的最具影响力的功能之一,是支持 扩展(Extension):可以为 PostgreSQL 添加功能的软件模块(2011年)。扩展让更多开发者能够独立、迅速且几乎无需协调地为 PostgreSQL 添加功能

得益于扩展机制,PostgreSQL 开始变成不仅仅是一个出色的关系型数据库。得益于 PostGIS,它成为了一个出色的地理空间数据库;得益于 TimescaleDB,它成为了一个出色的时间序列数据库;+ hstore,键值存储数据库;+ AGE,图数据库;+ pgvector,向量数据库。PostgreSQL 成为了一个平台。

现在,开发者出于各种目的选用 PostgreSQL。例如为了可靠性、为了可伸缩性(替代NoSQL)、为了数据分析(替代数仓)。


大数据则何如?

此时,聪明的读者应该会问,“那么大数据呢?” —— 这是个好问题。从历史上看,“大数据”(例如,几百TB甚至上PB)—— 及相关的分析查询,曾经对于 PostgreSQL 这种本身不支持水平扩展的数据库来说,并不是合适的场景。

但这里的情况也在改变,去年十一月,我们推出了 “分层存储”,它可以自动将你的数据在磁盘和对象存储(S3)之间进行分级存储,实际上实现了 无限存储表 的能力。

所以从历史上看,虽然 “大数据” 曾经是 PostgreSQL 的短板,但很快将没有任何工作负载是太大而处理不了的。

PostgreSQL 是答案。PostgreSQL 是我们解放自我,并构建未来的方式。


解放自我,构建未来,拥抱 PostgreSQL

相比于在各种异构数据库系统中纠结(每一种都有自己的查询语言和怪癖!),我们可以依靠世界上功能最丰富,而且可能是最可靠的数据库:PostgreSQL。我们可以不再耗费大量时间在基础设施上,而将更多时间用于构建未来。

而且 PostgreSQL 还在不断进步中。PostgreSQL 社区在不断改进内核。而现在有更多的公司参与到 PostgreSQL 的开发中,包括那些巨无霸供应商。

pigsty-ecosystem.png

今天的 PostgreSQL 生态 —— 《PostgreSQL正在吞噬数据库世界

同样,也有更多创新的独立公司围绕着 PostgreSQL 内核开发,以改善其使用体验:Supabase(2020年)正在将 PostgreSQL 打造成一个适用于网页和移动开发者的 Firebase 替代品;Neon(2021年)和 Xata(2022年)都在实现将 PostgreSQL “伸缩至零”, 以适应间歇性 Serverless 工作负载;Tembo(2022年)为各种用例提供开箱即用的技术栈;Nile(2023年)正在使 PostgreSQL 更易于用于 SaaS 应用;还有许多其他公司。当然,还有我们,Timescale(2017年)。

此处省略三节关于 TimescaleDB 的介绍


尾声:尤达?

我们的现实世界,无论是物理的还是虚拟的,离线的还是在线的,都充满着数据。正如尤达所说,数据环绕着我们,约束着我们。这个现实越来越多地由软件所掌控,而这些软件正是由我们这些开发者编写的。

这一点值得赞叹。特别是不久之前,在2002年,当我还是MIT的研究生时,世界曾经对软件失去了信心。我们当时正在从互联网泡沫破裂中复苏。主流媒体 “IT并不重要”。那时对一个软件开发者来说,在金融行业找到一份好工作比在科技行业更容易——这也是我许多 MIT 同学所选择的道路,我自己也是如此。

但今天,特别是在这个生成式AI的世界里,我们是塑造未来的人。我们是未来的建设者。我们应该感到惊喜。

一切都在变成计算机。这在很大程度上是一件好事:我们的汽车更安全,我们的家居环境更舒适,我们的工厂和农场更高效。我们比以往任何时候都能即时获取更多的信息。我们彼此之间的联系更加紧密。有时,它让我们更健康,更幸福。

但并非总是如此。就像原力一样,算力也有光明和黑暗的一面。越来越多的证据表明,手机和社交媒体直接导致了青少年心理疾病的全球流行。我们仍在努力应对AI于合成生物学的影响。当我们拥抱更强大的力量时,应该意识到这也伴随着相应的责任。

我们掌管着用于构建未来的宝贵资源:我们的时间和精力。我们可以选择把这些资源花在管理基础设施上,或者全力拥抱 PostgreSQL,构建正确的未来。

我想你已经知道我们的立场了。

感谢阅读。#Postgres4Life

PostgreSQL会修改开源许可证吗?

作者:Jonathan Katz,PostgreSQL 核心组成员(1 of 7),AWS RDS 首席产品经理

译者:冯若航,PostgreSQL 专家,Free RDS PG Alternative —— Pigsty 作者


PostgreSQL会修改开源许可证吗

声明:我是PostgreSQL 核心组 的成员,但本文内容是我的个人观点,并非 PostgreSQL 官方声明 …… 除非我提供了指向官方声明的链接

今天得知 Redis 项目将不再使用开源许可证发布,我感到非常遗憾。原因有二:一是作为长期的 Redis 用户和较早的采用者,二是作为一个开源贡献者。对于开源商业化这件事的挑战,我不得不说确实感同身受 —— 特别是我曾站在针锋相对的不同阵营之中(译注:作者也是 AWS RDS 首席产品经理)。我也清楚这些变化对下游的冲击,它们可能对用户采纳、应用技术的方式产生颠覆性的影响。

每当开源许可证领域出现重大变动时,尤其是在数据库及相关系统中(例如 MySQL => Sun => Oracle 就是第一个映入我脑海的),我总会听到这样的问题:“PostgreSQL会修改其许可证吗?”

PostgreSQL 的网站上其实 有答案

PostgreSQL会使用不同的许可证发布吗?PostgreSQL 全球开发组(PGDG)依然致力于永远将 PostgreSQL 作为自由和开源软件提供。我们没有更改 PostgreSQL 许可证,或使用不同许可证发布 PostgreSQL 的计划。

声明:上面这段确实是我参与撰写的

PostgreSQL许可证(又名 “协议” — Dave Page 和我在这个词上来回辩论挺有意思的)是一个开源倡议组织(OSI)认可的许可证,采用非常宽松的许可模型。至于它与哪个许可证最为相似,我建议阅读 Tom Lane在2009年写的这封电子邮件 (大意是:更接近 MIT 协议,叫 BSD 也行)。

尽管这么说,但 PostgreSQL不会改变许可证,还是有一些原因在里面的:

  • 许可证的名字就叫 “PostgreSQL许可证” —— 你都用项目来命名许可证了,还改什么协议?
  • PostgreSQL项目发起时,以开源社区协作为主旨,意在防止任何单一实体控制本项目。这一点作为项目的精神主旨已经延续了近三十年时间了,并且在项目 项目政策 中有着明确体现。
  • Dave Page 在这封邮件中明确表示过 😊

那么真正的问题就变成了,如果 PostgreSQL 要改变许可证,会出于什么理由呢?通常变更许可证的原因是出于商业决策 —— 但看起来围绕 PostgreSQL 的商业业务与 PostgreSQL 的功能集合一样强壮。冯若航(Vonng)最近写了一篇博客文章,突出展现了围绕 PostgreSQL 打造的软件与商业生态,这还仅仅是一部分。

我说 “仅仅是一部分” 的意思是,在历史上和现在还有更多的项目和商业,是围绕着 PostgreSQL 代码库的某些部分构建的。这些项目中许多都使用了不同的许可证发布,或者干脆就是闭源的。但它们也直接或间接地推动了PostgreSQL 的采用,并使 PostgreSQL 协议变得无处不在。

但 PostgreSQL 不会改变其许可证的最大原因是,这将对所有 PostgreSQL 用户产生不利影响。对一项技术来说,建立信任需要很长时间,尤其是当该技术经常用于应用程序最关键的部分:数据存储与检索。PostgreSQL赢得了良好的声誉 —— 凭借其久经考验的架构、可靠性、数据完整性、强大的功能集、可扩展性,以及背后充满奉献精神的开源社区,始终如一地提供优质、创新的解决方案。修改 PostgreSQL 的许可证将破坏该项目过去近三十年来建立起的所有良好声誉。

尽管 PostgreSQL 项目确实有不完美之处(我当然也对这些不完美的地方有所贡献),但 PostgreSQL 许可证对PostgreSQL 社区和整个开源界来说,确实是一份真正的礼物,我们将继续珍惜并帮助保持 PostgreSQL 真正的自由和开源。毕竟,官网上也是这么说的 ;)


译者评论

能被 PostgreSQL 全球社区核心组成员提名推荐,我感到非常荣幸。上文中 Jonathan 提到我的文章是《PostgreSQL正在吞噬数据库世界》,英文版为《PostgreSQL is Eating The Database World》。发布于 Medium:https://medium.com/@fengruohang/postgres-is-eating-the-database-world-157c204dcfc4 ,并在 HackerNews ,X,LinkedIn 上引起相当热烈的讨论。

Redis 变更其许可证协议,是开源软件领域又一里程碑式的事件 —— 至此,所有头部的 NoSQL 数据库 ,包括 MongoDB, ElasticSearch,加上 Redis ,都已经切换到了 SSPL —— 一种不被 OSI 承认的许可证协议。

Redis 切换为更为严格的 SSPL 协议的核心原因,用 Redis Labs CEO 的话讲就是:“多年来,我们就像个傻子一样,他们拿着我们开发的东西大赚了一笔”。“他们”是谁? —— 公有云。切换 SSPL 的目的是,试图通过法律工具阻止这些云厂商白嫖吸血开源,成为体面的社区参与者,将软件的管理、监控、托管等方面的代码开源回馈社区。

不幸的是,你可以强迫一家公司提供他们的 GPL/SSPL 衍生软件项目的源码,但你不能强迫他们成为开源社区的好公民。公有云对于这样的协议往往也嗤之以鼻,大多数云厂商只是简单拒绝使用AGPL许可的软件:要么使用一个采用更宽松许可的替代实现版本,要么自己重新实现必要的功能,或者直接购买一个没有版权限制的商业许可。

当 Redis 宣布更改协议后,马上就有 AWS 员工跳出来 Fork Redis —— “Redis 不开源了,我们的分叉才是真开源!” 然后 AWS CTO 出来叫好,并假惺惺的说:这是我们员工的个人行为 —— 堪称是现实版杀人诛心。而同样的事情,已经发生过几次了,比如分叉 ElasticSearh 的 OpenSearch,分叉 MongoDB 的 DocumentDB。

因为引入了额外的限制与所谓的“歧视”条款,OSI 并没有将 SSPL 认定为开源协议。因此使用 SSPL 的举措被解读为 —— “Redis 不再开源”,而云厂商的各种 Fork 是“开源”的。从法律工具的角度来说,这是成立的。但从朴素道德情感出发,这样的说法对于 Redis 来说是极其不公正的抹黑与羞辱。

正如罗翔老师所说:法律工具的判断永远不能超越社区成员朴素的道德情感。如果协和与华西不是三甲,那么丢脸的不是这些医院,而是三甲这个标准。如果年度游戏不是巫师3,荒野之息,博德之门,那么丢脸的不是这些厂商,而是评级机构。如果 Redis 不再算“开源”,真正应该感到汗颜的应该是OSI 与开源这个理念。

越来越多的知名开源软件,都开始切换到敌视针对云厂商白嫖的许可证协议上来。不仅仅是 Redis 与 MongoDB,ElasticSearch 在 2021 年也从 Apache 2.0 修改为 SSL 与 ElasticSearch,知名的开源软件 MinIO 与 Grafana 分别在 2020,2021年从 Apache v2 协议切换到了 AGPLv3 协议。

一些老牌的开源项目例如 PostgreSQL ,正如 Jonathan 所说,历史沉淀(三十年的声誉!)让它们已经在事实上无法变更开源协议了。但我们可以看到,许多新强力的 PostgreSQL 扩展插件开始使用 AGPLv3 作为默认的开源协议,而不是以前默认使用的 BSD-like / PostgreSQL 友善协议。例如分布式扩展 Citus,列存扩展 Hydra,ES全文检索替代扩展 BM25,OLAP 加速组件 PG Analytics …… 等等等等。包括我们自己的 PostgreSQL 发行版 Pigsty,也在 2.0 的时候由 Apache 协议切换到了 AGPLv3 协议,背后的动机都是相似的 —— 针对软件自由的最大敌人 —— 云厂商进行反击。

在抵御云厂商白嫖的实践中,修改协议是最常见的做法:但AGPLv3 过于严格容易敌我皆伤,SSPL 因为明确表达这种敌我歧视,不被算作开源。业界需要一种新的歧视性软件许可证协议,来达到名正言顺区分敌我的效果。使用双协议进行明确的边界区分,也开始成为一种主流的开源商业化实践。

真正重要的事情一直都是软件自由,而“开源”只是实现软件自由的一种手段。而如果“开源”的理念无法适应新阶段矛盾斗争的需求,甚至会妨碍软件自由,它一样会过气,并不再重要,并最终被新的理念与实践所替代 —— 比如“本地优先”。


英文原文

WILL POSTGRESQL EVER CHANGE ITS LICENSE?

(Disclosure: I’m on the PostgreSQL Core Team, but what’s written in this post are my personal views and not official project statements…unless I link to something that’s an official project statement ;)

I was very sad to learn today that the Redis project will no longer be released under an open source license. Sad for two reasons: as a longtime Redis user and pretty early adopter, and as an open source contributor. I’ll preface that I’m empathetic to the challenges of building businesses around open source, having been on multiple sides of this equation. I’m also cognizant of the downstream effects of these changes that can completely flip how a user adopts and uses a piece of technology.

Whenever there’s a shakeup in open source licensing, particularly amongst databases and related systems (MySQL => Sun => Oracle being the one that first springs to mind), I’ll hear the question “Will PostgreSQL ever change its license?”

The PostgreSQL website has an answer:

Will PostgreSQL ever be released under a different license? The PostgreSQL Global Development Group remains committed to making PostgreSQL available as free and open > source software in perpetuity. There are no plans to change the PostgreSQL License or release PostgreSQL under a different license.

(Disclosure: I did help write the above paragraph).

The PostgreSQL Licence (aka “License” – Dave Page and I have fun going back and forth on this) is an Open Source Initiative (OSI) recognized license, and has a very permissive model. In terms of which license it’s most similar to, I defer to this email that Tom Lane wrote in 2009.

That said, there are a few reasons why PostgreSQL won’t change it’s license:

The question then becomes - is there a reason that PostgreSQL would change its license? Typically these changes happen as part of a business decision - but it seems that business around PostgreSQL is as robust as its feature set. Ruohang Feng (Vonng) recently wrote a blog post that highlighted just a slice of the PostgreSQL software and business ecosystem that’s been built around it, which is only possible through the PostgreSQL Licence. I say “just a slice” because there’s even more, both historically and current, projects and business that are built up around some portion of the PostgreSQL codebase. While many of these projects may be released under different licenses or be closed source, they have helped drive, both directly and indirectly, PostgreSQL adoption, and have helped make the PostgreSQL protocol ubiquitous.

But the biggest reason why PostgreSQL would not change its license is the disservice it would do to all PostgreSQL users. It takes a long time to build trust in a technology that is often used for the most critical part of an application: storage and retrieval of data. PostgreSQL has earned a strong reputation for its proven architecture, reliability, data integrity, robust feature set, extensibility, and the dedication of the open source community behind the software to consistently deliver performant and innovative solutions. Changing the license of PostgreSQL would shatter all of the goodwill the project has built up through the past (nearly) 30 years.

While there are definitely parts of the PostgreSQL project that are imperfect (and I certainly contribute to those imperfections), the PostgreSQL Licence is a true gift to the PostgreSQL community and open source in general that we’ll continue to cherish and help keep PostgreSQL truly free and open source. After all, it says so on the website ;)

PostgreSQL正在吞噬数据库世界

PostgreSQL 并不是一个简单的关系型数据库,而是一个数据管理的抽象框架,具有吞噬整个数据库世界的力量。而这也是正在发生的事情 —— “一切皆用 Postgres” 已经不再是少数精英团队的前沿探索,而是成为了一种进入主流视野的最佳实践。


OLAP 领域迎来踢馆者

在 2016 年的一次数据库沙龙里,我提出了一个观点: 现在 PostgreSQL 生态的一个主要遗憾是,缺少一个足够好的列式存储分析插件来做 OLAP 分析。尽管PostgreSQL 本身提供了很强大的分析功能集,应付常规的分析任务绰绰有余。但在较大数据量下全量分析的性能,相比专用的实时数仓仍然有些不够看。

以分析领域的权威评测 ClickBench 为例,我们在其中标注出了 PostgreSQL 与生态扩展插件以及兼容衍生数据库在其中的性能表现。原生未经过调优的 PostgreSQL 表现较为拉垮(x1050),但经过调优后可以达到(x47);此外还有三个与分析有关系的扩展:列存 Hydrax42),时序扩展 TimescaleDBx103),以及分布式扩展 Citusx262)。

clickbench.png

ClickBench c6a.4xlarge, 500gb gp2,Hot Run 执行相对耗时

这样的分析性能表现不能说烂,因为比起 MySQL,MariaDB 这样的纯 OLTP 数据库的辣眼表现(x3065,x19700)确实好很多;但第三梯队的性能表现也绝对说不上足够好,与专注于 OLAP 的第一梯队组件:Umbra,ClickHouse,Databend,SelectDB(x3~x4)相比,在分析性能上仍然有十几倍的性能差距。食之无味,弃之可惜。

然而, ParadeDB DuckDB 的出现改变了这一点!

ParadeDB 提供的 PG 原生扩展 pg_analytics 实现了第二梯队(x10)的性能水准,与第一梯队只有 3~4 倍的性能差距。相对于其他功能上的收益,这种程度的性能差距通常是可以接受的 —— ACID,新鲜性与实时性,无需 ETL、额外学习成本、维护独立的新服务,更别提它还提供了 ElasticSearch 质量的全文检索能力。

DuckDB 则专注于 OLAP ,将分析性能这件事做到了极致(x3.2) —— 略过第一名 Umbra 这种学术研究型闭源数据库,DuckDB 也许是 OLAP 实战性能最快的数据库了。它并不是 PG 的扩展插件,但它是一个嵌入式文件数据库,而 DuckDB FDW 以及 pg_quack 这样的 PG 生态项目,能让 PostgreSQL 充分利用 DuckDB 带来的完整分析性能红利!

ParadeDB 与 DuckDB 的出现让 PostgreSQL 的分析性能来到了 OLAP 的第一梯队与金字塔尖,弥补了 PostgreSQL 在 OLAP 性能这最后一块关键短板。


分久必合的数据库领域

数据库诞生伊始,并没有 OLTP 与 OLAP 的分野。OLAP 数据仓库从数据库中“独立”出来,已经是上世纪九十年代时候的事了 —— 因为传统的 OLTP 数据库难以支撑起分析场景下的查询模式,数据量与性能要求。

在相当一段时间里,数据处理的最佳实践是使用 MySQL / PG 处理 OLTP 工作负载,并通过 ETL 将数据同步到专用的 OLAP 组件中去处理,比如 Greenplum, ClickHouse, Doris, Snowflake 等等。

设计数据密集型应用,Martin Kleppmann,第三章

与许多 “专用数据库” 一样,专业的 OLAP 组件的优势往往在于性能 —— 相比原生 PG 、MySQL 上有 1~3 个数量级的提升;而代价则是数据冗余、 大量不必要的数据搬运工作、分布式组件之间缺乏一致性、额外的专业技能带来的复杂度成本、学习成本、以及人力成本、 额外的软件许可费用、极其有限的查询语言能力、可编程性、可扩展性、有限的工具链、以及与OLTP 数据库相比更差的数据完整性和可用性 —— 但这是一个合理的利弊权衡

然而天下大势,分久必合,合久必分硬件遵循摩尔定律又发展了三十年,性能翻了几个数量级,成本下降了几个数量级。在 2024 年的当下,x86 单机可以达到几百核 (512 vCPU EPYC 9754x2),几个TB的内存,单卡 NVMe SSD 可达 64TB,全闪单机柜 2PB ;S3 这样对象存储更是能实现几乎没有上限的存储。

io-bandwidth.png

硬件的发展解决了数据量的问题,而数据库软件的发展(PostgreSQL,ParadeDB,DuckDB)解决了查询模式的问题,而这导致分析领域 —— 所谓的“大数据” 行业基本工作假设面临挑战。

正如 DuckDB 发表的宣言《大数据已死》所主张的:大数据时代已经结束了 —— 大多数人并没有那么多的数据,大多数数据也很少被查询。大数据的前沿随着软硬件发展不断后退,99% 的场景已经不再需要所谓“大数据”了。

如果 99% 的场景甚至都可以放在一台计算机上用单机/主从的 DuckDB 或 PostgreSQL 搞定,那么使用专用的分析组件还有多少意义?如果每台手机都可以自由自主收发短信,那么 BP 机还有什么存在价值?(北美医院还在用BP机,正好比也还有 1% 不到的场景也许真的需要“大数据”)

基本工作假设的变化,将重新推动数据库世界从百花齐放的“合久必分”阶段,走向“分久必合”的阶段,从大爆发到大灭绝,大浪淘沙中,新的大一统超融合数据库将会出现,重新统一 OLTP 与 OLAP。而承担重新整合数据库领域这一使命的会是谁?


吞食天地的 PostgreSQL

数据库领域有许多“细分领域”:时序数据库,地理空间数据库,文档数据库,搜索数据库,图数据库,向量数据库,消息队列,对象数据库。而 PostgreSQL 在任何一个领域都不会缺席。

一个 PostGIS 插件,成为了地理空间事实标准;一个 TimescaleDB 扩展,让一堆“通用”时序数据库尴尬的说不出话来;一个向量扩展 PGVector 插件,更是让整个 专用向量数据库细分领域 变成笑话。

同样的事情已经发生过很多次,而现在,我们将在拆分最早,地盘最大的一个子领域 OLAP 分析中再次见证这一点。但 PostgreSQL 要替代的可不仅仅是 OLAP 数仓,它的野望是整个数据库世界!

ecosystem.jpg

然 PostgreSQL 有何德何能,可当此大任?诚然 PostgreSQL 先进,但 Oracle 也先进;PostgreSQL 开源,但 MySQL 也开源。PostgreSQL 先进且开源,这是它与 Oracle / MySQL 竞争的底气,但要说其独一无二的特点,那还得是它的极致可扩展性,与繁荣的扩展生态

survey.png

TimescaleDB 2022 社区调研:用户选择 PostgreSQL 的原因:开源,先进,扩展

PostgreSQL 并不是一个简单的关系型数据库,而是一个数据管理的抽象框架,具有囊括一切,吞噬整个数据库世界的力量。而它的核心竞争力(除了开源与先进)来自可扩展性,即基础设施的可复用性与扩展插件的可组合性


极致可扩展性的魔法

PostgreSQL 允许用户开发功能模块,复用数据库公共基础设施,以最低的成本交付功能。例如,仅有两千行代码的向量数据库扩展 pgvector 与百万行代码的 PostgreSQL 在复杂度上相比可以说微不足道,但正是这“微不足道”的扩展,实现了完整的向量数据类型与索引能力,干翻了几乎所有专用向量数据库。

为什么?因为 PGVECTOR 作者不需要操心数据库的通用额外复杂度:事务 ACID,故障恢复,备份PITR,高可用,访问控制,监控,部署,三方生态工具,客户端驱动这些需要成百上千万行代码才能解决好的问题,只需要关注自己所需问题的本质复杂度即可。

向量数据库哪家强?

再比如,ElasticSearch 基于 Lucene 搜索库开发,而 Rust 生态有一个改进版的下一代 Tantivy 全文搜索库作为 Lucene 的替代;而 ParadeDB 只需要将其封装对接到 PostgreSQL 的接口上,即可提供比肩 ElasticSearch 的搜索服务。更重要的是,它可以站在 PostgreSQL 巨人的肩膀上,借用 PG 生态的全部合力(例如,与 PG Vector 做混合检索),不讲武德地用数据库全能王的力量,去与一个专用数据库单品来对比。

img

Pigsty 中提供了 255 个可用扩展插件,在生态中还有 1000+ 扩展


可扩展性带来的另一点巨大优势是扩展的可组合性,让不同扩展相互合作,产生出 1+1 » 2 的协同效应。例如,TimescaleDB 可以与 PostGIS 组合使用,提供时空数据支持;再比如,提供全文检索能力的 BM25 扩展可以和提供语义模糊检索的 PGVector 扩展组合使用,提供混合检索能力。

再比如,分布式扩展 Citus 可以将单机主从数据库集群,原地升级改造为透明水平分片的分布式数据库集群。而这个能力是可以与其他功能正交组合的,因此,PostGIS 可以成为分布式地理数据库,PGVector 可以成为分布式向量数据库,ParadeDB 可以成为分布式全文搜索数据库,诸如此类。


更强大的地方在于,扩展插件是独立演进的,不需要繁琐的主干合并,联调协作。因此可以 Scale —— PG 的可扩展性允许无数个团队并行探索数据库前研发展方向,而扩展全部都是的可选的,不会影响主干核心能力的稳定性。那些非常强大成熟的特性,则有机会以稳定的形态进入主干中。

通过极致可扩展性的魔法,PostgreSQL 做到了**守正出奇,实现了主干极致稳定性与功能敏捷性的统一。**扎实的基本盘配上惊人的演进速度,让它成为了数据库世界中的一个异数,改变了数据库世界的游戏规则。


改变游戏规则的玩家

PostgreSQL 的出现,改变了数据库领域的游戏规则:任何试图开发“新数据库内核”的团队,都需要经过这道试炼与考验 —— 相比开源免费、功能齐备的 Postgres,价值点在哪里?

至少到硬件出现革命性突破前,实用的通用数据库新内核都不太可能诞生了,因为任何单一数据库都无法与所有扩展加持下的 PG 在整体实力上相抗衡 —— 包括 Oracle,因为 PG 还有开源免费的必杀技。

而某个细分领域的数据库产品,如果能在单点属性(通常是性能)上相比 PostgreSQL 实现超过一个数量级的优势,那也许还有一个专用数据库的生态位存在。但通常用不了多久,便会有 PostgreSQL 生态的开源替代扩展插件滚滚而来。因为选择开发 PG 扩展,而不是一个完整数据库的团队会在追赶复刻速度上有碾压性优势!

因此,如果按照这样的逻辑展开,PostgreSQL 生态的雪球只会越滚越大,随着优势的积累,不可避免地进入一家独大的状态。在几年的时间内,实现 Linux 内核在服务器操作系统领域的状态。而各种开发者调研报告,数据库流行趋势都在印证着这一点。

sf-survey.png

StackOverflow 2023 调研结果,PostgreSQL 三项全能王

sf-trend.jpg

StackOverflow过去7年的数据库指标走势

在引领潮流的 HackerNews StackOverflow 上,PostgreSQL 早已成为了最受欢迎的数据库。许多新的开源项目都默认使用 PostgreSQL 作为首要,甚至唯一的数据库 —— 例如,给各种数据库做模式管理的 Bytebase。《云时代数据库DevOps:硅谷调研》也提出,许多新一代互联网公司都开始积极拥抱并 All in PostgreSQL。

正如《技术极简主义:一切皆用 Postgres 》所言:简化技术栈、减少组件、加快开发速度、降低风险并提供更多功能特性的方法之一就是 “一切皆用 Postgres”。Postgres 能够取代许多后端技术,包括 MySQL,Kafka、RabbitMQ、ElasticSearch,Mongo和 Redis,至少到数百万用户时都毫无问题。一切皆用 Postgres ,已经不再是少数精英团队的前沿探索,而是成为了一种进入主流视野的最佳实践。


还有什么可以做的?

我们已经不难预见到数据库领域的终局。但我们又能做什么,又应该做什么呢?

PostgreSQL 对于绝大多数场景都已经是一个足够完美的数据库内核了,在这个前提下,数据库内核卡脖子纯属无稽之谈。这些Fork PostgreSQL 和 MySQL 并以内核魔改作为卖点的所谓“数据库”基本没啥出息。

这好比今天我们看 Linux 操作系统内核一样,尽管市面上有这么多的 Linux 操作系统发行版,但大家都选择使用同样的 Linux 内核,吃饱了撑着魔改内核属于没有困难创造困难也要上,会被业界当成山炮看待。

同理,数据库内核本身已经不再是主要矛盾,焦点将会集中到两个方向上 —— 数据库扩展与数据库服务!前者体现为数据库内部的可扩展性, 后者体现为数据库外部的可组合性。而竞争的形式,正如操作系统生态一样 —— 集中于数据库发行版上。对于数据库领域来说,只有那些以扩展和服务作为核心价值主张的发行版,才有最终成功的可能。

做内核的厂商不温不火,MariaDB 作为 MySQL 的亲爹 Fork 甚至都已经濒临退市,而白嫖内核自己做服务与扩展卖 RDS 的 AWS 可以赚的钵满盆翻。投资机构已经出手了许多 PG 生态的扩展插件与服务发行版:Citus,TimescaleDB,Hydra,PostgresML,ParadeDB,FerretDB,StackGres,Aiven,Neon,Supabase,Tembo,PostgresAI,以及我们正在做的 Pigsty 。


PostgreSQL 生态中的一个困境就是,许多扩展插件,生态工具都是独立演进,各自为战的,没有一个整合者能将他们凝聚起来形成合力。例如,提供分析的 Hydra 会打一个包一个 Docker 镜像, PostgresML 也会打自己的包和镜像,各家只发行加装了自己扩展的 Postgres 镜像。而这些朴素的镜像与包也距离 RDS 这样完整的数据库服务相距甚远。

即使是类似于 AWS RDS 这样的服务提供商与生态整合者,在诸多扩展面前也依然力有所不逮,只能提供其中的少数。更多的强力扩展出于各种原因(AGPLv3 协议,多租户租赁带来的安全挑战)而无法使用。从而难以发挥 PostgreSQL 生态扩展的协同增幅作用。

这里列出了一些重要扩展,对比基于最新的 PostgreSQL 16 主干版本进行,截止至 2024-02-28

扩展类目 Pigsty RDS / PGDG 官方仓库 阿里云 RDS AWS RDS PG
加装扩展 自由加装 不允许 不允许
地理空间 PostGIS 3.4.2 PostGIS 3.3.4 / Ganos 6.1 PostGIS 3.4.1
雷达点云 PG PointCloud 1.2.5 Ganos PointCloud 6.1
向量嵌入 PGVector 0.6.1 / Svector 0.5.6 pase 0.0.1 PGVector 0.6
机器学习 PostgresML 2.8.1
时序扩展 TimescaleDB 2.14.2
水平分布式 Citus 12.1
列存扩展 Hydra 1.1.1
全文检索 pg_bm25 0.5.6
图数据库 Apache AGE 1.5.0
GraphQL PG GraphQL 1.5.0
OLAP pg_analytics 0.5.6
消息队列 pgq 3.5.0
DuckDB duckdb_fdw 1.1
模糊分词 zhparser 1.1 / pg_bigm 1.2 zhparser 1.0 / pg_jieba pg_bigm 1.2
CDC抽取 wal2json 2.5.3 wal2json 2.5
膨胀治理 pg_repack 1.5.0 pg_repack 1.4.8 pg_repack 1.5.0

许多关键扩展在RDS中并不可用

扩展是 PostgreSQL 的灵魂,无法自由使用扩展的 Postgres 就像做菜不放盐。只能和 MySQL 放在同一个 RDS 的框子里同台,龙游浅水,虎落平阳。

而这正是我们想要解决的首要问题之一。


知行合一的实践:Pigsty

虽然接触 MySQL 和 MSSQL 要早得多,但我在 2015 年第一次上手 PostgreSQL 时,就相信它会是数据库领域的未来了。快十年过去,我也从 PG 的使用者,管理者,变为了贡献者,开发者。也不断见证着 PG 走向这一目标。

在与形形色色的用户沟通交流中,我早已发现数据库领域的木桶短板不是内核 —— 现有的 PostgreSQL 已经足够好了,而是用好数据库内核本身的能力,这也是 RDS 这样的服务赚的钵满盆翻的原因。

但我希望这样的能力,应该像自由软件运动所倡导的理念那样,像 PostgreSQL 内核本身一样 —— 普及到每一个用户手中,而不是必须向赛博空间上的封建云领主花大价钱租赁。

所以我打造了 Pigsty —— 一个开箱即用的开源 PostgreSQL 数据库发行版,旨在凝聚 PostgreSQL 生态扩展的合力,并把提供优质数据库服务的能力普及到每个用户手中。

Pigsty 是 PostgreSQL in Great STYle 的缩写,意为 PostgreSQL 的全盛状态

我们提出了六点核心价值主张,对应 PostgreSQL 数据库服务中的的六个核心问题:Postgres 的可扩展性基础设施的可靠性图形化的可观测性服务的可用性工具的可维护性,以及扩展模块和三方组件可组合性

Pigsty 六点价值主张的首字母合起来,则为 Pigsty 提供了另外一种缩写解释:

Postgres, Infras, Graphics, Service, Toolbox, Yours.

属于你的图形化 Postgres 基础设施服务工具箱。

可扩展的 PostgreSQL 是这个发行版中最重要的价值主张。在刚刚发布的 Pigsty v2.6 中,我们整合了上面提到的 DuckdbFDW 与 ParadeDB 扩展,这两个插件让 PostgreSQL 的分析能力得到史诗级增强,而我们确保每个用户都能轻松用得上这样的能力。

regards.png

来自 ParadeDB 创始人与 DuckdbFDW 作者的感谢致意

我们希望整合 PostgreSQL 生态里的各种力量,并将其凝聚在一起形成合力,打造一个数据库世界中的 Ubuntu 发行版。而我相信,内核之争早已尘埃落定,而这里才会是数据库世界的未来竞争焦点。

  • PostGIS:提供地理空间数据类型与索引支持,GIS 事实标准 (& pgPointCloud 点云,pgRouting 寻路)
  • TimescaleDB:添加时间序列/持续聚合/分布式/列存储/自动压缩的能力
  • PGVector:添加 AI 向量/嵌入数据类型支持,以及 ivfflat 与 hnsw 向量索引。(& pg_sparse 稀疏向量支持)
  • Citus:将经典的主从PG集群原地改造为水平分片的分布式数据库集群。
  • Hydra:添加列式存储与分析能力,提供比肩 ClickHouse 的强力分析能力。
  • ParadeDB:添加 ElasticSearch 水准的全文搜索能力与混合检索的能力。(& zhparser 中文分词)
  • Apache AGE:图数据库扩展,为 PostgreSQL 添加类 Neo4J 的 OpenCypher 查询支持,
  • PG GraphQL:为 PostgreSQL 添加原生内建的 GraphQL 查询语言支持。
  • DuckDB FDW:允许您通过 PostgreSQL 直接读写强力的嵌入式分析数据库 DuckDB 文件 (& DuckDB CLI 本体)。
  • Supabase:基于 PostgreSQL 的开源的 Firebase 替代,提供完整的应用开发存储解决方案。
  • FerretDB:基于 PostgreSQL 的开源 MongoDB 替代,兼容 MongoDB API / 驱动协议。
  • PostgresML:使用SQL完成经典机器学习算法,调用、部署、训练 AI 模型。

Pigsty 支持的 180+ 扩展列表

开发者朋友们,你们的选择会塑造数据库世界的未来。希望我的这些工作,可以帮助你们更好的用好这世界上最先进的开源数据库内核 —— PostgreSQL。

Medium 英文版 | GitHub 仓库: Pigsty


参考阅读

Pigsty v2.6:PostgreSQL 踢馆 OLAP

技术极简主义:一切皆用Postgres

PG生态新玩家ParadeDB

DBA会被云淘汰吗?

令人惊叹的PostgreSQL可伸缩性

中国对PostgreSQL的贡献约等于零吗?

展望PostgreSQL的2024 (Jonathan Katz)

2023年度数据库:PostgreSQL (DB-Engine)

MySQL的正确性为何如此拉垮?

向量数据库凉了吗?

重新拿回计算机硬件的红利

数据库真被卡脖子了吗?

PG查询优化:观宏之道

FerretDB:假扮成MongoDB的PostgreSQL

如何用 pg_filedump 抢救数据?

PGSQL x Pigsty: 数据库全能王来了

Pigsty 特性与快速上手

PG先写脏页还是先写WAL?

PostgreSQL:世界上最成功的数据库

ISD数据集:分析全球120年气候变化

AI大模型与向量数据库 PGVECTOR

更好的开源RDS替代:Pigsty

PostgreSQL 到底有多强?

为什么PostgreSQL是最成功的数据库?

PG与Pigsty用户需求问卷调研结果

高可用PgSQL集群架构设计与落地

为什么说PostgreSQL前途无量?

Postgres本地化排序规则

PG复制标识详解(Replica Identity)

利用监控系统诊断PG慢查询

数据库集群管理概念与实体命名规范

PostgreSQL的KPI

PostgreSQL监控系统Pigsty概述

故障档案:PG安装扩展导致无法连接

PostgreSQL中的表锁

把PG放入Docker是一个好主意吗?

PostgreSQL监控系统概览

pg_dump导致的血案

PostgreSQL数据页面损坏修复

PostgreSQL关系膨胀:原理,监控与处理

探探PostgreSQL开发规约

并发异常那些事

PG好处都有啥?

IP归属地查询的高效实现

PostGIS高效解决行政区划归属查询问题

技术极简主义:一切皆用Postgres

本文由 Stephan Schmidt @ KingOfCoders 发表于 Hacker News 并引发热议[1]:使用 Postgres 替代 Kafka、RabbitMQ、ElasticSearch、Mongo 和 Redis 是一种切实可行的方式,这样做可以极大降低系统复杂度,并将敏捷性发挥到极致。


如何简化复杂度并快速前进:用 PostgreSQL 完成所有任务

欢迎,HN(Hacker News)读者们。技术是关于取舍的艺术。全面使用 PostgreSQL 完成所有工作,也是一种策略与权衡。显然,我们应根据需求选用合适的工具。很多情况下,这个工具就是 Postgres

在辅助许多初创企业的过程中,我观察到很多人过度复杂化他们的系统,这样做的公司远超过那些选择了过于简单工具的公司。如果你们拥有超过一百万用户,超过五十名开发者,并且你们确实需要 Kafka、Spark 和 Kubernetes,那么请便。如果你的系统数量比开发者还多,只用 Postgres 就是一个明智之选。

附言:全面使用 Postgres 并不意味着单台器搞定一切 ;-)


简单来说,一切皆可用 Postgres 解决

请神容易送神难,让复杂度溜进家里,再送走就没那么容易了。


然而,我们有极致简化的方案


在初创公司中简化技术栈、减少组件、加快开发速度、降低风险并提供更多功能特性的方法之一就是**“一切皆用 Postgres”**。Postgres 能够取代许多后端技术,包括 Kafka、RabbitMQ、ElasticSearch,Mongo和 Redis ,至少到数百万用户时都毫无问题。

使用 Postgres 替代 Redis 作为缓存,使用 UNLOGGED Table[3] 并用 TEXT 类型存储 JSON 数据,并使用存储过程来添加并强制执行过期时间,正如 Redis 所做的那样。

使用 Postgres 作为消息队列,采用 SKIP LOCKED[4] 来代替Kafka(如果你只需要消息队列的能力)。

使用加装了 TimescaleDB[5] 扩展的 Postgres 作为数据仓库。

使用 PostgreSQL 的 JSONB[6] 类型来存储、索引、搜索 JSON 文档,从而替代 MongoDB。

使用加装 pg_cron[7] 扩展的 Postgres 作为定时任务守护程序,在特定时间执行特定任务,例如发送邮件,或向消息队列中添加事件。

使用 Postgres + PostGIS 执行 地理空间查询[8]。

使用 Postgres 进行全文搜索[9],加装 ParadeDB 替代 ElasticSearch。

使用 Postgres 在数据库中生成JSON[10],免去服务器端代码编写,直接供 API 使用。

使用 GraphQL适配器[11],也可以让 PostgreSQL 提供 GraphQL 服务。

我已明言,一切皆可Postgres


关于作者 Stephan

作为一名CTO、临时CTO、CTO教练以及开发者,斯蒂芬在许多快速成长的初创公司的技术部门中都留下了自己的足迹。他在1981年左右,因为想编写视频游戏,就在一家百货公司自学了编程。斯蒂芬在乌尔姆大学(University of Ulm)学习计算机科学,专攻分布式系统和人工智能,并且还学习了哲学。90年代互联网进入德国时,他作为几家初创公司的首位编程员工。他创办过一家获风险资本投资的初创公司,在其他获得风险资本投资的快速成长的初创公司中负责架构、流程和成长挑战,曾在ImmoScout担任管理职位,并且是一家eBay Inc.公司的CTO。在他的妻子成功出售了她的初创公司后,他们搬到了海边,斯蒂芬开始从事CTO辅导工作。你可以在LinkedIn上找到他,或者在Twitter上关注@KingOfCoders。


译者评论

译者:冯若航,创业者与 PostgreSQL 专家,下云倡导者,开源 PG RDS 替代,开箱即用的 PostgreSQL 发行版 —— Pigsty 作者。

使用 Postgres 完成一切工作并不是一种空想,而是一种正在流行起来的最佳实践。对此我感到非常欣慰:早在 2016 年时我便看到了这里的潜力[12]并选择躬身入局,而事情的发展正如所愿。

我曾任职的探探,便是这条道路的先锋 —— PostgreSQL for Everything。这是一个由瑞典创始团队打造的中国互联网 App —— 使用 PostgreSQL 的规模与复杂度在中国首屈一指。探探的技术架构选型参照了 Instagram —— 或者说更为激进,几乎所有业务逻辑都使用 PostgreSQL 存储过程实现(甚至包括 100ms 的推荐算法!)。

探探整个系统架构围绕 PostgreSQL 而设计并展开。几百万日活,几百万全局 DB-TPS,几百 TB数据的量级下,数据组件只用了 PostgreSQL 。直到接近千万日活,才开始进行架构调整引入独立的数仓,消息队列和缓存。在 2017 年,我们甚至没有使用 Redis 缓存,250万 TPS 完全是由一百多台服务器上的 PostgreSQL 直接扛下的。消息队列也是用 PostgreSQL 实现的,早中期的数据分析也是由一套十几TB的专用PG集群负责。我们早已经践行了 —— “一切皆用 PostgreSQL 的理念”,并从中获益良多。

这个故事还有下半段 —— 随后的 “微服务改造” 带来了海量的复杂度,最终让系统陷入泥潭。这让我从另一个角度更加确信这一点 —— 我非常怀念一切皆用 PostgreSQL 时那种简单可靠高效敏捷的状态。


PostgreSQL 并不是一个简单的关系型数据库,而是一个数据管理的抽象框架,具有囊括一切,吞噬整个数据库世界的潜力。在十年前,这仅仅是一种潜力与可能性,在十年后,它已经兑现成为真正的影响力。而我很高兴能见证这个过程,并推动这一进程。

PostgreSQL is for Everything!


参考阅读

PGSQL x Pigsty: 数据库全能王来了

PG生态新玩家ParadeDB

FerretDB:假扮成MongoDB的PostgreSQL

AI大模型与向量数据库 PGVECTOR

PostgreSQL 到底有多强?

PostgreSQL:世界上最成功的数据库

为什么PostgreSQL是最成功的数据库?

为什么说PostgreSQL前途无量?

更好的开源RDS替代:Pigsty

References

PG生态新玩家:ParadeDB

微信公众号原文链接


PG生态新玩家ParadeDB

YC S23 投了一个新项目 ParadeDB, 非常有意思。他们的 Slogan 是 “Postgres for Search & Analytics —— Modern Elasticsearch Alternative built on Postgres”。就是用于搜索和分析的 PostgreSQL,旨在成为 Elasticsearch 的替代。

PostgreSQL 的生态确实越来越繁荣了,在基于 PG 的扩展与衍生中,我们已经有了基于 MongoDB 开源替代 —— FerretDB,SQL Server 开源替代 Babelfish,Firebase 开源替代 Supabase,AirTable 开源替代 NocoDB,现在又多了 ElasticSearch 开源替代 —— ParadeDB。

ParadeDB 实际上是由三个 PostgreSQL 扩展组成:pg_bm25pg_analytics,以及 pg_sparse。这三个扩展都可以独立使用了。我已经将这几个扩展打好包(v0.5.6),并将会在 Pigsty 的下个 Release 中默认收录,让用户能够开箱即用。

我翻译了 ParadeDB 的官网介绍与四篇博客文章,为您介绍这个 PostgreSQL 生态的新星。 今天是第一篇 —— 概览


ParadeDB

我们荣幸地向您介绍 ParadeDB:针对搜索场景优化的 PostgreSQL 数据库。ParadeDB 是第一个旨在成为 Elasticsearch 替代的 Postgres 数据库构建,被设计为可以在PG表上进行闪电般快速的全文检索、语义检索、以及混合检索。

ParadeDB解决什么问题?

对于许多组织而言,搜索依然是一个未解问题 —— 尽管有像 Elasticsearch 这样的巨头存在,但大多数与其打过交道的开发者都知道,运行、调优和管理 Elasticsearch 是多么痛苦的一件事。虽然也有其他的搜索引擎服务,但在现有数据库上粘连对接这些外部服务,会引入更多重建索引和数据复制的复杂难题与成本。

那些追求统一权威数据源与搜索引擎的开发者转了 Postgres,PG 已经通过 tsvector 提供了基本的全文检索能力,也通过 pgvector 提供了向量语义检索能力。这些工具也许对于简单用例和中等大小的数据集来说很好使,但当表变大或查询变得复杂时就有些不够用了:

  1. 大表上的排序和关键词搜索非常缓慢
  2. 不支持 BM25 计算
  3. 没有混合检索支持,将向量搜索与全文搜索的技术
  4. 没有实时搜索 — 数据必须手动重新索引或重新嵌入
  5. 对复杂查询如分面或相关性调优的支持有限

到目前为止,我们已经目睹了许多工程团队用很勉强的方式在 Postgres 上叠加了一套 Elasticsearch,随即因为后者太过于臃肿、昂贵或复杂,而最终放弃。我们在想:如果 Postgres 本身就带有 ElasticSearch 水平的搜索会发生什么?那么开发者就不会有这种两难选择了 —— 统一使用 PostgreSQL 但搜索能力受限,还是使用事实源和搜索引擎两种独立的服务?

ParadeDB适用于谁?

Elasticsearch 拥有广泛的应用场景,但我们并不企图一蹴而就地覆盖所有场景——至少现阶段不是。我们更倾向于专注于一些核心场景 —— 专为那些希望在 PostgreSQL 上进行搜索的用户服务。对于以下情况,ParadeDB 会是您的理想选择:

  • 希望使用单一 Postgres 作为事实来源,厌恶在多个服务之间搬运复制数据。
  • 希望在不损害性能与可伸缩性的前提下,对存储在 Postgres 中的海量文档进行全文搜索。
  • 希望 ANN/相似度搜索与全文搜索相结合,从而获得更精准的语义匹配效果

ParadeDB产品介绍

ParadeDB 是一个完全托管的 Postgres 数据库,具有在任何其他 Postgres 提供者中未发现的索引和搜索 Postgres 表的能力:

特性 描述
BM25全文搜索 支持布尔、模糊、提升和关键字查询的全文搜索。搜索结果使用 BM25 算法打分。
分面搜索 Postgres 列可以定义为分面,以便轻松分桶和收集指标。
混合搜索 搜索结果可以打分,综合考虑语义相关性(向量搜索)与全文相关性( BM25)。
分布式搜索 表可以进行分片,以便进行并行查询加速。
生成式搜索 Postgres 列可以输入到大型语言模型(LLMs)中,用于自动摘要、分类或文本生成。
实时搜索 文本索引和向量列自动与底层数据保持同步。

与 AWS RDS 等托管服务不同,ParadeDB 是一个 PostgreSQL 扩展插件,不需要任何设置,可以与整个 PG 生态集成,并完全可定制。ParadeDB 是开源的(AGPLv3),并提供了一个简单的 Docker Compose 模板以满足需要自建/定制的开发者的需求。

ParadeDB 的构建方式

ParadeDB 的核心是一个带有自定义扩展的标准 Postgres 数据库,这些扩展使用 Rust 编写,引入了增强的搜索能力。

ParadeDB 的搜索引擎基于 Tantivy 构建,Tantivy 是受 Apache Lucene 启发的开源 Rust 搜索库。其索引作为原生的 PG 索引存储在PG中,从而避免了繁琐的数据复制/ETL工作,并同时可以确保事务 ACID。

ParadeDB 为 Postgres 生态提供了一个新扩展:pg_bm25pg_bm25 使用 BM25 评分算法在 Postgres 中实现了基于 Rust 的全文搜索。ParadeDB 会预装这个扩展插件。

下一步是什么?

ParadeDB 的托管云版本目前处于 PrivateBeta 阶段。我们的目标是在 2024 年初推出一个自助服务的云平台。如果你想在此期间访问 PrivateBeta 版本,欢迎加入我们的等待名单

我们核心团队的重点是开发 ParadeDB 的开源版本,将在 2023 年冬季推出。

我们 Build in Public,并很高兴能与整个社区分享 ParadeDB。欢迎关注我们,在未来的博文中我们会进一步详细介绍 ParadeDB 背后的有趣技术挑战。

令人惊叹的PostgreSQL可伸缩性

本文概述了 Cloudflare 是如何利用 15 个 PostgreSQL 集群,伸缩到支持每秒 5500 万个请求。

2009年7月,美国加州,一个创业团队搞了一个名为 Cloudflare 的内容分发网络(CDN),用于加速互请求,让网络访问更稳定且更快捷。他们在发展初期面临着各种挑战,然而其增长速度却十分惊人。

Overall Internet Traffic; PostgreSQL Scalability

互联网流量全局概览

现在他们承载着 20% 的互联网流量,每秒 5500 万个 HTTP 请求。 而他们仅仅使用 15 个 PostgreSQL 集群就做到了这一点。

Cloudflare 使用 PostgreSQL 来存储服务元数据,并处理 OLTP 工作负载。然而在同一个集群支持有着多种不同负载类型的租户是一个难题。一个**集群(Cluster)是一组数据库服务器,一个租户(tenant)**是特定用户或用户组专用的隔离数据空间。


PostgreSQL的可伸缩性

以下是他们如何将 PostgreSQL 的可伸缩性用到极致的。

1. 争用

大多数客户端都会相互争用 Postgres 连接。但是 Postgres 连接的成本很高,因为每个连接都是操作系统级别的独立进程。而且每个租户都有独特的工作负载类型,所以很难创建一个全局阈值进行限流。

而且,人工限制行为不端的租户是一项巨大的工作。某个租户可能会发起一个开销巨大的查询,因而阻塞邻居租户的查询饿着他们。同时,一旦查询到达数据库服务器这儿,再想隔离它就很难了。

Connection Pooling With PgBouncer

使用 Pgbouncer 进行连接池化

因此他们使用 Pgbouncer 作为 Postgres 前面的连接池。PgBouncer 将充当 TCP 代理,池化 Postgres 连接。租户连接到 PgBouncer ,而不是直连 Postgres。因而限制了 Postgres 连接的数量,也能防止连接饥饿现象。

此外,PgBouncer 还通过使用持久连接来规避了创建和销毁数据库连接的高昂开销,也被用于在运行时限流那些发起高开销查询的租户们。

2. 惊群

当许多客户端同时查询服务器时就会出现**惊群(Thundering Herd)**的问题,这会导致数据库性能降级。

Thundering Herd Problem

惊群

当应用程序被重新部署时,其状态会初始化,应用会一次性创建许多条数据库连接。因而当当租户争抢 Postgres 连接时,就会引起惊群现象,Cloudflare 使用 PgBouncer 来限制特定租户创建的 Postgres 连接数。

3. 性能

Cloudflare 没有在云上运行 PostgreSQL ,而是使用没有任何虚拟化开销的裸金属物理机,以实现最好的性能。

Load Balancing Traffic Between Database Instances

在数据库实例之间对流量做负载均衡

Cloudflare 使用 HAProxy 作为四层负载均衡,Pgbouncer 将查询转发至 HAProxy,而 HAProxy 负载均衡器会在集群主实例与只读副本之间对流量进行负载均衡。

4. 并发

如果有许多租户发起并发(Concurrent)查询,性能会下降。

Congestion Avoidance Algorithm Throttling Tenants

拥塞控制限流算法

因而 Cloudflare 使用 TCP Vegas 拥塞控制算法 来对租户限流。这个算法的工作原理是,首先采样每个租户的事务往返 Postgres 的响应时间(RTT),然后只要 RTT 不降级就持续调整连接池大小,因而在出现资源枯竭前就能实现限流。

5. 排队

Cloudflare 在 PgBouncer 层面使用队列对查询进行排队。查询在队列中的顺序取决于它们的历史资源使用情况,换句话说,需要更多资源的查询会排在队列的尾部。

Ordering Queries in Priority Queue

使用优先队列排序查询

Cloudflare 只在流量峰时刻启用优先队列以防资源饥饿。换言之在正常流量中,查询不会永远排在队尾。

这种方法改善了绝大多数查询的延迟(Latency),不过在流量峰时发起大开销查询的租户会观察到更高的延迟。

6. 高可用

Cloudflare 使用 Stolon 集群管控负责 Postgres 的高可用.

High Availability of Data Layer With Stolon

使用 Stolon 负责数据库高可用

Stolon 可用于搭建 Postgres 主从复制,并在出现问题时负责选举 Postgres 集群领导者(主库)并进行故障切换。

这里的每个数据库集群都会复制到两个区域,每个区域内有三个实例。

写请求会被路由到主要区域中的主库上,然后异步复制到次要区域,读请求会路由到次要区域中处理。

Cloudflare 会进行组件间连通性测试以便主动发现网络分区问题,也会进行混沌测试以优化系统韧性,还会配置冗余的网络交换机于路由器来避免网络分区。

当故障切换结束,主库实例重新上线时,他们会使用 pg_rewind 工具重放错过的写入变更,来让旧主库重新与集群同步。

Cloudflare 的 Postgres 主库实例与从库实例加起来超过 100 台。他们组合使用了 操作系统资源管理,排队理论,拥塞控制算法,甚至是 PostgreSQL 统计量来实现 PostgreSQL 的可伸缩性。


评价与讨论

这是一篇有价值的经验分享,主要介绍了如何使用 Pgbouncer 以解决 PostgreSQL 的可伸缩性(Scalability)问题。五千万 QPS + 20% 的互联网流量,听上去是不小的一个规模。尽管从 PostgreSQL 专家的角度看这里的实践确实写的有些朴素简陋,但是这篇文章确实抛出来了一个有意义的问题 —— PostgreSQL的 可伸缩性

PostgreSQL 的可伸缩性现状

PostgreSQL 在垂直伸缩和水平伸缩能力上享有盛誉。在读请求上,PostgreSQL 没有什么伸缩性问题 —— 因为读写互不阻塞,所以只读查询的吞吐量上限几乎是随投入的资源(CPU)线性增长的,无论是垂直增加 CPU/内存还是水平扩容拖从库,都可以通过加资源解决。

PostgreSQL 在写入上的伸缩性没有读上那么强,单机 WAL 写入/重放速度达到 100 MB/s ~ 300 MB/s 就会遇到软件瓶颈 —— 但对于常规生产 OLTP 负载这已经是一个很大的值了 —— 作为参考,探探这样一个两亿用户千万日活的应用,所有数据库写入的结构化数据率就在 120 MB/s 左右。PostgreSQL 社区也正在讨论通过 DIO/AIO 以及并行WAL重放的方式来进一步拓展此瓶颈。用户也可以考虑使用 Citus 或者其他分库分表中间件实现写入的伸缩扩容。

在容量上,PostgreSQL 的可伸缩性主要取决于磁盘,本身并没有瓶颈。在 NVMe SSD 单卡64TB的当下,配合压缩卡支持百TB级别的数据容量毫无问题,更大的容量也可以使用 RAID 或使用多个表空间的方式进行支持。社区曾经报告不少百TB量级的OLTP实例,也有零星 PB 级的实例。大实例的挑战主要是备份管理与空间维护上的,而不是性能上的。

在过去,PostgreSQL 可伸缩性比较为人诟病的一个问题,就是对海量连接的支持 (在 PostgreSQL 14 后得到显著改善)。PostgreSQL 和 Oracle 默认的模型一样都使用了多进程架构。这种设计有着更好的可靠性,但在面对海量高并发场景时,这种模型就有些拖后腿了。

互联网场景下数据库访问模式主要是海量短连接:一个查询过来就创建一条连接,执行完后就销毁连接 —— PHP 以前就是这么干的,所以和使用线程模型的搭档 MySQL 很配。但对于 PostgreSQL 而言,海量的后端进程与频繁的进程创建销毁会浪费大量的软硬件资源,因而在这种场景的性能表现上就些力不从心了。

连接池 —— 解决高并发问题

PostgreSQL 推荐默认使用的连接数量约为 CPU 核数的两倍,通常在几十 ~ 几百的范围内会比较合适。互联网场景下动辄以千/以万计的客户端连接如果直连 PostgreSQL,就会产生显著的额外负担。连接池便是为了解决这个问题而出现的 —— 可以说,连接池对于在互联网场景下使用 PostgreSQL 是一个必选项,能够起到化腐朽为神奇的效果。

请注意,PostgreSQL 并非不支持高吞吐,问题的关键在于并发连接的数量 —— 在《PG性能有多强》中,我们在 92 vCPU 的服务器上使用 约 96 条连接压测出 sysbench 点查吞吐量峰值 233 万。而在超出可用资源后,这一最大吞吐随着并发进一步加大而开始缓慢下降。

使用连接池有一些显著的好处:首先,数万条客户端连接,可以池化缓冲收敛为几条活跃 Server 连接(使用事务级连接池),极大减少了操作系统上的进程数量与开销,也避免了进程创建销毁的开销。第二点,并发争用的情况因为活跃连接数的减少而大大减小,进一步优化了性能。第三点,突然出现的负载峰值会在连接池上排队,而不是直接打爆数据库,降低了雪崩概率,从而提高了系统的稳定性。

性能与瓶颈

我在探探时有很多关于 PgBouncer 的最佳实践,我们有一套核心数据库集群,整个集群有着 50万 QPS,主库上的客户端连接数为两万,写入 TPS 约为 5 万。这样的负载如果直接打到 Postgres 上会立即打爆数据库。因此在应用与数据库之间,还有一个 PgBouncer 连接池中间件。所有两万条客户端连接经过连接池事务池化模式后,总共只需要 5 ~ 8 条活跃服务器连接就支撑起所有的请求,CPU 使用率约为 20%,这是一个非常巨大的性能改善。

PgBouncer 是一个轻量级连接池,可以部署在用户侧或者数据库侧。PgBouncer 本身因为使用了单进程模式,存在一个 QPS / TPS 瓶颈,约为 3 ~ 5 万。因此为了避免 PgBouncer 本身的单点问题与瓶颈,在核心主库上我们使用了 4 个幂等的 PgBouncer 实例,并通过 HAProxy 均匀分发流量给这四个 PgBouncer 连接池池化后,再到数据库主库上处理。但是对于绝大多数场景而言,单个 PgBouncer 进程的 3万 QPS 的处理能力已经是绰绰有余了。

管理灵活性

PgBouncer 的一个巨大优势是,它可以提供 User / Database / Instance 级别的查询响应时间指标(RT)。这是用于性能衡量的核心指标,对于早些年的 PostgreSQL 老版本,PgBouncer 中的统计值也是获取这类数据的唯一方式。尽管用户可以通过 pg_stat_statements 扩展获取查询组的 RT, PostgreSQL 14 以后也可以获取数据库级别的会话活跃时间来计算事务 RT,新出现的 eBPF 也可以完成这一点。但 PgBouncer 提供的性能监控数据对于数据库管理仍然是非常重要的参考依据。

PgBouncer 连接池不仅提供了性能上的改善,还为精细管理提供了抓手。例如在数据库在线不停机迁移中,如果在线流量完全通过连接池访问,那么你就可以通过简单修改 PgBouncer 配置文件的方式,将旧集群的读写流量丝滑重定向到新集群中,甚至都不需要业务方即时参与改配置重启服务。你也可以像上面 Cloudflare 的例子一样,在连接池修改 Database / User 的参数,实现限流的能力。如果某一个数据库租户表现不良,影响了整个共享集群,管理员可以在 PgBouncer 上轻松实现限流与阻断的能力。

其他替代品

PostgreSQL 生态中还有其他的一些连接池产品。与 PgBouncer 同期的 PGPool-II 也曾经是一个有力竞争者:它提供了更为强大的负载均衡/读写分离等能力,也能充分利用多核的能力,但是对 PostgreSQL 数据库本身有侵入性 —— 需要安装扩展才能用,而且曾经有比较显著的性能折损(30%)。所以在连接池大PK中,简单轻量的 PgBouncer 成为了胜利者,占据了PG连接池的主流生态位。

除了 PgBouncer 之外,新的 PostgreSQL 连接池项目也在不断出现,比如 Odyssey,pgcat,pgagroal,ZQPool 等。我非常期待能有一个完全兼容 PgBouncer 的高性能/更易用原位替代出现。

此外,许多编程语言标准库的数据库驱动里,都开始内置了连接池,加上 PostgreSQL 14 的改进让多个进程的开销减少。以及硬件性能的指数增长(现在都有 512 vCPU 的服务器了,内存也不是啥稀缺资源了)。所以有时候不用连接池,几千个连接直接干上去也是一个可行选项了。

我能用上 Cloudflare 的实践吗?

随着硬件性能的不断提升,软件架构的不断优化,管理最佳实践的逐渐普及 —— 高可用、高并发、高性能(可伸缩性)对于互联网公司来说属于老生常谈,基本不算什么新鲜技术了。

例如在当下,随便一个初级 DBA / 运维,只要使用 Pigsty 部署一套 PostgreSQL 集群都可以轻松做到这一点,包括 Cloudflare 提到的 Pgbouncer 连接池,以及高可用组件 Stolon 的上位替代 Patroni ,都已经做到开箱即用了。只要硬件达标,轻松处理好海量并发百万请求不是梦。

在本世纪初,一台 Apache 服务器只能处理很可怜的一两百个并发请求。最优秀的软件也很难处理上万的并发 —— 业界有个著名的 C10K 高并发 问题,谁要是能做到几千并发,那就是业界高手。但随着 Epoll 和 Nginx 在 2003/2004 年相继问世,“高并发” 不再是什么难题了 —— 随便一个小白只要学会配置 Nginx,就可以达到前几年大师们做梦都不敢想的程度 —— 瑞典马工《云厂商眼中的客户:又穷又闲又缺爱

这就跟现在随便哪个新手都可以拿 Nginx 实现以前用 httpd 的大师们想都不敢想的 Web 海量请求与高并发一样。PostgreSQL 的可伸缩性也随着 PgBouncer 的普及走入千家万户。

例如,在 Pigsty 中,默认为所有 PostgreSQL 1:1 部署了 PgBouncer 实例,使用事务池化模式,并纳入监控。而默认的 Primary 与 Replica 服务也是通过 PgBouncer 访问 Postgres 数据库的。用户不需要操心太多与 PgBouncer 有关的细节 —— 例如, PgBouncer 的数据库与用户是在通过剧本创建 Postgres 数据库/用户时自动维护的。一些常见的配置注意事项和坑也在预置配置模板中进行了规避,力求做到开箱即用。

当然,对于非互联网场景的应用,PgBouncer 也并非必须品。而且默认的 Transaction Pooling 虽然在性能上非常优秀,但也是以牺牲了一些会话级功能为代价的。所以您也完全可以配置 Primary / Replica 服务直连 Postgres,绕过 PgBouncer;或者使用兼容性最好的 Session Pooling 模式。

总的来说,PgBouncer 确实是一个非常实用的 PostgreSQL 生态工具。如果您的系统对于 PostgreSQL 客户端并发连接数有着较高要求,那么在测试性能时请务必试一试这款中间件。

原文:Cloudflare是如何用15个PG集群支持55M QPS的 |

展望 PostgreSQL 的2024

本文是 PostgreSQL 核心组成员 Jonathan Katz 对 2024 年 PostgreSQL 项目的未来展望,并回顾过去几年 PostgreSQL 所取得的进展。

作者:Jonathan Kats,Amazon RDS 首席产品经理兼技术主管, PostgreSQL 全球开发组核心成员与主要贡献者。博客:https://jkatz05.com/。

译者:冯若航 / Vonng。磐吉云数创始人 / CEO,PostgreSQL 专家与布道师,开源 RDS PG —— Pigsty 作者。博客:https://vonng.com

点击“查看原文”查看英文原文:https://jkatz05.com/post/postgres/postgresql-2024/

在我经常听到的问题中,有一个尤为深刻:“PostgreSQL 将走向何方?” —— 这也是我经常问自己的一个问题。这个问题不仅仅局限在数据库内核引擎的技术层面,而关乎整个社区的方方面面 —— 包括相关的开源项目、活动和社区发展。PostgreSQL 已经广受欢迎,并且已经是第四次被 DB Engine评为“年度数据库。尽管已取得显著成功,我们依然需要不时地后退一步,从更宏观的角度思考 PostgreSQL 的未来。虽然这种思考不会立即带来显著的变化,但它对于社区正在进行的工作提供了重要的背景板。

新年是思考 “PostgreSQL的未来” 这一问题的绝佳时机,我对2024年的PostgreSQL发展方向也有一些思考,这里是我的一些想法:这并不是一个路线图,而是我个人对 PostgreSQL 发展方向的一些想法。


PostgreSQL功能开发

PGCon 2023 开发者会议上,我提出了一个题为“PostgreSQL 用户面临的重大挑战是什么?”的话题。这个话题旨在探讨用户的常见需求和数据库工作负载的发展趋势,以此来判断我们是否正在朝着正确的方向发展 PostgreSQL。通过多次交谈和观察,我提出了三个主要的特性类目:

  • 可用性
  • 性能
  • 面向开发者的特性

这些特性组将成为 2024 年,甚至更长时间段里的工作重点。接下来,我将对每个特性类目进行更深入的探讨。

可用性

对于PostgreSQL现有用户和潜在用户来说,提高可用性是最迫切的需求。这个需求不仅仅是排在第一位,而且毫不夸张地讲,也同时能排在第二位和第三位。虽然重启 PostgreSQL 通常可以迅速完成,但在某些极端情况下,这个过程可能耗时过长。此外,长时间的写入阻塞,例如某些锁操作,也可被视作一种“停机时间”。

大部分 PostgreSQL 用户对现有的可用性水平已感满意,但有些工作负载对可用性的要求极为严格。为了更好地满足这些要求,我们需要进行额外的开发工作。这篇文章或这一小节就聚焦于这一点:通过改进使 PostgreSQL 适用于更多有严苛可用性需求的环境。

逻辑复制是如何助益于双主,蓝绿部署,零停机升级,以及其他工作流的

对于现有的 PostgreSQL 用户,以及那些计划迁移至 PostgreSQL 的用户来说,提升可用性是最重要的需求。这通常指的是高可用——即在计划内的更新或计划外的中断期间,数据库能够持续进行读写操作的能力。PostgreSQL 已经提供了许多支持高可用的特性,如流复制。然而为了实现最高水平的可用性,通常还需要借助额外的服务或诸如 Patroni 这样的工具。

我聊过许多用户,在绝大多数情况下,他们对 PostgreSQL 提供的可用性是满意的。但我也发现了一个新趋势:现在有一些负载对可用性的要求越来越高,15-30 秒的离线窗口已不够了。这包括计划内的中断(如小版本升级、大版本升级),以及计划外的中断。一些用户表示,他们的系统最多只能承受1秒的不可用时间。起初我对这种要求持怀疑态度,但了解到这些工作负载的具体用途后,我认为1秒确实是一个合理的需求。

在持续提高 PostgreSQL 可用性方面,逻辑复制 是一个关键特性。逻辑复制能够实时将 PostgreSQL 数据库中的变更流式传输到任何支持 PostgreSQL 逻辑复制协议的系统中。PostgreSQL 中的逻辑复制已经存在了一段时间,而最近的版本在可用性方面带来了显著的改进,包括功能和性能上的新特性。

逻辑复制在 PostgreSQL 的大版本升级过程中扮演着关键角色,与传统的物理(或二进制)复制相比,它的一大优势在于能够实现跨版本的数据流转。举例来说,通过逻辑复制,我们可以轻松地将 PostgreSQL 15 的数据变更实时传输至 PostgreSQL 16,从而大幅缩减升级过程中的停机时间。这种方法已在 Instacart 的零停机大版本升级中得到成功应用。然而,PostgreSQL 在支持此类用例和其他高可用性场景方面仍有待提升。未来的发展预计将进一步优化支持蓝绿部署的功能,以实现更加无缝的数据迁移和应用升级。

除了在大版本升级中的用例,逻辑复制本身也是构建高可用系统的重要手段。"多主复制“就是其中的一个典型应用,它允许多个数据库实例同时接受写入操作,并在它们之间同步数据变更。这种模式尤其适用于对停机时间敏感的系统(例如:不接受1秒以上的不可用时间),其设计目标是在任何写入数据库出现问题时,应用能迅速切换到另一可用的写入数据库,而不必等待它被提升为新主库。构建与管理这样的双活系统是极度复杂的:它会影响到应用设计,并需要用户提供对写入冲突进行管理的策略,而且为了确保数据完整性(比如:冲突风暴),需要有仔细设计的容错监控系统 —— (比如,一个实例如果几个小时都无法复制它的变更会发生什么?)

大版本升级和双活复制案例为我们指明了改善 PostgreSQL 逻辑复制的方向。Amit Kapila 是众多逻辑复制功能开发的领导者。今年,他和我共同在一场会议上发表了题为“PostgreSQL 中的多主复制之旅”的演讲(并提供了视频版本),深入探讨了为何针对这些用例的解决方案至关重要、PostgreSQL 在逻辑复制方面取得的成就,以及为更好支持这些场景所需做的工作。好消息是从 PostgreSQL 16 版本起,我们已经有了大部分基础模块来支持双活复制、蓝绿部署和零停机大版本升级。虽然这些功能可能没有全部集成在内核中,但某些扩展(比如我参与开发的pgactive)已提供了这些能力。

在 2024 年,有多项努力旨在帮助缩小这些功能差距。对于 PostgreSQL 17 来说(惯例免责声明:这些特性可能不会发布),有一个重点是确保逻辑复制能够与关键工作流(如pg_upgrade高可用系统)协同工作,支持更多类型的数据变更(如序列/Sequence)的复制,扩展对更多命令(如 DDL)的支持,提高性能,以及增加简化逻辑复制管理的特性(如节点同步/再同步)。

这些努力能让 PostgreSQL 适用于更多种类的负载,特别是那些有着极致严苛可用性要求的场景,并简化用户在生产环境中滚动发布新变更的方式。尽管改进逻辑复制功能的道路仍然漫长,但 2024 年无疑将为 PostgreSQL 带来更多强大的功能特性,帮助用户在关键环境中更加高效地运行 PostgreSQL。

减少锁定

另一个有关可用性的领域是模式维护操作(即DDL语句)。例如,ALTER TABLE的大部分形式会对表施加 ACCESS EXCLUSIVE 锁,从而阻止对该表的所有并发访问。对于许多用户来说这等同于不可用,即使这只是数据的一个子集。PostgreSQL 缺乏对非阻塞/在线模式维护操作的完整支持,随着其他关系数据库也开始支持这些功能,这方面的不足开始逐渐凸显。

目前虽有多种工具和扩展支持非阻塞模式更新,但如果 PostgreSQL 能原生支持更广泛的非阻塞模式变更,那肯定更方便,而且性能也会更好。从设计上来看,我们已有了开发此功能的基础,但还需要一些时间来实现。尽管我不确定是否有正在进行中的具体实现,但我相信在2024年我们应该在这方面取得更多进展:让用户能够在不阻塞写入的情况下执行大部分(或全部)DDL 命令

性能

性能是一个不断持续演进的特性 —— 我们总是会追求更快的速度。好消息是,PostgreSQL 在垂直扩展能力上享有盛誉 —— 当你为单个实例提供更多硬件资源时,PostgreSQL 也能扩展自如。虽然在某些场景下,水平扩展读写操作是有意义的。但我们还是要确保 PostgreSQL 能够随着计算和内存资源的增加而持续扩展。

举个更具体的例子:考虑到 AWS EC2 实例中有着高达 448 vCPU / 24TB 内存 的选配项 —— PostgreSQL 能否在单个实例上充分利用这些资源呢?我们可以根据 PostgreSQL 用户现在与未来可能使用的硬件配置,设定一个性能提升的目标,并持续提升 PostgreSQL 的整体表现。

在 2024 年,已经有多项工作致力于继续垂直扩展 PostgreSQL。其中最大的努力之一,也是一个持续多年的项目,就是在 PostgreSQL 中支持 DirectIO(DIO)与 Asynchronous IO(AIO)。至于细节我就留给 Andres Freund 在PGConf.EU上关于在 PostgreSQL 中添加 AIO 的现状的PPT来讲了。看起来在 2024 年,我们将离完全支持 AIO 更进一步。

另一项让我感兴趣的工作是并行恢复。有着大量写入负载的 PostgreSQL 用户往往会推迟 Checkpoint 以减少 I/O 负载。对于忙碌的系统而言,如果 PostgreSQL 在执行 Checkpoint 的相当一段时间后才崩溃,那么当 PostgreSQL 重新启动时,它会进入 “崩溃恢复 “状态:它会重新执行自上次 Checkpoint 以来的所有变更,以便达到一致的状态 —— 在崩溃恢复期间,PostgreSQL 不能读也不能写,这意味着它不可用。这对繁忙的核心系统来说是个问题:虽然 PostgreSQL 可以接受并发写入,但它重放变更时只能使用单个进程。如果一个繁忙系统崩溃于上个检查点后的一小时,那么系统会需要离线追赶几个小时,才能达到一致的状态点重新上线!

克服这一局限性的方法之一是支持”并行恢复",或者说能够并行重放WAL变更。在PGCon 2023上,Koichi Suzuki做了一个 关于PostgreSQL如何支持并行恢复 的详细介绍。这不仅适用于崩溃恢复,也适用于任何 PostgreSQL WAL 重放操作(例如:PITR 时间点恢复)。虽然这是一个极具挑战性的问题,但支持并行恢复有助于 PostgreSQL 继续垂直扩展,因为用户可以进一步针对重度写入负载进行优化,也能缓解 “从故障中恢复上线所需的延时超出承受范围” 的风险。

这并不是一份关于性能特性的详细清单。在 PostgreSQL 服务器性能上还有很多工作要做,包括索引优化、改进锁机制、充分利用硬件加速等。此外,客户端(如驱动程序和连接池)上的工作也能为应用与 PostgreSQL 的交互带来额外的性能提升。展望 2024 年,看看社区正在进行的工作,我相信 PostgreSQL 在各个领域上的性能都会有整体性提升。

开发者特性

我认为 “开发者特性 “(developer features)是一个相当宽泛的类目,核心在于如何让用户围绕 PostgreSQL 来架构 & 构建应用。这里包括:SQL语法、函数、存储过程语言支持,以及帮助用户从其他数据库系统迁移到 PostgreSQL 的功能。一个具体的创新例子是在 PostgreSQL 14 中引入的 multirange 数据类型,它允许用户将一些不连续的 范围(Range) 聚合在一起,这个特性非常实用,我个人在实现一个调度功能时,用它将数百行PL/pgSQL代码减少到三行。开发者特性也关乎 PostgreSQL 如何支持新出现的工作负载:例如JSON 或向量

值得一提的是,许多开发者特性创新主要出现在**扩展(Extension)**上,而这正是 PostgreSQL 可扩展模型的优势所在。然而就数据库服务器本身而言,PostgreSQL 在某些开发者特性上的发布速度相比过去有所落后。例如,尽管PostgreSQL是第一个将JSON作为可查询数据类型的关系数据库,但它在实现 SQL/JSON 标准锁定义的语法与特性上已经开始变得迟缓。PostreSQL 16 发布了 SQL/JSON 中的一些语法特性,2024 年也会有更多的努力用在实现 SQL/JSON 标准上。

话既然说到这儿了,我们应当着力于 PostgreSQL 中那些无法通过扩展插件实现的开发者特性,比如 SQL标准特性。我的建议是集中精力关注那些其他数据库已经具备的功能,比如进一步实现 SQL/JSON 标准(例如: JSON_TABLE)、系统层面的版本化表(对于审计、闪回,与在特定时间点进行的时态查询非常有用),以及对模块的支持(对于“打包”存储过程来说尤其重要)。

此外,考虑到之前讨论的可用性和性能问题,我们应继续努力简化用户从其他数据库迁移到 PostgreSQL 的过程。在我的日常工作中,我有机会了解了大量与数据库迁移相关的内容:从商业数据库到 PostgreSQL 的迁移策略。当我们增强 PostgreSQL 功能的同时,也有许多机会可以简化迁移流程。包括引入其他数据库中现有的功能(例如全局临时表、全局分区索引、自治事务),并在 PL/pgSQL 中增加更多功能与性能优化(如批量数据处理函数、模式变量缓存函数元数据)。所有这些都将改善 PostgreSQL 开发者的体验,并让其他关系数据库的用户更容易采纳 PostgreSQL。

最后我们需要了解,如何才能持续不断地支持来自 AI/ML 数据的新兴负载,特别是向量存储与检索。在2023年的PGCon会议上,尽管人们希望在 PostgreSQL 本身中看到原生的向量支持,但大家一致认为,在 pgvector这样的扩展中实现这类功能可以抢占先机,更快地支持这些工作负载(这一策略似乎已经奏效在向量数据上性能表现优异)。有鉴于向量负载的诸多特征,我们可以在PostgreSQL中添加一些额外的支持,以便进一步支持它们:其中包括对处理 活动查询路径中的TOAST数据的规划器进行优化,并探索如何更好地支持带有大量过滤条件和 ORDER BY 子句的查询。

我确信在 2024 年,PostgreSQL 可以在这些领域取得显著进步。我们看到在 PostgreSQL 的扩展生态中,有大量的新能力正在涌现;但即便如此,我们还是可以继续直接为 PostgreSQL 添加新特性,让它更易于构建应用。

安全性如何?

我想快速过一下 PostgreSQL 的安全特性。众所周知在安全敏感型场景中,PostgreSQL 有着极佳的声誉。但总会有许多能改进的地方。在过去几年中,PostgreSQL社区对引入透明数据加密(TDE)的原生支持表现出许多兴趣与关注。然而还有许多其他地方可以搞搞创新,比如支持其他的身份验证方式/机制(主要需求是OIDC),或是探索联邦授权模式的可能性,使PostgreSQL能够继承其他系统的权限设置。尽管这些特性在当下都颇有挑战,我建议先在 “Per-Database” 层面上支持 TDE。这里我不想过多展开,因为已经有在 PostgreSQL 中满足这些特性需求的方法了,但我们还是应该不懈努力,争取实现完整的原生支持。

让我们再来看看PostgreSQL能在2024年里发力的其他方向。


扩展

PostgreSQL 的设计是高度可扩展的。您可以为PostgreSQL添加新功能,而无需分叉项目。包括新的数据类型、索引方法、与其他数据库系统协同工作的方法、更易于管理PostgreSQL特性的实用工具、额外的编程语言支持,甚至编写自己的扩展插件。人们已经围绕一些特定的 PostgreSQL 扩展(如PostGIS)建立了开源社区和公司;PostgreSQL 单一数据库便能支持不同类型的工作负载(地理空间、时间序列、数据分析、人工智能),正是扩展让这件事变得可能。数千个可用的PostgreSQL扩展成为了PostgreSQL的 “力量倍增器” —— 它一方面让用户能够快速的为数据库新增功能,另一方面也极大推动了 PostgreSQL 的普及与采用。

然而这也产生了一个副作用,即“扩展蔓延”现象。用户如何去选择合适的扩展?扩展的支持程度如何?如何判断某个扩展是否有持续积极的维护?如何为扩展做出自己的贡献?甚至“在哪里可以下载扩展”也成为了一个大问题。postgresql.org 提供了一个不完整的扩展列表,社区也维护了一些扩展包,也有其他几个可供选择的 PostgreSQL 扩展仓库(例如 PGXNdbdevTrunk)和 pgxman 可供选择。

PostgreSQL社区的一个优势是去中心化,广泛散布于世界各处。但我们可以做得更好,帮助用户在复杂的数据管理中做出明智的选择。我认为2024年是一个机遇,我们可以投入更多资源来整合与展示 PostgreSQL 扩展,帮助用户理解什么时候可以使用哪些扩展,并了解扩展们的开发成熟度,并同样为扩展开发者提供更好的管理支持与维护资源。


社区建设

在谈论2024年社区建设的构想时,我深感自加入 PostgreSQL 贡献者社区以来,我们已取得显著进步。社区在认可各类贡献者方面表现突出(尽管仍有提升空间)—— 不仅限于代码贡献,还包括项目的各个方面。展望未来,我想着重强调三个关键领域:导师制、多元化、公平与包容(DEI)以及透明度,这些都对项目的全方位发展至关重要。

PGCon 2023开发者会议上,Melanie Plageman 就新贡献者的体验和挑战进行了深入分析。她提到了诸多挑战,如初学者需要花费大量时间来掌握基本知识,包括使用代码库和邮件列表进行交流,以及将补丁提交到可审查状态所需的努力。她还指出,提供建设性指导意见(从审查补丁开始)可能比编写代码本身更具挑战性,同时也讨论了如何有效地提供反馈。

关于提供反馈,我想引用罗伯特-哈斯(Robert Haas)的一篇优秀博文,其中他特别强调了在批评时同时给予表扬的重要性——这种方法可以产生显著的效果,并提醒我们即使在批评时也应保持支持态度。

回到 Melanie 的观点,我们应该在整个社区更好地实施导师计划。就我个人而言,我认为我在宣传项目方面做得不够好,包括帮助更多人为网络基础设施发布流程 做出贡献。这并不是说 PostgreSQL 缺乏优秀的导师,而是我们可以在帮助人们开始贡献和找到导师方面做得更好。

2024年将是建立更完善导师制度的起点。我们希望在5月于温哥华举行的 PGConf.dev 2024 上试验一些新想法。

PGConf.dev 出现前,从2007年到2023年,PGCon一直是PostgreSQL贡献者们集结并讨论即将开始的开发周期和关键项目的重要活动。PGCon 一直由 Dan Langille 负责组织。经过多年的辛勤工作,他决定将组织职责扩展至一个团队,并协助成立了 PGConf.dev

PGConf.dev 是专为那些希望为 PostgreSQL 做贡献的人士举办的会议。会议内容覆盖了 PostgreSQL 的开发工作(包括内核及所有相关的开源项目,如扩展和驱动程序)、社区建设以及开源意见领袖等主题。PGConf.dev 的一大特色是导师制,并计划举办关于如何为 PostgreSQL 贡献的研讨会。如果你正寻找为 PostgreSQL 贡献的机会,我强烈建议你考虑参加本活动或提交演讲提案

接下来是 PostgreSQL 社区如何在多元化、公平与包容性(DEI)上进步的话题。我强烈建议观看凯伦·杰克斯莱蒂西亚·阿夫罗特在 2023 年 PGConf.eu 上的演讲: 在肯的 Mojo Dojo Casa House 里尝试成为芭比:因为这是一场关于如何继续让 PostgreSQL 社区变得更加包容的深刻演讲。社区在这方面取得了进步(凯伦和莱蒂西亚指出了有助于此的一些举措),但我们还能做得更好,我们应该积极主动地处理反馈,以确保为 PostgreSQL 做出贡献是一种受欢迎的体验。我们所有人都可以采取行动,例如,在发生(诸如性别歧视的)不当行为时及时指出,并指出行为不当的原因。

最后是透明度问题。在开源领域这可能听起来有些奇怪,毕竟它本身就是开放的。但有不少治理问题并不会在公开场合讨论,了解决策制定的流程会很有帮助。PostgreSQL 行为守则委员会 提供了一个优秀的例子:一个社区如何就需要敏感处理的问题保持透明度。该委员会每年都会发布一份报告(这是 2022 年的报告),包括案例的总体描述和整体统计数据。我们可以在许多 PostgreSQL 团队中复制这种做法 —— 这些团队参与的任务可能由于其敏感性需要保密。


结论:本来这篇文章应该更短

最初,我以为这篇文章会是一篇简短的帖子,几小时内就能完成。但几天后,我意识到情况并非如此……

老实说,PostgreSQL目前处于一个非常好的状态。它依然备受欢迎,其可靠性、鲁棒性和性能的声誉稳如磐石。然而我们仍可以做得更好,令人感到振奋的是,社区正在积极地在各个方向上努力改善。

虽然上面这些是 PostgreSQL 在 2024 年及以后可以做的事情,但 PostgreSQL 走到今天已经做成了很多很多的事。提出 “PostgreSQL何去何从” 这样的问题,实际上为我们提供了一个机会:回顾过去几年 PostgreSQL 所取得的进展,并展望未来!

FerretDB:假扮成MongoDB的PG

MongoDB 曾经是一项令人惊叹的技术,让开发者能够抛开关系型数据库的“模式束缚”,快速构建应用程序。然而随着时间推移,MongoDB 放弃了它的开源本质,这使得许多开源项目和早期商业项目无法使用它。

大多数 MongoDB 用户其实并不需要 MongoDB 提供的高级功能,但他们确实需要一个易于使用的开源文档数据库解决方案。PostgreSQL 的 JSON 功能支持已经足够完善了:二进制存储 JSONB,GIN 任意字段索引 ,各种 JSON 处理函数,JSON PATH 和 JSON Schema,PG早已是一个功能完备,性能强大的文档数据库了。但是提供替代的功能,和直接仿真还是不一样的。

为了填补这个空白,FerretDB 应运而生,旨在提供一个真正开源MongoDB 替代。这是一个非常有趣的项目,之前的名字叫 “MangoDB”,因为有碰瓷 “MongoDB” 的嫌疑(芒果DB vs 蒙古DB),所以在 1.0 版本改成了现在的名字 FerretDB。FerretDB 可以为使用 MongoDB 驱动的应用提供一个丝滑迁移到 PostgreSQL 的过渡方案。

它的功能就是让 PostgreSQL 假扮成 MongoDB。它是一个为 PG 提供 MongoDB Wire Protocol 支持的协议转换中间件/Proxy。上次做过这种事的插件是 AWS 的 Babelfish,让 PostgreSQL 兼容 SQL Service 的线缆协议假扮成 Microsoft SQL Server。

FerretDB 作为一个选装组件,对丰富 PostgreSQL 生态大有裨益。Pigsty 在 1.x 中就提供了基于 Docker 的 FerretDB 模板,在 v2.3 中更是提供了原生部署支持。目前,Pigsty 社区已经与 FerretDB 社区成为了合作伙伴,后续将进行深度的合作与适配支持。

本文简单介绍了 FerretDB 的安装、部署与使用。


配置

在部署 Mongo (FerretDB) 集群前,你需要先在配置清单中使用相关参数定义好它。下面的例子将默认的单节点 pg-meta 集群的 meta 数据库作为 FerretDB 的底层存储:

ferret:
  hosts: { 10.10.10.10: { mongo_seq: 1 } }
  vars:
    mongo_cluster: ferret
    mongo_pgurl: 'postgres://dbuser_meta:DBUser.Meta@10.10.10.10:5432/meta'

这里 mongo_clustermongo_seq 属于不可或缺的身份参数,对于 FerretDB 来说,还有一个必须提供的参数是 mongo_pgurl,指定了底层 PG 的位置。

您可以使用 服务 来接入高可用的 PostgreSQL 集群,并部署多个 FerretDB 实例副本并绑定 L2 VIP 以实现 FerretDB 层本身的高可用。

ferret-ha:
  hosts:
    10.10.10.45: { mongo_seq: 1 }
    10.10.10.46: { mongo_seq: 2 }
    10.10.10.47: { mongo_seq: 3 }
  vars:
    mongo_cluster: ferret
    mongo_pgurl: 'postgres://test:test@10.10.10.3:5436/test'
    vip_enabled: true
    vip_vrid: 128
    vip_address: 10.10.10.99
    vip_interface: eth1

管理

创建Mongo集群

在配置清单中定义好MONGO集群后,您可以使用以下命令完成安装。

./mongo.yml -l ferret   # 在 ferret 分组上安装“MongoDB/FerretDB”

因为 FerretDB 使用了 PostgreSQL 作为底层存储,所以重复运行此剧本通常并无大碍。

移除Mongo集群

要移除 Mongo/FerretDB 集群,运行 mongo.yml剧本的子任务:mongo_purge,并使用 mongo_purge 命令行参数即可:

./mongo.yml -e mongo_purge=true -t mongo_purge

安装MongoSH

您可以使用 MongoSH 作为客户端工具访问 FerretDB 集群

cat > /etc/yum.repos.d/mongo.repo <<EOF
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
EOF
yum install -y mongodb-mongosh

当然,您也可以直接安装 mongosh 的 RPM 包:

rpm -ivh https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el7/RPMS/mongodb-mongosh-1.9.1.x86_64.rpm

连接到FerretDB

你可以使用 MongoDB 连接串,用任何语言的 MongoDB 驱动访问 FerretDB,这里以上面安装的 mongosh 命令行工具为例:

mongosh 'mongodb://dbuser_meta:DBUser.Meta@10.10.10.10:27017?authMechanism=PLAIN'mongosh 'mongodb://test:test@10.10.10.11:27017/test?authMechanism=PLAIN'

Pigsty 管理的 PostgreSQL 集群默认使用 scram-sha-256 作为默认的认证方式,因此,您必须使用 PLAIN 认证方式连接至 FerretDB。参阅 FerretDB:认证[17] 获取详细信息。

你也可以使用其他 PostgreSQL 用户来访问 FerretDB,只要在连接串中指定即可:

mongosh 'mongodb://dbuser_dba:DBUser.DBA@10.10.10.10:27017?authMechanism=PLAIN'

快速上手

你可以连接到 FerretDB 并假装它是一个 MongoDB 集群。

$ mongosh 'mongodb://dbuser_meta:DBUser.Meta@10.10.10.10:27017?authMechanism=PLAIN'

MongoDB 的命令会被翻译为SQL命令,在底下的 PostgreSQL 中执行:

use test                            # CREATE SCHEMA test;
db.dropDatabase()                   # DROP SCHEMA test;
db.createCollection('posts')        # CREATE TABLE posts(_data JSONB,...)
db.posts.insert({                   # INSERT INTO posts VALUES(...);
    title: 'Post One',body: 'Body of post one',category: 'News',tags: ['news', 'events'],
    user: {name: 'John Doe',status: 'author'},date: Date()}
)
db.posts.find().limit(2).pretty()   # SELECT * FROM posts LIMIT 2;
db.posts.createIndex({ title: 1 })  # CREATE INDEX ON posts(_data->>'title');

如果你不是很熟悉 MongoDB,这里有一个快速上手教程,同样适用于 FerretDB: Perform CRUD Operations with MongoDB Shell[18]

如果你希望生成一些样例负载,可以使用 mongosh 执行以下的简易测试剧本:

cat > benchmark.js <<'EOF'
const coll = "testColl";
const numDocs = 10000;

for (let i = 0; i < numDocs; i++) {  // insert
  db.getCollection(coll).insert({ num: i, name: "MongoDB Benchmark Test" });
}

for (let i = 0; i < numDocs; i++) {  // select
  db.getCollection(coll).find({ num: i });
}

for (let i = 0; i < numDocs; i++) {  // update
  db.getCollection(coll).update({ num: i }, { $set: { name: "Updated" } });
}

for (let i = 0; i < numDocs; i++) {  // delete
  db.getCollection(coll).deleteOne({ num: i });
}
EOF

mongosh 'mongodb://dbuser_meta:DBUser.Meta@10.10.10.10:27017?authMechanism=PLAIN' benchmark.js

你可以查阅 FerretDB 支持的 MongoDB命令,同时还有一些已知的区别,对于基本的使用来说,通常不是什么大问题。

  1. FerretDB uses the same protocol error names and codes, but the exact error messages may be different in some cases.
  2. FerretDB does not support NUL (\0) characters in strings.
  3. FerretDB does not support nested arrays.
  4. FerretDB converts -0 (negative zero) to 0 (positive zero).
  5. Document restrictions:
    • document keys must not contain . sign;
    • document keys must not start with $ sign;
    • document fields of double type must not contain Infinity, -Infinity, or NaN values.
  6. When insert command is called, insert documents must not have duplicate keys.
  7. Update command restrictions:
    • update operations producing Infinity, -Infinity, or NaN are not supported.
  8. Database and collection names restrictions:
    • name cannot start with the reserved prefix _ferretdb_;
    • database name must not include non-latin letters;
    • collection name must be valid UTF-8 characters;
  9. FerretDB offers the same validation rules for the scale parameter in both the collStats and dbStats commands. If an invalid scale value is provided in the dbStats command, the same error codes will be triggered as with the collStats command.

剧本

Pigsty 提供了一个内置的剧本: mongo.yml,用于在节点上安装 FerretDB 集群。

mongo.yml

该剧本由以下子任务组成:

  • mongo_check :检查 mongo 身份参数•mongo_dbsu :创建操作系统用户 mongod•mongo_install :安装 mongo/ferretdb RPM包•mongo_purge :清理现有 mongo/ferretdb 集群(默认不执行)•mongo_config :配置 mongo/ferretdb
  • mongo_cert :签发 mongo/ferretdb SSL证书
  • mongo_launch :启动 mongo/ferretdb 服务•mongo_register:将 mongo/ferretdb 注册到 Prometheus 监控中

监控

MONGO 模块提供了一个简单的监控面板:Mongo Overview

Mongo Overview

Mongo Overview: Mongo/FerretDB 集群概览

这个监控面板提供了关于 FerretDB 的基本监控指标,因为 FerretDB 底层使用了 PostgreSQL,所以更多的监控指标,还请参考 PostgreSQL 本身的监控。


参数

MONGO[24] 模块中提供了9个相关的配置参数,如下表所示:

参数 类型 级别 注释
mongo_seq int I mongo 实例号,必选身份参数
mongo_cluster string C mongo 集群名,必选身份参数
mongo_pgurl pgurl C/I mongo/ferretdb 底层使用的 PGURL 连接串,必选
mongo_ssl_enabled bool C mongo/ferretdb 是否启用SSL?默认为 false
mongo_listen ip C mongo 监听地址,默认留控则监听所有地址
mongo_port port C mongo 服务端口,默认使用 27017
mongo_ssl_port port C mongo TLS 监听端口,默认使用 27018
mongo_exporter_port port C mongo exporter 端口,默认使用 9216
mongo_extra_vars string C MONGO 服务器额外环境变量,默认为空白字符串

向量是新的 JSON

向量是新的JSON”,这本身就是一种很有趣的说法。因为向量(Vector)是一种已经被深入研究过的数学结构,而 JSON 是一种数据交换格式。然而,在数据存储和检索的世界中,这两种数据表示方式都已经成为了各自领域的通用语言,成为(或即将成为)现代应用开发中必不可少的要素。如果按当下的趋势发展,向量将会像 JSON 一样,成为构建应用时的关键要素

生成型AI 引发的热潮促使开发者寻找一种简便的方法来存储与查询这些系统的输出。出于很多因素,PostgreSQL 成为了最自然的选择。但即使是生成型AI 炒翻天也无法改变这一事实:向量并不是一种新的数据模式,它作为一种数学概念已经存在数百年了,而机器学习领域也对其已有半个世纪多的研究。向量的基础数据结构 —— 数组,几乎在所有初级导论性质的计算机科学课程中都会讲授。连 PostgreSQL 对向量运算的支持也已经有20多年的历史了!

高中数学知识:向量的余弦距离与相似度

那有什么东西是新的呢?其实是 AI/ML 算法的 易用性(Accessibility),以及如何将一些“真实世界”的结构(文本、图像、音频、视频)用向量的形式表示,并将其存储起来,以供应用实现一些有用的功能。有些人可能会说,把这些AI系统的输出(也就是所谓的“嵌入 Embedding”)放进数据存储系统中并不是什么新把戏。所以这里我们得再次强调,真正的新模式是 易用性:几乎所有应用都可以用这种近乎实时的方式查询并返回这些数据(文字图片音视频的向量表示)。

不过,这些与 PostgreSQL 有什么关系?那关系可大了!高效存储检索向量 —— 这种普适泛用数据类型,可以极大地简化应用程序开发,让相关联的数据都存放在同一个地方,并让人们继续使用现有的工具链。我们在十多年前的 JSON 上看到了这一点,现在我们在向量上也看到了这一点。

要理解为什么向量是新的 JSON,让我们回顾一下 JSON —— 互联网通信的事实标准,当 JSON 崭露头角时发生了什么?


JSON 简史:PostgreSQL 实现

在 “JSON崛起” 期间,我主要还是一名应用开发者。我正在构建的系统,要么是将 JSON 数据发送到前端,使其可以完成某种操作(例如渲染一个可更新的组件),要么是与返回 JSON 格式数据的“现代”API交互。JSON 的好处在于其简单性(很容易阅读和操作),作为一种数据交换格式具有很强的表达力。JSON 确实简化了系统间的通信,无论是从开发还是运维的角度。但我是希望在JSON中看到一些我喜欢的东西 —— 在数据库这一侧,我是使用模式(Schemas)的坚定支持者。

虽然 JSON 最初是作为一种交换格式而存在的,但人们确实会问 “为什么我不能直接存储和查询这玩意?” 这个问题引出了一种专门的数据存储系统 —— 可以用来存储和查询 JSON 文档。我确实试过好几种不同的 专用 JSON 存储系统,来解决一个特定场景下的问题,但我并不确定我是否想把他们引入到自己的应用技术栈中 —— 出于性能与可维护性的原因 (我不会说具体是哪些,因为十多年过去,时过境迁了)。这就引出了一个问题 —— 能否在PostgreSQL中存储 JSON 数据?

PostgreSQL JSON 特性矩阵

我记得当年去参加 PostgreSQL 活动时的急切心情 —— 等待 PostgreSQL 对原生 JSON 存储检索支持的更新。我记得当 PostgreSQL 9.2 增加了基于文本的 JSON 类型支持时自己是多么的激动开心。PostgreSQL 对 JSON 最开始的支持是对所存储 JSON 内容的合法性校验,以及一些用于提取 JSON 文档数据的函数与运算符。那时候并没有原生的索引支持,但如果你需要根据文档中的某个 Key 进行频繁查询,还是可以使用 表达式索引 功能来为你感兴趣的 Key 添加索引。

PostgreSQL 对 JSON 的初步支持帮助我解决了一些问题,具体来说有:对数据库中几个表的状态做快照,以及记录我与之交互的 API 的输出。最初的基于文本的 JSON 数据类型在检索能力上乏善可陈:你确实可以构建表达式索引来根据 JSON 文档中的特定 Key 来走索引,但实践上我还是会把那个 Key 单独抽取出来放在与 JSON 相邻的单独列中。

这里的关键在于:PG 对 JSON 的初步支持以 “JSON数据库”的标准来看还是很有限的。没错,我们现在可以存储 JSON,也拥有了一些有限的查询能力,但要和专用 JSON 数据库拼功能,显然还需要更多的工作。不过对于许多这样的用例,PostgreSQL仍然已经是足够好了:只要能和现有的应用基础设施一起使用,开发者还是愿意在某种程度上接受这些局限性的。PostgreSQL 也是第一个提供 JSON 支持的关系型数据库,带了一波节奏,最终直接导致 JSON 进入到 SQL 标准中。

俄罗斯的 PostgreSQL 与 Oleg 对 PG JSON 特性居功至伟

紧接着 PostgreSQL 作为 “JSON数据库” 的可行性,在 PostgreSQL 9.4 发布后出现质变:这个版本新增了 JSONB 类型,这是 JSON 数据类型的二进制表示,而且可以使用 GIN 索引来索引 JSON 文档中的任意数据。这让 PostgreSQL 能在性能上与专用 JSON数据库旗鼓相当,同时还能保留有关系数据库的所有好处 —— 尽管适应并支持这类应用负载花费了 PostgreSQL 好几年的时间。

PostgreSQL 对 JSON 的支持在过去的几年中持续发展演进,随着PostgreSQL不断实现和采纳 SQL/JSON 标准,未来也一定会继续保持这种发展势头。我曾与一些 PostgreSQL 用户聊过,他们在 PostgreSQL 数据库中存了几十TB的 JSON 文档 —— 用户表示体验甚好!

这个故事的关键是,开发者愿意押注 PostgreSQL 会拥有一个具有竞争力的 JSON存储系统,并愿意接受其最初实现的局限性,直到更为强大稳健的支持出现。这就引出了我们要讨论的 向量


向量崛起:一种新 JSON

向量并不是新东西,但近来它们的流行度飙升。如前所述,这归功于AI/ML系统新涌现出的易用性,而这些系统的输出结果是向量。典型用例是在存储的数据(文本、声音、视频)上建立模型,并用模型将其转换为向量格式,然后用于“语义搜索”。

语义搜索工作原理如下:你把输入用模型转换为对应的向量,并在数据库中查找与此向量最为相似的结果。相似度使用距离函数进行衡量:比如欧式距离,或余弦距离,结果通常会按距离排序取 TOP K,即 K 个最为相似的对象(K-NN, k nearest neighbors)。

向量的余弦距离被广泛用于衡量两者的相似度

用模型将“训练集”编码为向量需要耗费很长的时间,所以把这些编码结果 “缓存” 在持久化数据存储 —— 比如说数据库中是有意义的,然后你就可以在数据库中运行 K-NN 查询了。事先在数据库里准备好一组备查的向量,通常会为语义搜索带来更好的用户体验,需要“向量数据库”的想法就是这么来的。

AI模型将各种对象统一编码为向量(浮点数组)

在PostgreSQL中存储向量不是一件新鲜事儿。1996 年 PostgreSQL 首次开源时就已经带有数组类型(Array)了!而且多年来又进行了无数的改进。实际上,PostgreSQL 中 数组 类型名称可能有些用词不当,因为它其实可以存储多维数据(例如矩阵/张量)。PostgreSQL 原生支持了一些数组函数,不过有一些常见的向量运算不在其中,比如计算两个数组间的距离。你确实可以写个存储过程来干这个事,但这就是把活儿推给开发者了。

PostgreSQL特性矩阵:数组与Cube

幸运的是,cube 数据类型克服了这些局限。cube 在PostgreSQL代码库中也已经有20多年了,并且是为在高维向量上执行运算而设计的。cube 包含了在向量相似性搜索中使用的大多数常见距离函数,包括欧几里得距离,而且可以使用 GiST索引来执行高效的 K-NN 查询!但是 cube 最多只能存储100维的向量,而许多现代AI/ML系统的维度远超这个数。

ChatGPT Embedding API 使用 1536 维向量

那么,如果 array 可以搞定向量维度的问题但没有解决向量运算的问题;而 cube 可以搞定运算但搞不定维度,我们该怎么办?


PGVECTOR: 开源PG向量扩展

可扩展性 是 PostgreSQL 的基石特性之一:PostgreSQL 提供创建新数据类型和新索引方法的接口。这让 pgvector 成为可能:一个开源 PostgreSQL 扩展,提供了一种可索引的 vector 数据类型。简而言之,pgvector 允许您在 PostgreSQL 中存储向量,并使用各种距离度量执行K-NN查询:欧式距离、余弦和内积。到目前为止,pgvector 带有一种新索引类型 ivfflat,实现了 IVF FLAT 向量索引。

当您使用索引来查询向量数据时,事情可能和您所习惯的 PostgreSQL 数据查询略有不同。由于在高维向量上执行最近邻搜索的计算成本很高,许多向量索引方法选择寻找与正确结果 “足够接近” 的 “近似” 答案,这将我们带入 “近似最近邻搜索”(ANN)的领域。ANN 查询的关注焦点是,性能与召回率两个维度上的利弊权衡,这里“召回率(Recall)”指的是返回相关的结果所占百分比。

pgvector 在 ANN Benchmark 各测试集下的召回率/性能曲线

让我们以 ivfflat 方法为例。构建 ivfflat 索引时,您需要决定有多少个 list 。每个 list 代表一个“中心”,这些中心会使用 k-means 聚类算法确定。确定所有中心后,ivfflat 会计算每一个向量最接近哪个中心点,并将其添加到索引中。当查询向量数据时,你还需要决定需要检查多少个中心,这由 ivfflat.probes 参数确定。这就是您所看到的 ANN性能/召回率权衡:你检查的中心越多,结果就会越精确,但性能开销就越大。

IVF FLAT 索引算法的的召回率取决于检查的中心数量

把 AI/ML 的输出存入 “向量数据库” 已经很流行了,至于 pgvector 也已经有大把的使用样例。所以这里我们将关注重点放在未来的发展方向上。


迈向明天:更好的向量支持

与 PostgreSQL 9.2 版本中的 JSON 情况类似,我们正处于如何在 PostgreSQL 中存储向量数据的初级阶段 —— 虽然我们在PostgreSQL和 pgvector 中看到的大部分内容都很不错,但它即将要好得多!

pgvector 已经可以处理许多常见的 AI/ML 数据用例 —— 我已经看到许多用户成功地使用它开发部署应用!—— 因此下一步是帮助它打江山。这与 PostgreSQL 中的 JSON 和 JSONB 的情况没有太大区别,但 pgvector 作为一个扩展,将有助于它更快地迭代。

pgvector 的 Github Star 增长在2023年4月出现加速

在 2023 年的 PGCon 上,这是一个聚集了许多内部开发者的 PostgreSQL 会议,我做了一个名为《向量是新的JSON[1]》的快速演讲,其中分享了使用案例,以及改进 PostgreSQL 和 pgvector 向量数据检索性能所面临的挑战。这是一些需要解决的问题(有些已经在做了!):包括给 pgvector 添加更多并行机制,对超过 2000 维向量的索引支持,以及尽可能使用硬件来加速计算。好消息是添加这些功能并不难,只需要开源贡献!

许多人对于把 PostgreSQL 当成向量数据库这件事充满兴趣(重点是 PG 还是一个全能数据库!)。我预计正如历史上的 JSON 一样,PostgreSQL 社区会找到一种支持这种新兴工作负载的方法,更为安全,更容易伸缩扩展。

我期待您能提供各种反馈 —— 无论是关于PostgreSQL 本身还是 pgvector ,还是关于您如何在 PostgreSQL 中处理向量数据,或者您希望如何在 PostgreSQL 中处理数据,因为这将帮助社区为向量查询提供最佳的支持。

本文译自《VECTORS ARE THE NEW JSON IN POSTGRESQL[2]》一文。

作者 JONATHAN KATZ ,译者 冯若航


译者评论

PostgreSQL 在过去十年间有着持续稳定的高速增长,从一个"相对来说小众"的数据库,成为如今全世界开发者中最流行,最受喜爱,需求量最大的数据库,不可谓不成功。PG 成功的因素有很多,开源,稳定,可扩展,等等等等。但我认为这里的关键一招还是 JSON 支持。笔者本人就是在 PostgreSQL 9.4 为其强大 JSON 功能折服,果断从 MySQL 跳车弃暗投明。

PostgreSQL 获得数据库三项大满贯冠军,且势头一往无前

拥有了 JSON 特性的 PostgreSQL 等于 MongoDBMySQL 合二为一,恰到好处地赶上了互联网下半场的风口。从 DB-Engine 热度趋势上也能看出,PostgreSQL 开始起飞的时间正是在 2014 年 发布 PostgreSQL 9.4 之后。2013 ~ 2023 这十年可以说是 PG 的黄金十年,无数强大的新功能与各式扩展插件喷涌而出,奠定了 PG 现今不可撼动的地位。

DB-Engine 热度走势,来自搜索引擎与网站的综合指数

而放眼未来十年,数据库的下一站会是哪里?本文给出了答案 —— 向量。正如同 JSON 一样,PostgreSQL 永远站在时代浪潮的巅峰引领潮流 —— 成为第一个提供全方位向量支持的关系型数据库。我有充足的把握断言:以向量为代表的功能将在接下来的十年中继续驱动 PostgreSQL 的高速增长。

pgvector 一定不会是 PostgreSQL 处理向量数据的终点,但它为 SQL 向量处理设定了一个标杆。PGVector 项目由 Andrew Kane 于 2021年4月创建,慢热了两年,而从今年三四月开始半年不到暴涨 4K star。而我也可以骄傲的说,作为 PG 社区的一员,我也在这里推波助澜,做了一些工作。

我们将 pgvector 提入 PostgreSQL PGDG 官方源,正式成为 PG向量扩展的事实标准;我们进行性能评测,引发了推上关于 PGVector 的大讨论;而我们所维护的开箱即用的开源 RDS PG 替代 Pigsty,则是第一波将 pgvector 集成整合提供服务的 PostgreSQL 发行版

Pigsty 凝聚 PG 生态合力,为用户提供开源免费开箱即用的本地 PostgreSQL RDS 服务

目前我们也在着力于改进 pgvector 的实现,实现了另一种主流向量索引算法 hnsw,在一些 ANN 场景下相比 IVFFLAT 有20倍的性能提升,而且完全兼容 pgvector 接口,并将于近期 Pigsty Release 提供预览。

pgvector 改进实现在 ANN-Benchmark 下的初步表现

最重要的是,我们相信 PostgreSQL 社区的力量,我们愿意凝聚合力,劲往一处使,共同让 PostgreSQL 走得更快、更远,让 PostgreSQL 在 AI 时代再创辉煌!


References

[1] 向量是新的JSON

[2] VECTORS ARE THE NEW JSON IN POSTGRESQL

[3] AI大模型与向量数据库 PGVECTOR

[4] PostgreSQL:世界上最成功的数据库

[5] 更好的开源RDS替代:Pigsty

[6] Pigsty v2.1 发布:向量扩展 / PG12-16 支持

PostgreSQL:最成功的数据库

2023 年 StackOverflow 调研结果已经新鲜出炉,来自185个国家与地区的9万名开发者给出了高质量的反馈。 在今年的调研中,PostgreSQL 在数据库全部三项调研指标(流行度,喜爱度,需求度)上获得无可争议的全能冠军,成为真正意义上“最成功”的数据库 —— “PostgreSQL is the Linux of Database!”

https://demo.pigsty.cc/d/sf-db-survey

当我们说一个数据库“成功”时,究竟在说什么?评价一个数据库有许多标准:功能、质量、安全、性能、成本,但没有哪种可以普世泛用。不过 Succeed 既代表成功,又代表继承,所以成功与“后继有人”相通。对一项技术而言,用户的规模 、喜好、需求决定了生态的繁荣程度,唯有这种最终存在意义上的神意裁决 —— 才能让所有人心服口服。 而连续进行七年的 StackOverflow 年度开发者调研为我们窥见技术发展流行趋势打开了一扇窗户。

PostgreSQL现在是全世界最流行的数据库

PostgreSQL是开发者最喜爱欣赏的数据库!

PostgreSQL是用户需求最为强烈的数据库!

流行度代表过去,喜爱度代表现在,需求度代表将来,这三个指标很好地反映了一项技术的生命力。存量与增量,时与势都站在 PostgreSQL 一侧,恐怕在几年内恐怕都不会有任何能挑战 PostgreSQL 地位的竞争对手。 作为 PostgreSQL 忠实的用户,社区成员,专家,布道师与贡献者,从拥抱 PostgreSQL的那一刻起,我就相信会有这一天,然而亲自见证这一刻,仍然让我感慨良多。遂撰此文,聊一聊这件事背后的 WhyWhat

推荐阅读:StackOverflow 2022 往期调研结果回顾:《为什么PostgreSQL将成为最成功的数据库?


数据的来源:社区调研

数据库的用户是开发者,而没有比直接问开发者们更有代表性的调研方式了。StackOverflow 调研结果中提供了 流行,欣赏,渴望三个结果指标,但这三项数据都来自同一个巧妙设计的问卷题目:

“在过去一年中,您在哪些数据库环境中进行了密集的开发工作,您又希望在接下来一年在哪些数据库上工作?如果你过去一年用了这个数据库,来年还希望接着用,那么就在两个复选框上都打勾”。

“Which database environments have you done extensive development work in over the past year, and which do you want to work in over the next year? If you both worked with the database and want to continue to do so, please check both boxes in that row.”

每个数据库后都有两个复选框,如果开发者在第一个框上打勾,即去年我在用此数据库,那么就会被标记为“使用者”(Used); 如果开发者在第二个框上打勾,即来年我想用这个数据库,那么会被标记为“需求者”(Wanted);而两个框都打勾的开发者,会被标记为“赞赏者”(Loved / Admired)。

https://survey.stackoverflow.co/2023

使用者占总体的比例,就是流行度,或使用率,在上图左边用柱状图表示。需求者占总体的比例,就是需求度,或渴望度,在上图右边以蓝点表示。 赞赏者占现有使用者的比例,就是欣赏度,或喜爱度/口碑,在上图右边以红点表示。不难看出,2023年,PostgreSQL 在流行度上甩开 MySQL,成为世界上最流行的数据库。在需求度和口碑上更是远远甩开其他数据库独树一帜。

同样的问题连续问了七年,如果我们结合这过去七年的变迁,把排名前10的主流数据库流行度 - 净喜爱度画在一张二维散点图上,那么就能更容易地获得一些关于数据库领域的发展变迁的洞察,对形成正确的比例感很有帮助。

X轴为流行度,Y轴为净喜爱程度(2*喜爱度% - 100),图元大小与流行度与喜爱度的几何平均数成正比。

在 2023年的当下切面中,四个角落被四种数据库占据:右上角是最为流行且最受欢迎的 PostgreSQL,右下角是流行但不受待见的 MySQL; 左上角是流行程度一般但备受喜爱的 Redis,左下角是过气且不受待见的 Oracle。在四者中间,坐落着相对中庸的 SQLiteMongoDBSQL Server

结合时间轴不难看出,PostgreSQL 的流行程度与受欢迎程度在持续增长;MySQL 的受欢迎程度变化不大但流行度暴跌; Redis 与 SQLite 整体上在进步,而 MongoDB 开始见顶回落,SQL Server 和 Oracle 这两种商业关系型数据库最近几年都在持续走下坡路。

从图中我们可以得出一个基本的判断:在未来几年中,数据库领域都不会出现足以挑战 PostgreSQL 的对手。PostgreSQL 在数据库领域的地位,已经如同 Linux 在服务器操作系统上的地位一样难以撼动


过去的积累:流行度

PostgreSQL —— 世界上最流行的数据库

一项技术使用者占总体的比例,就是流行度。它的含义是:过去一年有多少比例的用户使用了这项技术。流行度代表过去一年的积累使用,是存量指标,也是最核心的事实指标。

在 2023 年, “最先进” PostgreSQL 在所有开发者中以 45.6% 的使用率,首次超过“最流行”数据库 MySQL 41.1%,领先 4.5%,使用率是第二名 MySQL 的1.1倍。 对于专业开发者(约占总样本的3/4)来说,PostgreSQL 的使用率在去年(2022)就已经超过 MySQL 了,以 46.5% vs 45.7% 领先0.8个百分点; 在 2023 年,这一差距进一步拉大到 49.1% vs 40.6,领先 8.5% —— 换句话说,专业开发者中,PostgreSQL 的使用率已经是 MySQL 的 1.2 倍了。

过去几年,MySQL 一直霸占着数据库流行榜的榜首,洋洋得意地打起了“世界上最流行的开源关系型数据库” 这一旗号。 不过这次,“最流行” 的桂冠真的要让给 PostgreSQL 了。在流行度上,其他数据库和 PostgreSQL / MySQL 比根本就不是一个重量级,自然就更不用说了。

更重要的的是变化趋势:在长期列入排名的十几款头部数据库中,只有 PostgreSQL 的流行度是持续上升的,保持着高歌猛进的增长势头,而其他所有的数据库使用率都在下行。 此消彼长,随着时间的推移,PostgreSQL 与其他数据库的流行度差距只会进一步拉大 —— 因此在相当长的一段时间内,恐怕是看不到有任何挑战者能撼动 PostgreSQL 现在的位置了。

值得一提的是,“国产数据库”的标杆 ”TiDB“ 这次也加入到 StackOverflow 排行榜中,并以 0.2% 的使用率,拿到了末位第 32 名的名次。

流行度反映的是当下数据库的规模势能,而喜爱度反映的是未来数据库的增长潜能。


现在的动能:喜爱度

PostgreSQL —— 最受开发者喜爱的数据库

所谓“口碑”,喜爱度(Loved)或欣赏度(Admired),指的是有多少比例的用户愿意继续使用此项技术,这是一个年度的“留存率”指标,可以反映用户对一项技术的看法与评价。

2023 年, PostgreSQL 蝉联最受开发者喜爱的数据库。过去几年 Redis 一直是用户最喜欢的数据库。直到 2022 年,PostgreSQL 第一次超过 Redis,成为最受开发者喜爱的数据库。 PostgreSQL 和 Redis 的口碑一直在伯仲之间(70%),并与其他后来者拉开了非常显著的差距。

作为一个交叉印证,在 2022 PostgreSQL 社区年度调研中,对于 PostgreSQL 的存量用户来说,使用程度加深,用量加大的比例(蓝/粉)对于用量萎缩的比例(黄绿)占据了压倒性多数,足以说明基本盘留存的稳定程度。

Redis是简单易用的数据结构缓存服务器,经常会与关系型数据库 PostgreSQL 搭配使用,广受开发者喜爱(但流行度一般,只有20%,位列第六)。 在后面的交叉分析环节我们也可以看到这两者之间有着所有数据库间最为强烈的羁绊 —— 86% 的 Redis 用户想要使用 PostgreSQL,而 30% 的 PostgreSQL 用户想要使用 Redis。 其他评价正面的数据库包括:SQLite,MongoDB,SQL Server 等。MySQL 和 ElasticSearch 的口碑在 50% 中线算毁誉参半。榜上最不受用户待见的数据库为 Access、 IBM DB2 、CouchDB,Couchbase,以及 Oracle。

并不是所有潜能,都可以转换为实打实的动能。用户的喜爱并不一定会付诸行动,而这就是第三项指标所要回答的问题 —— 需求度。


未来的趋势:需求度

PostgreSQL —— 需求量最大的数据库

需求者占总体的比例,就是需求率(Wanted),或渴望度(Desired)。它的含义是,接下来一年有多少比例的用户会实际选择使用此项技术。 在需求度 / 渴望度 这一项中,PostgreSQL 一骑绝尘,远远甩开其他数据库。以 42.3% 的比例连续第二年获得第一,且保持着一往无前的增长态势。不断与后来者拉开距离。

在 2023 年,一些数据库的需求量出现了显著增长。大概率是因为由 OpenAI ChatGPT 所引领的大语言模型AI浪潮所致:对智能的需求拉动了对数据基础设施的需求。 10年前,对 JSONB/GIN 等 NoSQL 特性的支持奠定了 PostgreSQL 在互联网黄金时代的蓬勃发展,而今天,第一个构建在成熟数据库上的向量扩展 pgvector ,更是让 PostgreSQL 有了进入 AI 时代的船票,为下个十年的增长准备好了敲门砖。


但是,为什么呢?

PostgreSQL 在需求率, 使用率,喜爱率上都拔得头筹,天时地利人和齐备,动能势能潜能都有,足以称得上是最成功的数据库,而且在肉眼可见的几年里也不会有任何挑战者。 但令人好奇的是,为什么 PostgreSQL 会如此成功 ? 其实,秘密就藏在它的 Slogan 里:“世界上最先进的开源关系型数据库

关系型数据库是如此的普及与重要,也许其他的数据库品类如键值,文档,搜索引擎,时序,图,向量加起来也比不上它的一个零头。以至于当大家谈起数据库时,如果没有特殊说明,默认隐指的就是”关系型数据库“。在它面前,没有其他数据库品类敢称自己为”主流“。 在去年的《为什么PostgreSQL将成为最成功的数据库?》中,我们详细介绍了关系型数据库的竞争格局 —— 三足鼎立:关系型数据库的生态位高度重叠,其关系可以视作零和博弈。抛开微软生态关门自嗨相对独立的商业数据库 SQL Server 不提,在当下分久必合的收敛阶段中,以 WireProtocol 计能作为“根”的数据库只有三种:Oracle,MySQL,以及PostgreSQL。关系型数据库世界里上演的是一场 三国演义

今天下三分,然 Oracle/MySQL 疲敝 ,日薄西山, PostgreSQL 高歌猛进,如日中天。此消彼长,前途无量。

Oracle 有才无德,MySQL 才浅德薄,PGSQL 德才兼备”

Oracle 是老牌商业数据库,有着深厚的历史技术积淀,功能丰富,支持完善。广受不差钱且需要背锅侠的企业,特别是金融行业喜爱。但其费用高昂,且以讼棍行径成为知名的业界毒瘤。 Microsoft SQL Server 性质与Oracle类似,都属于商业数据库。商业数据库整体受开源数据库冲击,处于缓慢衰退的状态。

MySQL 号称“最流行”,然而树大招风:前有狼后有虎,上有野爹下有逆子,处于四面楚歌的境地中: 在严谨的事务处理和数据分析上,MySQL 被同为开源生态位的 PostgreSQL 甩开几条街;而在糙猛快的敏捷方法论上,MySQL 又不如新兴 NoSQL 好用; 上有养父 Oracle 压制,中有兄弟 MariaDB 分家,下有逆子 TiDB/OB 等兼容 NewSQL 分羹,因此也在走下坡路。

Oracle 作为老牌商业数据库,才毋庸质疑;但其作为业界毒瘤,“德” ,亦不必多说,故曰:“有才无德”。 MySQL 虽有开源之功德,奈何认贼作父;且才疏学浅,功能简陋,只能干干CRUD,故曰:“才浅德薄”。 唯 PostgreSQL德才兼备:既占据了开源崛起之天时,又把握了最为流行之地利,还有着先进稳定之人和。 正所谓:君子藏器于身,因时而动。不鸣则已,一鸣惊人!


开源与先进

来自 TimescaleDB 的PostgreSQL 社区年度调研也反映出,用户选择 PostgreSQL 的首要因素便是 开源稳定开源 —— 意味着软件本身可以免费使用,可以二次开发,没有供应商锁定,不存在“卡脖子问题”。 可靠 —— 意味它能正确稳定工作,行为表现能够符合预期,而且有着长时间大规模生产环境的优异战绩。越是资深的开发者,便越是看重这两个属性。

宽泛地讲,扩展,生态,社区,协议可以归并入 “开源” 。而稳定可靠,ACID,SQL,扩展,可用性,可以总结为 “先进” 。这便正好与 PostgreSQL 的 Slogan 相呼应 —— 世界上最先进的开源关系型数据库

https://www.timescale.com/state-of-postgres/2022


开源之德

PG的“德”在于开源。祖师爷级的开源项目,全世界开发者群策群力的伟大成果。协议友善BSD,生态繁荣扩展多。开枝散叶,子孙满堂,Oracle替代扛旗者.

什么叫“德”,合乎于“道”的表现就是德。而这条“道”就是开源。PostgreSQL是历史悠久的祖师爷级开源项目,更是全世界开发者群策群力的典范成果。

很久很久以前,开发软件/信息服务需要使用非常昂贵的商业数据库软件。单花在软件授权上的费用可能就有六七位数,加之相近的硬件成本与服务订阅成本。Oracle一个 CPU 核一年的软件授权费用便高达十几万,壕如阿里也吃不消要“去IOE”。以 PostgreSQL / MySQL 为代表的的开源数据库崛起,让世界多了一个新的选择。

“不要钱” 的开源数据库可以让我们自由随意地使用数据库软件,而这一点引发了行业变革:从上万元每核·每月的商业数据库软件授权,到20块钱/核·月的纯硬件成本。数据库走入了寻常企业中,让免费提供信息服务成为可能。

开源是有大功德的:互联网的历史就是开源软件的历史,IT行业之所以有今天的繁荣,人们能享受到如此多的免费信息服务,核心原因之一就是开源软件。 开源是一种真正成功的,以软件自由为目的,由开发者构成的 Communism(社区主义软件这种IT业的核心生产资料变为全世界开发者公有,按需分配。开发者各尽所能,人人为我,我为人人。

一个开源程序员工作时,其劳动背后可能蕴含的是数以万计顶尖开发者的智慧结晶。程序员薪资高从原理上来说是因为,开发者本质上不是一个简单的工人,而是一个指挥软件和硬件干活的 包工头。程序员自己就是核心生产资料;软件来自公有社区;服务器硬件更是唾手可得;因此一个或几个高级的软件工程师,就可以很轻松地利用 开源生态快速解决领域问题。

通过开源,所有社区开发者形成合力,极大降低了重复造轮子的内耗。使得整个行业的技术水平以匪夷所思的速度向前迈进。开源的势头就像滚雪球,时至今日已经势不可挡。 越是底层基础的软件,开源便越占据主导优势。基本上除了一些特殊场景和路径依赖,软件特别是基础软件中,闭门造车/所谓“自力更生”已经成了业内超级大笑话。

开源,是 PostgreSQL 对阵 Oracle 的最大底气所在。

Oracle 先进,但 PostgreSQL 也不差。PostgreSQL 是 Oracle 兼容性最好的开源数据库,原生即支持 Oracle 85% 的功能,更有 96% 功能兼容的专业发行版。 但更重要的是,Oracle 价格高昂,而 PG 开源免费。压倒性的成本优势让 PG 拥有了巨大的生态位基础:它不一定要在功能先进性上超过Oracle 才能成功 ,廉价9成正确 已经足以干翻 Oracle 。

PostgreSQL 可以视作一个开源版的“Oracle”,是唯一能真正威胁到 Oracle 的数据库。作为 ”去O“ 抗旗者,PG 可谓子孙满堂,养活了一大批自主可控 的国产数据库公司。 根据信通院统计,36% 的 “国产数据库” 直接基于PG “二开/魔改/套壳/换皮”,华为的openGauss 与 GaussDB 就是最典型的例子。 重要的是,PostgreSQL 使用 BSD-Like 的 PostgreSQL 协议,是允许这种行为的 —— 你只要不打着PG的名号招摇撞骗,改个名字直接卖起来都行。这样开放的胸襟,是被Oracle收购的,使用GPL协议的 MySQL 所难以比拟的。


先进之才

PG的“才”在于先进。一专多长,全栈多模:“自主可控自动驾驶时序地理空间AI向量分布式文档图谱全文检索可编程超融合联邦流批一体 HTAP Serverless 全栈式平台数据库”,单一组件即可覆盖几乎所有数据库需求。

PostgreSQL 不仅仅是传统意义上只能做 OLTP 的单纯 “关系型数据库”,而是一个多模态数据库。 对于中小企业来说,基本单一组件便足以覆盖中小型企业绝大多数场景的数据需求:OLTP,OLAP,时序,地理空间GIS,分词与全文检索,JSON/XML文档,NoSQL特性,图,向量,全都能用上。

皇帝数据库 —— 自主可控自动驾驶时序地理空间AI向量分布式文档图谱全文检索可编程超融合联邦流批一体 HTAP Serverless 全栈式平台数据库

PostgreSQL 的先进,除了体现在其备受赞誉的内核稳定性上,更是体现在它强大的可扩展性里。 插件系统让 PostgreSQL 不再仅仅是一个单线程演化的数据库内核,而是可以有无数并行演进的扩展插件,如同量子计算一般同时探索所有方向上的可能性。每一个数据处理的细分垂直领域,PostgreSQL 绝不会缺席。

正如:PostGIS 之于地理时空数据库,TimescaleDB 之于时序数据库,Citus 之于分布式/列存储/HTAP数据库,PGVector 之于AI向量数据库,AGE之于图数据库,PipelineDB 之于流处理; 以及终极杀招 —— 使用外部数据源包装器(FDW),使用统一的 SQL 访问所有异构的外部数据库。可以说PG是真正的全栈数据库平台,比起 MySQL 这样单纯的 OLTP 数据库,它的功能要先进太多了。

在一个很可观的规模内,PostgreSQL 都可以独立扮演多面手的角色,一个组件当多种组件使。而单一数据组件选型可以极大地削减项目额外复杂度,这意味着能节省很多成本。它让十个人才能搞定的事,变成一个人就能搞定的事。 在使用“专用数据库”前切莫忘记:为了不需要的规模而设计是白费功夫,这属于过早优化的一种形式。如果真有那么一样技术可以满足你所有的需求,那么使用该技术就是最佳选择,而不是试图用多个组件来重新实现它。

以探探为例,在 250w TPS200 TB 不重复TP数据的量级下,单一PostgreSQL选型依然能稳定可靠地撑起业务,并能在很可观的规模内做到一专多长。 除了本职的 OLTP,PG 还在相当长的时间里兼任了缓存,OLAP,批处理,甚至消息队列的角色。当然神龟虽寿,犹有竟时。最终这些兼职功能还是要逐渐分拆出去由专用组件负责,但那已经是近千万日活时候的事了。

PostgreSQL 的先进,更是体现在其繁荣的生态里。以数据库内核为中心,向上,有着衍生特化的变体与构建于其上的“上层数据库” —— Greenplum数据仓库,Firebase的开源替代 Supabase,专用图数据库 edgedb 等等等等。 向下,有着各种开源/商业/云发行版来整合各种工具形成合力 —— 各家的RDS ,开箱即用的 Pigsty ;水平方向上,甚至还有着一些强大的拟态组件/版本,可以通过兼容 Wire Protocol 的方式来仿真其他数据库,无需修改客户端驱动就能完成数据库迁移 —— 模拟 SQL Server 的 babelfish,模拟 MongoDB 的 FerretDB,兼容 Oracle 的 EnterpriseDB / IvorySQL 都是样例。

PostgreSQL 的先进性有目共睹,这也是其对阵同为开源关系型数据库的老对手 —— MySQL 时,真正的核心竞争力。

先进,是 PostgreSQL 压倒 MySQL 的核心竞争力。

MySQL的口号是“世界上最流行的开源关系型数据库”,它的核心特点是糙猛快,基本盘是互联网公司。互联网公司的典型特点是什么?追逐潮流糙猛快 说的是互联网公司业务场景简单(CRUD居多);数据重要性不高,不像传统行业(例如银行)那样在意数据的一致性与正确性;可用性优先,相比停服务更能容忍数据丢乱错,而一些传统行业宁可停止服务也不能让账目出错。 说的则是互联网行业数据量大,它们需要的就是水泥槽罐车做海量 CRUD,而不是高铁和载人飞船。 说的则是互联网行业需求变化多端,出活周期短,要求响应时间快,大量需求的就是开箱即用的软件全家桶(如LAMP)和简单培训就能上手干活的 CRUD Boy。 于是,糙猛快的互联网公司和糙猛快的 MySQL 一拍即合,MySQL吃到了互联网崛起的一波大红利。

然而时来天地皆同力,运去英雄不自由。时过境迁,PostgreSQL 进步神速,在”快“与”猛“上 MySQL 已经不占优,现在只剩下”“了。

MySQL竟然默认允许部分成功的事务提交

先进的因会反映为流行的果,流行的东西因为落后而过气,而先进的东西会因为先进变得流行。在这个变革的时代中,没有先进的功能打底,“流行”也也难以长久。时代所赋予的红利,也会随时代过去而退潮。 调查的结果也用事实证明,MySQL 唯一能引以为豪的 “流行” 在 PostgreSQL 压倒性的 “先进” 优势前,根本维持不住。

先进开源,就是 PostgreSQL 成功的最大法宝。Oracle 先进, MySQL 开源,PostgreSQL 先进又开源。天时地利人和齐备,何愁大业不成?


展望未来

PostgreSQL 数据库内核在数据库领域的生态位,类似于 Linux 操作系统内核在操作系统领域的生态位。 对于数据库,至少是 OLTP 数据库来说,数据库内核之争已经尘埃落定 —— PostgreSQL 已经是一台足够完美的内核发动机。

然而,用户最终需要的不单单是一台发动机,而是整车、驾驶能力与交通服务。数据库领域竞争的焦点,已经从 Software 本身,转移到了 Software enabled Service —— 完整的数据库发行版与数据库服务。 对于基于 PostgreSQL 内核的数据库发行版而言,竞争才刚刚开始。谁会成为PG的Debian,RedHat 与 Ubuntu ? 这便是我们做 Pigsty 的初衷 —— 制作一个开箱即用的、开源免费、本地优先的 PostgreSQL 数据库发行版,让所有人都能用好数据库用好数据库。 当然,限于篇幅,那就是另一篇要介绍的故事了。


参考阅读

2022-08 《PostgreSQL 到底有多强?

2022-07 《为什么PostgreSQL是最成功的数据库?

2022-06 《StackOverflow 2022数据库年度调查

2021-05 《Why PostgreSQL Rocks!

2021-05 《为什么说PostgreSQL前途无量?

2018 《PostgreSQL 好处都有啥?

2023 《更好的开源RDS替代:Pigsty

2023 《StackOverflow 7年调研数据跟踪

2022 《PostgreSQL 社区状态调查报告 2022

PostgreSQL 到底有多强?

上回,我们通过分析 StackOverflow 的用户调研数据,说明了《为什么PostgreSQL是最成功的数据库》。

而这一次我们将用性能数据来说话,聊聊最成功的 PostgreSQL 到底有多强,帮助大家做到“心中有数”。


太长不看

如果您对以下这些问题有兴趣,那么本文会对您有所帮助:

  • PostgreSQL 到底性能有多强? 点查 QPS 60万+,最高达 200 万。读写 TPS (4写1读)每秒 7 万+,最高达14万。
  • PostgreSQL 与 MySQL 的极限性能对比 极限条件下,PgSQL点查性能显著压倒 MySQL,其他性能基本与MySQL持平。
  • PostgreSQL 与其他数据库的性能对比 “分布式数据库”/NewSQL 在相同硬件规格下的性能表现显著落后于经典数据库。
  • PostgreSQL 与其他分析数据库的 TPC-H 表现。 PostgreSQL 原生作为一个 HATP 数据库,有比较亮眼的分析表现。
  • 云数据库 / 云服务器 的成本到底有没有优势? c5d.metal 用1年的价格,可以把服务器买下来托管用5年。对应规格云数据库用1年的价格,可以供你买同样的EC2用20年

详细测试过程与原始数据放置于:github.com/Vonng/pgtpc


PGBENCH

软件与硬件的技术日新月异,尽管性能评测的文章汗牛充栋,却没有多少能反映这些变换。在这项测试中,我们选择了两种新规格硬件,使用 PGBENCH 测试了最新的 PostgreSQL 14.5 在这些硬件上的性能表现。

测试的主体包括四种规格的硬件,两台 Apple 笔记本与三台 AWS EC2云服务器,分别是 2018 年使用 Intel 6核 i9芯片的 15寸顶配 Macbook Pro,2021 年使用 M1 MAX 芯片的顶配 16 寸 Macbook Pro ,AWS z1d.2xlarge (8C 64G),以及 AWS c5d.metal ,这些都是市面上可以轻松买到的商用硬件。

PGBENCH是 PostgreSQL 自带的压测工具,默认使用类 TPC-B 的查询,可用于评估 PostgreSQL 及其兼容版数据库的性能。测试分为两种:只读查询 RO、以及读写 RW。只读查询包含一条 SQL,随机从1亿条数据库中挑选一条查出;而读写事务包含5条SQL语句,一条查询、1条插入与三条更新。测试基于 s=1000 的数据集规模,使用 PGBENCH 逐步增加客户端连接数,找到 QPS / TPS 的极大值点,并记录持续测试 3-5 分钟后的稳定均值,结果如下:

No Spec Config CPU Freq S RO RW
1 Apple MBP Intel 2018 Normal 6 2.9GHz - 4.8GHz 1000 113870 15141
2 AWS z1d.2xlarge Normal 8 4GHz 1000 162315 24808
3 Apple MBP M1 Max 2021 Normal 10 600MHz - 3.22GHz 1000 240841 31903
4 AWS c5d.metal Normal 96 3.6GHz 1000 625849 71624
5 AWS c5d.metal Extreme 96 3.6GHz 5000 1998580 137127

Read Write

pg-performence-2.jpg

图:各硬件配置下读写 TPS 上限

pg-performence-3.png

图:各硬件配置下读写 TPS 曲线

Read Only

pg-performence-4.png

图:各硬件配置下点查 QPS 上限

pg-performence-5.png

图:各硬件配置下点查 QPS - 并发曲线

结果相当令人震惊,在 Apple M1 Max 10C 笔记本上,PG 跑出了 32K 读写,240K 点查的性能水平,在 AWS c5d.metal 生产物理机上,PG 跑出了 72K 读写,630K 点查的性能。使用极限优化压榨,最多可以达到 单机 137K 读写,2M 点查 的怪兽级性能。

作为一个粗略的规格参考,探探作为一个前部的互联网App,PostgreSQL 全局 TPS 为 40万左右。这意味着十几台这样的新笔记本,或几台顶配服务器(10W内¥)就有潜力支撑起一个大型互联网应用的数据库服务,这对于以前来说是难以想象的。

关于成本

以宁夏区域,C5D.METAL 机型为例,该机型是目前综合算力最好的物理机,且自带 3.6 TB的本地NVME SSD存储,有7种可选的付费模式:

付费模式 月度 预付 折合每年
按需付费 31927 0 383,124
标准预留,1年,无预付费用 12607 0 151,284
标准预留,1年,预付部分 5401 64,540 129,352
标准预留,1年,预付全部费用 0 126,497 126,497
可转化预留,3年,无预付费用 11349 0 136,188
可转化预留,3年,预付部分 4863 174,257 116,442
可转化预留,3年,预付全部费用 0 341,543 113,847

折合每年成本在 11万 ~ 15万,零售按需每年成本38万。该机器如果自行购置,IDC托管代维网电五年综合成本应在10万内。尽管看上去云硬件的年化成本高达自建的五倍,但考虑到其灵活性,折扣优惠与抵扣券,AWS EC2 云服务器定价总体仍处于合理范围。使用此类云硬件自建数据库,也有非常优异的性能表现。

但 RDS for PostgreSQL 则完全是另一个故事了,如果您想使用类似规格的云数据库,最接近的规格是 db.m5.24xlarge,96C,384G,配置 3.6T / 80000 IOPS 的 io1存储(c5d.metal 3.6T NVME SSD 8K RW IOPS 大约95K左右,普通 io1 存储最高 IOPS 为 80K),则每月成本为 24万¥,每年成本为286,7630¥ ,是同规格 EC2 自建的近 20 倍

AWS价格计算器:https://calculator.amazonaws.cn/


SYSBENCH

PostgreSQL 确实很强,但与其他数据库系统相比则何如?PGBENCH 主要用于评估 PostgreSQL 及其衍生/兼容数据库的性能,但如果需要横向比较不同数据库的性能表现,我们就要用到 sysbench 了。

sysbench 是一款开源、跨平台的多线程数据库性能测试工具,测试结果可以很有代表性地反映一个数据库系统的事务处理能力能力。sysbench 包含了10个典型测试用例,如测试点查性能的 oltp_point_select,更新性能的 oltp_update_index,综合读写事务性能的 oltp_read_only (16条查询一个事务),oltp_read_write (20条混合查询一个事务)与oltp_write_only (6条写入SQL)等…。

sysbench 既可以用于测试 MySQL 的性能,也可以用来测试 PgSQL 的性能(当然也包括两者的兼容衍生),因此具有良好的横向可比性。让我们先来看一下最为喜闻乐见的对比,开源关系数据库内战:世界上“最流行”的开源关系型数据库 —— MySQL , 与世界上最先进的开源关系型数据库 —— PostgreSQL 性能横向对比。

Dirty Hack

MySQL 并没有提供一个官方的 sysbench 测试结果,只是在官网上贴出了一个第三方评测结果的图片与链接,不加解释地暗示 MySQL 可以做到 1M 的点查 QPS,240K 的索引键更新,约 39K 的复合读写TPS。

pg-performence-5.png

图:https://www.mysql.com/why-mysql/benchmarks/mysql/

这是相当不讲武德的行为。因为如果阅览了连接的评测文章就会发现:这是把所有 MySQL 安全特性关闭得到的结果:关闭Binlog,提交刷盘,FSYNC,性能监控,DoubleWrite,校验和,强制使用 LATIN-1 字符集,这样的数据库根本没法用于生产环境,只是为了刷分而刷分。

但反过来说,我们也可以使用这些 Dirty Hack,把对应的 PostgreSQL 安全特性也关闭,也看看 PostgreSQL 的最终极限在哪里?结果相当震撼,PGSQL点查QPS干到了 233万每秒,峰值远远甩开 MySQL 一倍还多。

pg-performence-6.png

图:不讲武德的Benchmark:PgSQL vs MySQL

pg-performence-7.png

PostgreSQL 极限配置下点查压测现场

必须说明的是,MySQL 的bench使用的是 48C 2.7GHz的机器,而PostgreSQL使用的是 96C 3.6GHz 的机器。不过因为PG使用进程模型,我们可以使用 c=48 的测试值作为 PG 在 48C 机器上表现的一个下限近似:对于只读请求,QPS峰值通常在客户端数略大于CPU核数时达到。即便如此,c=48 时PG的点查 QPS( 150万)仍然比MySQL峰值高了43%。

在此也期待 MYSQL 专家基于完全相同的硬件给出测评报告,更好的地进行对比。

pg-performence-8.png

图:MySQL 有结果的四项 sysbench 结果,c=48

在其他测试上,MySQL 也有不错的极限表现,otlp_read_only, oltp_update_non_index 都与 PostgreSQL (c=48)接近持平,甚至在 oltp_read_write 上还略微超过 PostgreSQL。

总体来说在极限条件下,PG除了点查上碾压了MySQL,其他测试上性能与 MySQL 基本持平。

Fair Play

尽管在功能丰富度上判若云泥,但 MySQL 在极限性能上基本能与 PostgreSQL 称得上大体旗鼓相当。那么其他的数据库,特别是新一代 NewSQL 的表现又如何呢?

能够在官网上给出 sysbench 测试报告的数据库都算是 Fair Play 的体面玩家,我们相信他们都是基于真实生产环境使用的配置进行的测试,因此不能和 MySQL 那样使用 Dirty Hack。这里我们依然使用 AWS c5d.metal 机型,但完全使用生产环境配置进行性能测试,相比极限性能有接近一半折损,但更为费厄泼赖,具有很强的可对比性。

我们从几种比较具有代表性的NewSQL数据库官网上收集到了官方的 sysbench 评测报告。并不是所有的数据库都给出了完整的 sysbench 10 项测试结果,而且硬件规格与表规格也参差不齐。不过考虑到几种数据库均使用基本相仿的硬件规格(100核上下的算力,PolarDB-X , YugaBytes 除外),数据规模也基本为 160M 记录(OB,YB除外),总体还是具有比较可观的横向可比性,也足以让我们管中窥豹形成直觉认知了。

Database PGSQL.C5D96C TiDB.108C OceanBase.96C PolarX.64C Cockroach Yugabyte
oltp_point_select 1372654 407625 401404 336000 95695
oltp_read_only 852440 279067 366863 52416
oltp_read_write 519069 124460 157859 177506 9740
oltp_write_only 495942 119307 9090
oltp_delete 839153 67499
oltp_insert 164351 112000 6348
oltp_update_non_index 217626 62084 11496
oltp_update_index 169714 26431 4052
select_random_points 227623
select_random_ranges 24632
Machine c5d.metal m5.xlarge x3 i3.4xlarge x3 c5.4xlarge x3 ecs.hfg7.8xlarge x3 ecs.hfg7.8xlarge x1 Enterprise c5d.9xlarge x3 c5.4xlarge x3
Spec 96C 192G 108C 510G 96C 384G 64C 256G 108C 216G 48C 96G
Table 16 x 10M 16 x 10M 30 x 10M 1 x 160M N/A 10 x 0.1M
CPU 96 108 96 64 108 48
Source Vonng TiDB 6.1 OceanBase PolarDB Cockroach YugaByte

pg-performence-10.png

图:sysbench 10项测试结果(QPS,越高越好)

pg-performence-11.png

按数据库分类,除以核数的归一化性能对比

让人感到震惊的是,新一代分布式数据库(NewSQL)全线拉胯。在相近的硬件规格下,与 PostgreSQL 表现出高达数量级的差距,几种新数据库中表现最好的反而是仍然基于经典主从架构的 PolarDB。这样的性能结果,难免不让人重新审视起分布式数据库与 NewSQL 的理念

通常来说,分布式数据库的核心利弊权衡是质量换规模,但让人没想到的是牺牲掉的不仅仅是功能与稳定性,还有如此可观的性能。高德纳曰:“过早优化是万恶之源”,为了不需要的规模(万亿级+,TP百TB+)牺牲如此大的性能(以及功能与稳定性)毫无疑问是过早优化的一种形式,而能有多少业务场景会有 Google 量级的数据非要分布式数据库不可,仍然是一个问号。


TPC-H分析性能

TP不行,AP来凑。尽管分布式数据库在 TP 领域如此拉胯,但数据分析 AP 才是分布式数据库的基本盘,因此很多分布式数据库喜欢炒作 HTAP 的概念。而衡量 AP 系统的能力,我们会用到 TPC-H 测试。

TPC-H 是一个模拟数仓,包含8张数据表,与22条复杂分析类SQL。衡量分析性能的标准通常是在指定仓数下执行这22条SQL的耗时。通常使用100仓,约100GB数据作为基准。我们在本地笔记本和小型AWS云服务器进行了 TPC-H 1,10,50,100 仓的测试,完成全部22个查询,耗时结果如下:

Scale Factor Time (s) CPU Environment Comment
1 8 10 10C / 64G apple m1 max
10 56 10 10C / 64G apple m1 max
50 1327 10 10C / 64G apple m1 max
100 4835 10 10C / 64G apple m1 max
1 13.5 8 8C / 64G z1d.2xlarge
10 133 8 8C / 64G z1d.2xlarge

作为横向对比,我们选取了一些其他数据库官网或比较详细的第三方测评结果。不过在对比前,有几点需要注意:一是有一些数据库产品仓数并非100,二来硬件规格也不尽相同,三来并不是所有数据库评测结果都来自原厂,因此只能作为大致的对照和参考

Database Time S CPU QPH Environment Source
PostgreSQL 8 1 10 45.0 10C / 64G M1 Max Vonng
PostgreSQL 56 10 10 64.3 10C / 64G M1 Max Vonng
PostgreSQL 1327 50 10 13.6 10C / 64G M1 Max Vonng
PostgreSQL 4835 100 10 7.4 10C / 64G M1 Max Vonng
PostgreSQL 13.51 1 8 33.3 8C / 64G z1d.2xlarge Vonng
PostgreSQL 133.35 10 8 33.7 8C / 64G z1d.2xlarge Vonng
TiDB 190 100 120 15.8 120C / 570G TiDB
Spark 388 100 120 7.7 120C / 570G TiDB
Greenplum 436 100 288 2.9 120C / 570G TiDB
DeepGreen 148 200 256 19.0 288C / 1152G Digoal
MatrixDB 2306 1000 256 6.1 256C / 1024G MXDB
Hive 59599 1000 256 0.2 256C / 1024G MXDB
StoneDB 3388 100 64 1.7 64C / 128G StoneDB
ClickHouse 11537 100 64 0.5 64C / 128G StoneDB
OceanBase 189 100 96 19.8 96C / 384G OceanBase
PolarDB 387 50 32 14.5 32C / 128G 阿里云
PolarDB 755 50 16 14.9 16C / 64G 阿里云

为了便于衡量,我们可以归一化核数与仓数,用 QPH ,即每小时,每核,执行1仓 TPC-H 查询可以执行多少轮,来近似评估数据库的相对分析性能。

pg-performence-12.png

QPH = (1 / 时长) * (仓数 / 核数) * 3600

22个查询耗时对于不同仓数来说并非完全线性关系,因此只可作为近似参考。

不过总体来说,即使是 10 核的笔记本跑 PostgreSQL,也可以有相当亮眼的分析成绩来

(注:50C以上已经超过内存,走SWAP与磁盘IO了)。

pg-performence-13.png

图:论文《how good is my HTAP system》提出的评测 HTAP系统能力的方法 —— 吞吐量前沿,在AP/TP二维平面上画出混合负载的吞吐量极值。

至少在百GB级的表上,PostgreSQL足以称得上是一款表现优秀的分析数据库。如果单表超过几TB量级,也可以平滑升级至 Greenplum / MatrixDB / DeepGreen 等 PostgreSQL 兼容MPP数仓。。采用主从复制的 PostgreSQL 可以通过级联从库的方式近乎无限地 Scale 读负载,采用逻辑复制的 PostgreSQL 可以内置/同步地完成AP模式ETL,可谓是真正的 HTAP 数据库。

综上所述,PostgreSQL 在 TP 领域表现极其亮眼,在 AP 领域表现可圈可点。这也难怪在最近几年的 StackOverflow 开发者年度调研中, PostgreSQL 成为了 专业开发者最常用,最受喜爱,最想要的三冠王数据库

pg-performence-14.png

StackOverflow 近六年数据库开发者调研结果


参考

[1] Vonng: PGTPC

[2] WHY MYSQL

[3] MySQL Performance : 1M IO-bound QPS with 8.0 GA on Intel Optane SSD !

[4] MySQL Performance : 8.0 and Sysbench OLTP_RW / Update-NoKEY

[5] MySQL Performance : The New InnoDB Double Write Buffer in Action

[6] TiDB Sysbench Performance Test Report – v6.1.0 vs. v6.0.0

[7] OceanBase 3.1 Sysbench 性能测试报告

[8] Cockroach 22.15 Benchmarking Overview

[9] Benchmark YSQL performance using sysbench (v2.15)

[10] PolarDB-X 1.0 Sysbench 测试说明

[11] StoneDB OLAP TCP-H测试报告

[12] Elena Milkai: “How Good is My HTAP System?",SIGMOD ’22 Session 25

[13] AWS Calculator

为什么PostgreSQL是最成功的数据库?

当我们说一个数据库"成功"时,到底在说什么?是指功能性能易用性,还是成本生态复杂度?评价指标有很多,但这件事最终还得由用户来定夺。

数据库的用户是开发者,而开发者的意愿、喜好、选择又如何?StackOverflow 连续六年,向来自180个国家的七万多开发者问了这三个问题。

总览这六年的调研结果,不难看出在2022年,PostgreSQL 已经同时在这三项上登顶夺冠,成了字面意义上 “最成功的数据库”:

  • PostgreSQL 成为 专业开发者最常使用的数据库!(Used)
  • PostgreSQL 成为 开发者最为喜爱的数据库!(Loved)
  • PostgreSQL 成为开发者最想要用的数据库!(Wanted)

流行度反映当年势能,需求度预示来年动能,喜爱度代表长期潜能。时与势都站在 PostgreSQL 一侧,让我们来看一看更具体的数据与结果。


最流行

PostgreSQL —— 专业开发者中最流行的数据库!(Used)

第一项调研,是关于开发者目前使用着什么样的数据库,即,流行度

过去几年,MySQL一直霸占着数据库流行榜的榜首,很符合其 ”世界上最流行的开源关系型数据库“ 这一口号。不过这一次,”最流行“的桂冠恐怕要让给 PostgreSQL 了。

专业开发者中,PostgreSQL 以 46.5% 的使用率第一次超过 MySQL 位居第一,而 MySQL 以 45.7% 的使用率降至第二名。 同为泛用性最好的开源关系型数据库,排名第一第二的 PGSQL 与 MySQL ,与其他的数据库远远拉开了距离。

TOP 9 数据库流行度演变(2017-2022)

PGSQL 与 MySQL 的流行度差别并不大。值得一提的是,在见习开发者群体中,MySQL 仍然占据显著使用率优势(58.4%),如果算上见习开发者,MySQL 甚至仍然保有 3.3% 的微弱整体领先优势。

但从下图中不难看出,PostgreSQL 有显著的增长动能,而其他数据库,特别是 MySQL、 SQL Server、Oracle 的使用率则在最近几年持续衰退。随着时间的推移,PostgreSQL 的领先优势将进一步拉大。

四大关系型数据库流行度对比

流行度反映的是当下数据库的规模势能,而喜爱度反映的是未来数据库的增长潜能。


最喜爱

PostgreSQL —— 开发者最为喜爱的数据库!(Loved)

第二个问题是关于开发者喜爱什么数据库,讨厌什么数据库。在此项调研中,PostgreSQL与Redis一骑绝尘,以70%+ 的喜爱率高居榜首,显著甩开其他数据库。

在过去几年,Redis一直是用户最喜欢的数据库。在 2022 年,形势发生了变化,PostgreSQL 第一次超过 Redis,成为最受开发者喜爱的数据库。 Redis是简单易用的数据结构缓存服务器,经常会与关系型数据库搭配使用,广受开发者喜爱。不过开发者明显更爱功能强大得多的 PostgreSQL 多一丢丢。

相比之下 MySQL 与 Oracle 的表现就比较拉胯了。喜欢和讨厌 MySQL 的人基本各占一半;而只有35%的用户喜欢 Oracle ,这也意味着近 2/3 的开发者反感 Oracle 。

TOP 9 数据库喜爱度演变(2017-2022)

从逻辑上讲,用户的喜爱将导致软件的流行,用户的厌恶将导致软件过气。 我们可以参照 净推荐指数(NPS,又称口碑,推荐者%-贬损者%)的构造方式, 设计一个净喜爱指数 NLS:即 喜爱人群% - 厌恶人群%, 而数据库流行度的导数应当与 NLS 呈现正相关性 。

数据很好的印证了这一点: PGSQL 有着全场最高的 NLS: 44% ,对应着最高的流行度增长率 每年 460个基点。 MySQL 的口碑刚好落在褒贬线上方 (2.3%),流行度平均增速为36个基点; 而 Oracle 的口碑则为负的 29%,对应平均每年44个基点的使用率负增长。 当然在这份榜单上, Oracle 只是倒数第三惨的,最不受人待见的是 IBM DB2 : 1/4的人喜欢,3/4的人讨厌,NLS = -48% ,对应46个基点的年平均衰退。

当然,并不是所有潜能,都可以转换为实打实的动能。 用户的喜爱并不一定会付诸行动,而这就是第三项调研所要回答的问题。


最想要

PostgreSQL —— 开发者 最想使用的数据库!(Wanted)

“在过去的一年中,你在哪些数据库环境中进行了大量开发工作?在未来一年,你想在哪些数据库环境中工作? ”

对于这个问题前半段的回答,引出了”最流行“数据库的调研结果;而后半段,则给出了”最想要“这个问题的答案。 如果说用户的喜爱代表的是未来增长的潜能,那么用户的需求(想要,Want)就代表了下一年实打实的增长动能。

在今年的调研中, PostgreSQL 毫不客气的挤开 MongoDB ,占据了开发者最想使用数据库的宝座。 高达 19% 的受访者表示,下一年中想要使用 PostgreSQL 环境进行开发。 紧随其后的是 MongoDB (17%) 与 Redis (14%),这三种数据库的需求程度与其他数据库显著拉开了一个台阶。

此前, MongoDB 一直占据”最想要“数据库榜首,但最近开始出现过气乏力的态势。 原因是多方面的:例如,MongoDB 本身也受到了 PostgreSQL 的冲击。 PostgreSQL 本身就包含了完整的 JSON 特性,可直接用作文档数据库,更有类似 FerretDB (原名 MangoDB)的项目可以直接在 PG 上对外提供 MongoDB 的 API。

MongoDB 与 Redis 都是 NoSQL 运动的主力军。但与 MongoDB 不同,Redis的需求在不断增长。PostgreSQL 与 Redis,分别作为 SQL 与 NoSQL 的领军者,保持着旺盛的需求与高速的增长,前途无量。


为什么?

PostgreSQL 在需求率, 使用率,喜爱率上都拔得头筹,天时地利人和齐备,动能势能潜能都有,足以称得起是最成功的数据库了。

但我们想知道的是,为什么 PostgreSQL 会如此成功 ?

其实,秘密就藏在它的 Slogan 里: ”世界上最先进开源 关系型数据库“。


关系型数据库

关系型数据库是如此的普及与重要,也许其他的数据库品类如键值,文档,搜索引擎,时序,图,向量加起来也比不上它的一个零头。以至于当大家谈起数据库时,如果没有特殊说明,默认隐指的就是”关系型数据库“。在它面前,没有其他数据库品类敢称自己为”主流“。

DB-Engine 为例,DB-Engine的排名标准包括搜索系统名称时的搜索引擎结果数,Google趋势,Stack Overflow讨论,Indeed 提及系统的工作机会,LinkedIn等专业网络中的个人资料数,Twitter等社交网络中的提及数等,可理解为数据库的“综合热度”。

数据库热度趋势:https://db-engines.com/en/ranking_trend

在 DB-Engine 的热度趋势图中我们可以看到一条鸿沟,前四名全都是 关系型数据库 ,加上排名第五的 MongoDB,与其他数据库在热度上拉开了 数量级上的差距。 我们只需要把关注点聚焦到这四种核心的关系型数据库 Oracle,MySQL,SQL Server,PostgreSQL 上即可。

关系型数据库的生态位高度重叠,其关系可以视作零和博弈。抛开微软生态关门自嗨相对独立的商业数据库 SQL Server不提。在关系型数据库世界里,上演的是一场三国演义。

Oracle有才无德,MySQL才浅德薄,唯有PostgreSQL德才兼备。

Oracle是老牌商业数据库,有着深厚的历史技术积淀,功能丰富,支持完善。稳坐数据库头把交椅,广受不差钱且需要背锅侠的企业喜爱。但Oracle费用昂贵,且以讼棍行径成为知名的业界毒瘤。Microsoft SQL Server性质与Oracle类似,都属于商业数据库。商业数据库整体受开源数据库冲击,处于缓慢衰退的状态。

MySQL流行度位居第二,但树大招风,处于前狼后虎,上有野爹下有逆子的不利境地:在严谨的事务处理和数据分析上,MySQL被同为开源生态位的PostgreSQL甩开几条街;而在糙猛快的敏捷方法论上,MySQL又不如新兴NoSQL好用;同时 MySQL 上有养父 Oracle 压制,中有兄弟 MariaDB 分家,下有诸如逆子 TiDB 等协议兼容NewSQL分羹,因此也在走下坡路。

作为老牌商业数据库,Oracle的毋庸质疑,但其作为业界毒瘤,“” ,亦不必多说,故曰:“有才无德”。MySQL 虽有开源之功德,奈何认贼作父;且才疏学浅,功能简陋,只能干干CRUD,故曰“才浅德薄”。唯有PostgreSQL,德才兼备,既占据了开源崛起之天时,又把握住功能先进之地利,还有着宽松BSD协议之人和。正所谓:藏器于身,因时而动。不鸣则已,一鸣惊人,一举夺冠!

而 PostgreSQL 德以致胜的秘密,就是 先进开源


开源之德

PG的“德”在于开源。祖师爷级的开源项目,全世界开发者群策群力的伟大成果。

协议友善BSD,生态繁荣扩展多。开枝散叶,子孙满堂,Oracle替代扛旗者

什么叫“德”,合乎于“道”的表现就是德。而这条“道”就是开源

PostgreSQL是历史悠久的祖师爷级开源项目,更是全世界开发者群策群力的典范成果。

生态繁荣,扩展丰富,开枝散叶,子孙满堂

很久很久以前,开发软件/信息服务需要使用非常昂贵的商业数据库软件:例如Oracle与SQL Server:单花在软件授权上的费用可能就有六七位数,加之相近的硬件成本与服务订阅成本。Oracle一个 CPU 核一年的软件授权费用便高达十几万,即使壕如阿里也吃不消要去IOE。以 PostgreSQL / MySQL 为代表的的开源数据库崛起,让用户有了一个新选择:软件不要钱。“不要钱” 的开源数据库可以让我们自由随意地使用数据库软件,而这一点深刻影响了行业的发展:从接近一万¥/ 核·月的商业数据库,到20块钱/核·月的纯硬件成本。数据库走入寻常企业中,让免费提供信息服务成为可能。

开源是有大功的。互联网的历史就是开源软件的历史,IT行业之所以有今天的繁荣,人们能享受到如此多的免费信息服务,核心原因之一就是开源软件。开源是一种真正成功的,以软件自由为目的,由开发者构成的 Communism(社区主义):软件这种IT业的核心生产资料变为全世界开发者公有,按需分配。开发者各尽所能,人人为我,我为人人。

一个开源程序员工作时,其劳动背后可能蕴含的是数以万计顶尖开发者的智慧结晶。程序员薪资高从原理上来说是因为,开发者本质上不是一个简单的工人,而是一个指挥软件和硬件干活的包工头。程序员自己就是核心生产资料;软件来自公有社区;服务器硬件更是唾手可得;因此一个或几个高级的软件工程师,就可以很轻松的利用开源生态快速解决领域问题。

通过开源,所有社区开发者形成合力,极大降低了重复造轮子的内耗。使得整个行业的技术水平以匪夷所思的速度向前迈进。开源的势头就像滚雪球,时至今日已经势不可挡。基本上除了一些特殊场景和路径依赖,软件开发中闭门造车搞自力更生几乎成了一个大笑话。

越是底层基础的软件,开源便越占优势。开源,也是 PostgreSQL 对阵 Oracle 的最大底气所在。

Oracle 先进,但 PostgreSQL 也不差。PostgreSQL 是 Oracle 兼容性最好的开源数据库,原生即支持 Oracle 85% 的功能,更有 96% 功能兼容的专业发行版。但更重要的是,Oracle价格高昂,而PG开源免费。压倒性的成本优势让PG拥有了巨大的生态位基础:它不一定要在功能先进性上超过 Oracle 才能成功 ,廉价9成正确已经足以干翻 Oracle 。

PostgreSQL 可以视作一个开源版的“Oracle”,是唯一能真正威胁到 Oracle 的数据库。作为 ”去O“ 抗旗者,PG 可谓子孙满堂, 36% 的 “国产数据库” 更是直接基于PG “开发”,养活了一大批 自主可控 的 数据库公司,可谓功德无量。更重要的是,PostgreSQL 社区并不反对这样的行为,BSD 协议允许这样做。这样开放的胸襟,是被Oracle收购的,使用GPL协议的MySQL所难以相比的。


先进之才

PG的“才”在于先进。一专多长的全栈数据库,一个打十个,天生就是 HTAP。

时空地理分布式,时序文档超融合,单一组件即可覆盖几乎所有数据库需求。

PG的“才”在于一专多长。PostgreSQL是一专多长的全栈数据库,天生就是HTAP,超融合数据库,一个打十个。基本单一组件便足以覆盖中小型企业绝大多数的数据库需求:OLTP,OLAP,时序数据库,空间GIS,全文检索,JSON/XML,图数据库,缓存,等等等等。

PostgreSQL是各种关系型数据库中性价比最高的选择:它不仅可以用来做传统的CRUD OLTP业务,数据分析更是它的拿手好戏。各种特色功能更是提供了切入多种行业以的契机:基于PostGIS的地理时空数据处理分析,基于Timescale的时序金融物联网数据处理分析,基于Pipeline存储过程触发器的流式处理,基于倒排索引全文检索的搜索引擎,FDW对接统一各式各样的外部数据源。可以说,PG是真正一专多长的全栈数据库,它可以实现的比单纯OLTP数据库要丰富得多的功能。

在一个很可观的规模内,PostgreSQL都可以独立扮演多面手的角色,一个组件当多种组件使。而单一数据组件选型可以极大地削减项目额外复杂度,这意味着能节省很多成本。它让十个人才能搞定的事,变成一个人就能搞定的事。 不是说PG要一个打十个把其他数据库的饭碗都掀翻:专业组件在专业领域的实力是毋庸置疑的。但切莫忘记,为了不需要的规模而设计是白费功夫,这属于过早优化的一种形式。如果真有那么一样技术可以满足你所有的需求,那么使用该技术就是最佳选择,而不是试图用多个组件来重新实现它。

以探探为例,在 250w TPS与 200TB 数据的量级下,单一PostgreSQL选型依然能稳定可靠地撑起业务。能在很可观的规模内做到一专多长,除了本职的OLTP,PG 还在相当长的时间里兼任了缓存,OLAP,批处理,甚至消息队列的角色。当然神龟虽寿,犹有竟时。最终这些兼职功能还是要逐渐分拆出去由专用组件负责,但那已经是近千万日活时的事了。

vs MySQL

PostgreSQL 的先进性有目共睹,这也是其对阵同为开源关系型数据库的老对手 —— MySQL 时,真正的核心竞争力。

MySQL的口号是“世界上最流行的开源关系型数据库”,它的核心特点是糙猛快,用户基本盘是互联网。互联网公司的典型特点是什么?追逐潮流糙猛快说的是互联网公司业务场景简单(CRUD居多);数据重要性不高,不像传统行业(例如银行)那样在意数据的一致性与正确性;可用性优先,相比停服务更能容忍数据丢乱错,而一些传统行业宁可停止服务也不能让账目出错。 说的则是互联网行业数据量大,它们需要的就是水泥槽罐车做海量CRUD,而不是高铁和载人飞船。 说的则是互联网行业需求变化多端,出活周期短,要求响应时间快,大量需求的就是开箱即用的软件全家桶(如LAMP)和简单培训就能上手干活的CRUD Boy。于是,糙猛快的互联网公司和糙猛快的MySQL一拍即合。

但时过境迁,PostgreSQL 进步神速,在”快“与”猛“上 MySQL 已经不占优了,现在能拿出手的只剩下”糙“了。举个例子,MySQL 的哲学可以称之为:“好死不如赖活着”,与 “我死后哪管洪水滔天”。 其“糙”体现在各种“容错”上,例如允许呆瓜程序员写出的错误的SQL也能跑起来。最离谱的例子就是MySQL竟然允许部分成功的事务提交,这就违背了关系型数据库的基本约束:原子性与数据一致性

图:MySQL默认竟然允许部分成功的事务提交

先进的因会反映为流行的果,流行的东西因为落后而过气,而先进的东西会因为先进变得流行。时代所赋予的红利,也会随时代过去而退潮。在这个变革的时代中,没有先进的功能打底,“流行”也也难以长久。在先进性上, PostgreSQL 丰富的功能已经甩开 MySQL 了几条街,而 MySQL 引以为豪的 ”流行度“ 也开始被 PostgreSQL 反超。

大势所趋,大局已定。正所谓:时来天地皆同力,运去英雄不自由。先进与开源,就是 PostgreSQL 最大的两样杀手锏。Oracle 先进, MySQL 开源,PostgreSQL 先进又开源。天时地利人和齐备,何愁大业不成?


展望未来

软件吞噬世界, 开源吞噬软件,而云吞噬开源。

看上去,数据库之争已经尘埃落定,一段时间内大概不会有其他数据库内核能威胁到 PostgreSQL 了。 但对 PostgreSQL 开源社区 真正的威胁,已经不再是其他数据库内核,而是软件使用范式的嬗变:云出现了。

最初,大家开发软件/信息服务需要使用昂贵的商业软件( Oracle,SQL Server,Unix)。而随着 Linux / PostgreSQL 这些开源软件的兴起,用户们有了新的选择。开源软件确实免费不要钱,但想用好开源软件,是一件门槛很高的事情,用户不得不雇佣开源软件专家来帮助自己用好开源软件。

当数据库上了规模,雇佣开源DBA自建始终是合算的,只是好DBA太稀缺了。

这便是开源的核心模式:开源软件开发者给开源软件做贡献;开源软件通过好用免费吸引大量用户;用户在使用开源软件时产生需求,创造更多开源软件相关就业岗位,创造更多的开源软件开发者。 这三步形成了一个正反馈循环:更多的开源贡献者让开源软件更好用,更省钱,从而吸引更多用户,并创造出更多的开源贡献者。开源生态的繁荣有赖于这个闭环,而公有云厂商的出现打破了这个循环。

公有云厂商将开源数据库套上壳,加上自己的硬件与管控软件,雇佣共享DBA提供支持,便成了云数据库。诚然这是一项很有价值的服务,但云厂商将开源软件放在自家的云平台售卖而鲜有回馈,实质上是一种通过“搭便车”吸血开源的行为。 这样的共享外包模式将导致开源软件的岗位向云厂商集中,最终形成少数巨头做大垄断,伤害到所有用户的软件自由。

世界已经被云改变了,闭源软件早已不是最重要的问题了。

在 2020 年,计算自由的敌人是云计算软件”。

这是 DDIA 作者 Martin Kleppmann 在其“本地优先软件”运动中提出的 宣言。云软件指的是运行在供应商服务器上的软件,例如:Google Docs、Trello、Slack、Figma、Notion 。以及最核心的云软件,云数据库

后云时代,开源社区如何应对云软件的挑战?Cloud Native 运动给出了答案。这是一场从公有云夺回软件自由的伟大运动,而数据库,则是其中的核心焦点。

Cloud Native 全景图,还缺少最后一块拼图:有状态的数据库!

这也是我们做 开箱即用的开源PostgreSQL 数据库发行版 —— Pigsty 想要解决的问题:做一个用户在本地即可使用的RDS服务,成为云数据库的开源替代!

Pigsty 带有开箱即用的 RDS / PaaS / SaaS 整合;一个无可比拟的PG监控系统与自动驾驶的高可用集群架构方案;一键安装部署,并提供 Database as Code 的易用体验;在体验比肩甚至超越云数据库的前提下,数据自主可控且成本减少 50% ~ 90%。我们希望它能极大降低 PostgreSQL 使用的门槛,让更多用户可以用 好数据库用好 数据库。

当然,限于篇幅,云数据库与后云时代的数据库未来,就是下一篇文章要介绍的故事了。

开箱即用的PG发行版:Pigsty

什么是Pigsty

Pigsty是开箱即用的生产级开源PostgreSQL发行版

所谓发行版(Distribution),指的是由数据库内核及其一组软件包组成的数据库整体解决方案。例如,Linux是一个操作系统内核,而RedHat,Debian,SUSE则是基于此内核的操作系统发行版。PostgreSQL是一个数据库内核,而Pigsty,BigSQL,Percona,各种云RDS,换皮数据库则是基于此内核的数据库发行版

Pigsty区别于其他数据库发行版的五个核心特性为:

  • 全面专业监控系统
  • 稳定可靠部署方案
  • 简单省心的用户界面
  • 灵活开放扩展机制
  • 免费友好开源协议

这五个特性,使得Pigsty真正成为开箱即用的PostgreSQL发行版。

谁会感兴趣?

Pigsty面向的用户群体包括:DBA,架构师,OPS,软件厂商、云厂商、业务研发、内核研发、数据研发;对数据分析与数据可视化感兴趣的人;学生,新手程序员,有兴趣尝试数据库的用户。

对于DBA,架构师等专业用户,Pigsty提供了独一无二的专业级PostgreSQL监控系统,为数据库管理提供不可替代的价值点。与此同时,Pigsty还带有一个稳定可靠,久经考验的生产级PostgreSQL部署方案,可在生产环境中自动部署带有监控报警,日志采集,服务发现,连接池,负载均衡,VIP,以及高可用的PostgreSQL数据库集群。

对于研发人员(业务研发、内核研发、数据研发),学生,新手程序员,有兴趣尝试数据库的用户,Pigsty提供了门槛极低,一键拉起,一键安装本地沙箱。本地沙箱除机器规格外与生产环境完全一致,包含完整的功能:带有开箱即用的数据库实例与监控系统。可用于学习,开发,测试,数据分析等场景。

此外,Pigsty提供了一种称为“Datalet”的灵活扩展机制 。对数据分析与数据可视化感兴趣的人可能会惊讶地发现,Pigsty还可以作为数据分析与可视化的集成开发环境。Pigsty集成了PostgreSQL与常用的数据分析插件,并带有Grafana和内嵌的Echarts支持,允许用户编写,测试,分发数据小应用(Datalet)。如:“Pigsty监控系统的额外扩展面板包”,“Redis监控系统”,“PG日志分析系统”,“应用监控”,“数据目录浏览器”等。

最后,Pigsty采用了免费友好的Apache License 2.0,可以免费用于商业目的。只要遵守Apache 2 License的显著声明条款,也欢迎云厂商与软件厂商集成与二次研发商用


全面专业的监控系统

You can’t manage what you don’t measure.

— Peter F.Drucker

Pigsty提供专业级监控系统,面向专业用户提供不可替代的价值点。

以医疗器械类比,普通监控系统类似于心率计、血氧计,普通人无需学习也可以上手。它可以给出患者生命体征核心指标:起码用户可以知道人是不是要死了,但对于看病治病无能为力。例如,各种云厂商软件厂商提供的监控系统大抵属于此类:十几个核心指标,告诉你数据库是不是还活着,让人大致有个数,仅此而已。

专业级监控系统则类似于CT,核磁共振仪,可以检测出对象内部的全部细节,专业的医师可以根据CT/MRI报告快速定位疾病与隐患:有病治病,没病健体。Pigsty可以深入审视每一个数据库中的每一张表,每一个索引,每一个查询,提供巨细无遗的全面指标(1155类),并通过几千个仪表盘将其转换为洞察:将故障扼杀在萌芽状态,并为性能优化提供实时反馈

Pigsty监控系统基于业内最佳实践,采用Prometheus、Grafana作为监控基础设施。开源开放,定制便利,可复用,可移植,没有厂商锁定。可与各类已有数据库实例集成。


稳定可靠的部署方案

A complex system that works is invariably found to have evolved from a simple system that works.

—John Gall, Systemantics (1975)

数据库是管理数据的软件,管控系统是管理数据库的软件。

Pigsty内置了一套以Ansible为核心的数据库管控方案。并基于此封装了命令行工具与图形界面。它集成了数据库管理中的核心功能:包括数据库集群的创建,销毁,扩缩容;用户、数据库、服务的创建等。Pigsty采纳“Infra as Code”的设计哲学使用了声明式配置,通过大量可选的配置选项对数据库与运行环境进行描述与定制,并通过幂等的预置剧本自动创建所需的数据库集群,提供近似私有云般的使用体验。

Pigsty创建的数据库集群是分布式高可用的数据库集群。Pigsty创建的数据库基于DCS、Patroni、Haproxy实现了高可用。数据库集群中的每个数据库实例在使用上都是幂等的,任意实例都可以通过内建负载均衡组件提供完整的读写服务,提供分布式数据库的使用体验。数据库集群可以自动进行故障检测与主从切换,普通故障能在几秒到几十秒内自愈,且期间只读流量不受影响。故障时。集群中只要有任意实例存活,就可以对外提供完整的服务。

Pigsty的架构方案经过审慎的设计与评估,着眼于以最小复杂度实现所需功能。该方案经过长时间,大规模的生产环境验证,已经被互联网/B/G/M/F多个行业内的组织所使用。


简单省心的用户界面

Pigsty旨在降低PostgreSQL的使用门槛,因此在易用性上做了大量工作。

安装部署

Someone told me that each equation I included in the book would halve the sales.

— Stephen Hawking

Pigsty的部署分为三步:下载源码,配置环境,执行安装,均可通过一行命令完成。遵循经典的软件安装模式,并提供了配置向导。您需要准备的只是一台CentOS7.8机器及其root权限。管理新节点时,Pigsty基于Ansible通过ssh发起管理,无需安装Agent,即使是新手也可以轻松完成部署。

Pigsty既可以在生产环境中管理成百上千个高规格的生产节点,也可以独立运行于本地1核1GB虚拟机中,作为开箱即用的数据库实例使用。在本地计算机上使用时,Pigsty提供基于Vagrant与Virtualbox的沙箱。可以一键拉起与生产环境一致的数据库环境,用于学习,开发,测试数据分析,数据可视化等场景。

用户接口

Clearly, we must break away from the sequential and not limit the computers. We must state definitions and provide for priorities and descriptions of data. We must state relation‐ ships, not procedures.

—Grace Murray Hopper, Management and the Computer of the Future (1962)

Pigsty吸纳了Kubernetes架构设计中的精髓,采用声明式的配置方式与幂等的操作剧本。用户只需要描述“自己想要什么样的数据库”,而无需关心Pigsty如何去创建它,修改它。Pigsty会根据用户的配置文件清单,在几分钟内从裸机节点上创造出所需的数据库集群。

在管理与使用上,Pigsty提供了不同层次的用户界面,以满足不同用户的需求。新手用户可以使用一键拉起的本地沙箱与图形用户界面,而开发者则可以选择使用pigsty-cli命令行工具与配置文件的方式进行管理。经验丰富的DBA、运维与架构师则可以直接通过Ansible原语对执行的任务进行精细控制。

灵活开放的扩展机制

PostgreSQL的 可扩展性(Extensible) 一直为人所称道,各种各样的扩展插件让PostgreSQL成为了最先进的开源关系型数据库。Pigsty亦尊重这一价值,提供了一种名为“Datalet”的扩展机制,允许用户和开发者对Pigsty进行进一步的定制,将其用到“意想不到”的地方,例如:数据分析与可视化。

当我们拥有监控系统与管控方案后,也就拥有了开箱即用的可视化平台Grafana与功能强大的数据库PostgreSQL。这样的组合拥有强大的威力 —— 特别是对于数据密集型应用而言。用户可以在无需编写前后端代码的情况下,进行数据分析与数据可视化,制作带有丰富交互的数据应用原型,甚至应用本身。

Pigsty集成了Echarts,以及常用地图底图等,可以方便地实现高级可视化需求。比起Julia,Matlab,R这样的传统科学计算语言/绘图库而言,PG + Grafana + Echarts的组合允许您以极低的成本制作出可分享可交付标准化的数据应用或可视化作品。

Pigsty监控系统本身就是Datalet的典范:所有Pigsty高级专题监控面板都会以Datalet的方式发布。Pigsty也自带了一些有趣的Datalet案例:Redis监控系统,新冠疫情数据分析,七普人口数据分析,PG日志挖掘等。后续还会添加更多的开箱即用的Datalet,不断扩充Pigsty的功能与应用场景。


免费友好的开源协议

Once open source gets good enough, competing with it would be insane.

Larry Ellison —— Oracle CEO

在软件行业,开源是一种大趋势,互联网的历史就是开源软件的历史,IT行业之所以有今天的繁荣,人们能享受到如此多的免费信息服务,核心原因之一就是开源软件。开源是一种真正成功的,由开发者构成的communism(译成社区主义会更贴切):软件这种IT业的核心生产资料变为全世界开发者公有,人人为我,我为人人。

一个开源程序员工作时,其劳动背后其实可能蕴含有数以万计的顶尖开发者的智慧结晶。通过开源,所有社区开发者形成合力,极大降低了重复造轮子的内耗。使得整个行业的技术水平以匪夷所思的速度向前迈进。开源的势头就像滚雪球,时至今日已经势不可挡。除了一些特殊场景和路径依赖,软件开发中闭门造车搞自力更生已经成了一个大笑话。

依托开源,回馈开源。Pigsty采用了友好的Apache License 2.0,可以免费用于商业目的只要遵守Apache 2 License的显著声明条款,也欢迎云厂商与软件厂商集成与二次研发商用


关于Pigsty

A system cannot be successful if it is too strongly influenced by a single person. Once the initial design is complete and fairly robust, the real test begins as people with many different viewpoints undertake their own experiments. — Donald Knuth

Pigsty围绕开源数据库PostgreSQL而构建,PostgreSQL是世界上最先进的开源关系型数据库,而Pigsty的目标就是:做最好用的开源PostgreSQL发行版

在最开始时,Pigsty并没有这么宏大的目标。因为在市面上找不到任何满足我自己需求的监控系统,因此我只好自己动手,丰衣足食,给自己做了一个监控系统。没有想到它的效果出乎意料的好,有不少外部组织PG用户希望能用上。紧接着,监控系统的部署与交付成了一个问题,于是又将数据库部署管控的部分加了进去;在生产环境应用后,研发希望能在本地也有用于测试的沙箱环境,于是又有了本地沙箱;有用户反馈ansible不太好用,于是就有了封装命令的pigsty-cli命令行工具;有用户希望可以通过UI编辑配置文件,于是就有了Pigsty GUI。就这样,需求越来越多,功能也越来越丰富,Pigsty也在长时间的打磨中变得更加完善,已经远远超出了最初的预期。

做这件事本身也是一种挑战,做一个发行版有点类似于做一个RedHat,做一个SUSE,做一个“RDS产品”。通常只有一定规模的专业公司与团队才会去尝试。但我就是想试试,一个人可不可以?实际上除了慢一点,也没什么不可以。一个人在产品经理、开发者,终端用户的角色之间转换是很有趣的体验,而“Eat dog food”最大的好处就是,你自己既是开发者也是用户,你了解自己需要什么,也不会在自己的需求上偷懒。

不过,正如高德纳所说:“带有太强个人色彩的系统无法成功”。 要想让Pigsty成为一个具有旺盛生命力的项目,就必须开源,让更多的人用起来。“当最初的设计完成并足够稳定后,各式各样的用户以自己的方式去使用它时,真正的挑战才刚刚开始”。

Pigsty很好的解决了我自己的问题与需求,现在我希望它可以帮助到更多的人,并让PostgreSQL的生态更加繁荣,更加多彩。

为什么PostgreSQL前途无量?

最近做的事儿都围绕着PostgreSQL生态,因为我一直觉得这是一个前途无量的方向。

为什么这么说?因为数据库是信息系统的核心组件,关系型数据库是数据库中的绝对主力,而PostgreSQL是世界上最先进的开源关系型数据库。占据天时地利,何愁大业不成?

做一件事最重要的就是认清形势,时来天地皆同力,运去英雄不自由。


天下大势

今天下三分,然Oracle | MySQL | SQL Server 疲敝,日薄西山。PostgreSQL紧随其后,如日中天。前四的数据库中,前三者都在走下坡路,唯有PG增长势头不减,此消彼长,前途无量。

DB-Engine 数据库流行度趋势 (注意这是对数坐标系)

在唯二两个头部开源关系型数据库 MySQL & PgSQL 中,MySQL (2nd) 虽占上风,但其生态位却在逐渐被PostgreSQL (4th) 和非关系型的文档数据库MongoDB (5th) 抢占。按照现在的势头,几年后PostgreSQL的流行度即将跻身前三,与Oracle、MySQL分庭抗礼。


竞争关系

关系型数据库的生态位高度重叠,其关系可以视作零和博弈。与PostgreSQL形成直接竞争关系的,就是OracleMySQL

Oracle流行度位居第一,是老牌商业数据库,有着深厚的历史技术积淀,功能丰富,支持完善。稳坐数据库头把交椅,广受不差钱的企业组织喜爱。但Oracle费用昂贵,且以讼棍行径成为知名的业界毒瘤。排名第三的SQL Server属于相对独立的微软生态,性质上与Oracle类似,都属于商业数据库。商业数据库整体受开源数据库冲击,流行度处于缓慢衰减的状态。

MySQL流行度位居第二,但树大招风,处于前有狼后有虎,上有野爹下有逆子的不利境地:在严谨的事务处理和数据分析上,MySQL被同为开源关系型数据库的PgSQL甩开几条街;而在糙猛快的敏捷方法论上,MySQL又不如新兴NoSQL。同时,MySQL上有养父Oracle的压制,中有MariaDB分家,下有诸如TiDB,OB之类的兼容性新数据库分羹,因而也止步不前。

唯有PostgreSQL迎头赶上,保持着近乎指数增长的势头。如果说几年前PG的势还是Potential,那么现在Potential已经开始兑现为Impact,开始对竞品构成强力挑战。

而在这场你死我活的斗争中,PostgreSQL占据了三个“”:

  1. 开源软件普及发展,蚕食商业软件市场

    在去IOE与开源浪潮的大背景下,凭借开源生态对商业软件(Oracle)形成压制。

  2. 满足用户日益增长的数据处理功能需求

    凭借地理空间数据的事实标准PostGIS处理立于不败之地,凭借对标Oracle的极为丰富的功能,对MySQL形成技术压制。

  3. 市场份额均值回归的势

    国内PG市场份额因历史原因,远低于世界平均水平,本身蕴含着巨大势能。

Oracle作为老牌商业软件,毋庸质疑,同时作为业界毒瘤,“”也不必多说,故曰:“有才无德”。MySQL有开源之功德,但它一来采用了GPL协议,比起使用无私宽松BSD协议的PgSQL还是差不少意思,二来认贼作父,被Oracle收购,三来才疏学浅,功能简陋,故曰“才浅德薄”。

德不配位,必有灾殃。唯有PostgreSQL,既占据了开源崛起之天时,又把握住功能强劲之地利,还有着宽松BSD协议之人和。正所谓:藏器于身,因时而动。不鸣则已,一鸣惊人。德才兼备,攻守之势易矣!


德才兼备

PostgreSQL的德

PG的“德”在于开源。什么叫“德”,合乎于“道”的表现就是德。而这条“道”就是开源

PG本身就是祖师爷级开源软件,是开源世界中的一颗明珠,是全世界开发者群策群力的成功典范。而且更重要的是它采用无私的BSD协议:除了打着PG的名号招摇撞骗外,基本可以说是百无禁忌:比如换皮改造为国产数据库出售。PG可谓无数数据库厂商们的衣食父母。子孙满堂,活人无数,功德无量。

数据库谱系图,若列出所有PgSQL衍生版,估计可以撑爆这张图

PostgreSQL的才

PG的“才”在于一专多长。PostgreSQL是一专多长的全栈数据库,天生就是HTAP,超融合数据库,一个打十个。基本单一组件便足以覆盖中小型企业绝大多数的数据库需求:OLTP,OLAP,时序数据库,空间GIS,全文检索,JSON/XML,图数据库,缓存,等等等等。

PostgreSQL在一个很可观的规模内都可以独立扮演多面手的角色,一个组件当多种组件使。而单一数据组件选型可以极大地削减项目额外复杂度,这意味着能节省很多成本。它让十个人才能搞定的事,变成一个人就能搞定的事。 如果真有那么一样技术可以满足你所有的需求,那么使用该技术就是最佳选择,而不是试图用多个组件来重新实现它。

参考阅读:PG好处都有啥


开源之德

开源是有大功的。互联网的历史就是开源软件的历史,IT行业之所以有今天的繁荣,人们能享受到如此多的免费信息服务,核心原因之一就是开源软件。开源是一种真正成功的,由开发者构成的communism(译成社区主义会更贴切):软件这种IT业的核心生产资料变为全世界开发者公有,人人为我,我为人人。

一个开源程序员干活时,其劳动背后其实可能蕴含有数以万计的顶尖开发者的智慧结晶。互联网程序员贵,因为从效果上来讲,其实程序员不是一个工人,而是一个指挥软件和机器来干活的包工头。 程序员自己就是核心生产资料,服务器很容易取得(相比其他行业的科研设备与实验环境),软件来自公有社区,一个或几个高级的软件工程师可以很轻松的利用开源生态快速解决领域问题。

通过开源,所有社区开发者形成合力,极大降低了重复造轮子的内耗。使得整个行业的技术水平以匪夷所思的速度向前迈进。开源的势头就像滚雪球,时至今日已经势不可挡。基本上除了一些特殊场景和路径依赖,软件开发中闭门造车搞自力更生几乎成了一个大笑话。

所以说,搞数据库也好,做软件也罢,要搞技术就要搞开源的技术,闭源的东西生命力太弱,没意思。开源之德,也是PgSQL与MySQL对Oracle的最大底气所在。


生态之争

开源的核心就在于生态(ECO),每一个开源技术都有自己的小生态。所谓生态就是各种主体及其环境通过密集相互作用构成的一个系统,而开源软件的生态模式大致可以描述为由以下三个步骤组成的正反馈循环:

  • 开源软件开发者给开源软件做贡献
  • 开源软件本身免费,吸引更多用户
  • 用户使用开源软件,产生需求,创造更多开源软件相关岗位

开源生态的繁荣有赖于这个闭环,而生态系统的规模(用户/开发者数量)与复杂度(用户/开发者质量)直接决定了这个软件的生命力,所以每一个开源软件都有天命去扩大自己的规模。而软件的规模通常取决于软件所占据的生态位,如果不同的软件的生态位重叠,就会发生竞争。在开源关系型数据库的生态位中,PgSQL与MySQL就是最直接的竞争者。


流行 vs 先进

MySQL的口号是“世界上最流行的开源关系型数据库”,而PostgreSQL的Slogan则是“世界上最先进的开源关系型数据库”,一看这就是一对老冤家了。这两个口号很好的反映出了两种产品的特质:PostgreSQL是功能丰富,一致性优先,高大上的严谨的学院派数据库;MySQL是功能粗陋,可用性优先,糙猛快的“工程派”数据库。

MySQL的主要用户群体集中在互联网公司,互联网公司的典型特点是什么?追逐潮流糙猛快说的是互联网公司业务场景简单(CRUD居多);数据重要性不高,不像传统行业(例如银行)那样在意数据的一致性(正确性);可用性优先(相比停服务更能容忍数据丢乱错,而一些传统行业宁可停止服务也不能让账目出错)。 说的则是互联网行业数据量大,它们需要的就是水泥槽罐车,而不是高铁和载人飞船。 说的则是互联网行业需求变化多端,出活周期短,要求响应时间快,大量需求的就是开箱即用的软件全家桶(如LAMP)和简单培训一下就能干活的CRUD Boy。于是糙猛快的互联网公司和糙猛快的MySQL一拍即合。

而PgSQL的用户则更偏向于传统行业,传统行业之所以称为传统行业,就是因为它们已经走过了野蛮生长的阶段,有着成熟的业务模型与深厚的底蕴积淀。它们需要的是正确的结果,稳定的表现,丰富的功能,对数据进行分析加工提炼的能力。所以在传统行业中,往往是Oracle、SQL Server、PostgreSQL的天下。特别是在地理相关的场景中更是有着不可替代的地位。与此同时,不少互联网公司的业务也开始成熟沉淀,已经一只脚迈入“传统行业”了,越来越多的互联网公司脱离了糙猛快的低级循环,将目光投向PostgreSQL 。


谁更正确?

最了解一个人的的往往是他的竞争对手,PostgreSQL与MySQL的口号都很精准地戳中了对手的痛点。PgSQL“最先进”的潜台词就是MySQL太落后,而MySQL”最流行“就是说PgSQL不流行。用户少但先进,用户多但落后。哪一个更”好“?这种价值判断的问题不好回答。

但我认为时间站在 先进 技术的一边:因为先进与落后是技术的核心度量,是因,而流行与否则是果;流行不流行是内因(技术是否先进)和外因(历史路径依赖)共同对时间积分的结果。当下的因会反映为未来的果:流行的东西因为落后而过气,而先进的东西会因为先进变得流行。

虽然很多流行的东西都是垃圾,但流行并不一定代表着落后。如果只是缺少一些功能,MySQL还不至于被称为“落后”。问题在于MySQL已经糙到连事务这种关系型数据库的基本功能都有缺陷,那就不是落后不落后能概括的问题,而是合格不合格的问题了。

ACID

一些作者声称,支持通用的两阶段提交代价太大,会带来性能与可用性的问题。让程序员来处理过度使用事务导致的性能问题,总比缺少事务编程好得多。 ——James Corbett等,Spanner:Google的全球分布式数据库(2012)

在我看来, MySQL的哲学可以称之为:“好死不如赖活着”,以及,“我死后哪管洪水滔天”。 其“可用性”体现在各种“容错”上,例如允许呆瓜程序员写出的错误的SQL查询也能跑起来。最离谱的例子就是MySQL竟然允许部分成功的事务提交,这就违背了关系型数据库的基本约束:原子性与数据一致性

图:MySQL竟然允许部分成功的事务提交

这里在一个事务中插入了两条记录,第一条成功,第二条因为约束失败。根据事务的原子性,整个事务要么整个成功,要么整个失败(最终一条都没有插入)。结果MySQL的默认表现竟然是允许部分成功的事务提交,也就是事务没有原子性没有原子性就没有一致性,如果这个事务是一笔转账(先扣再加),因为某些原因失败,那这里的帐就做不平了。这种数据库如果用来记账恐怕是一笔糊涂账,所以说什么“金融级MySQL”恐怕就是一个笑话。

当然,滑稽的是还有一些MySQL用户将其称为“特性”,说这体现了MySQL的容错性。实际上,此类“特殊容错”需求在SQL标准中完全可以通过SAVEPOINT机制实现。PgSQL对此的实现就堪称典范,psql客户端允许通过ON_ERROR_ROLLBACK选项,隐式地在每条语句后创建SAVEPOINT,并在语句失败后自动ROLLBACK TO SAVEPOINT,以标准SQL的方式,以客户端可选项的形式,在不破坏事物ACID的情况下,同样实现这种看上去便利实则苟且的功能。相比之下,MySQL的这种所谓“特性”是以直接在服务端默认牺牲事务ACID为代价的(这意味着用户使用JDBC,psycopg等应用驱动也照样受此影响)。

如果是互联网业务,注册个新用户丢个头像、丢个评论可能不是什么大事。数据那么多,丢几条,错几条又算个什么?别说是数据,业务本身很可能都处于朝不保夕的状态,所以糙又如何?万一成功了,前人拉的屎反正也是后人来擦。所以一些互联网公司通常并不在乎这些。

PostgreSQL所谓“严格的约束与语法“可能对新人来说“不近人情”,例如,一批数据中如果有几条脏数据,MySQL可能会照单全收,而PG则会严格拒绝。尽管苟且妥协看上去很省事,但在其他地方卖下了雷:因为逻辑炸弹深夜加班排查擦屁股的工程师,和不得不天天清洗脏数据的数据分析师肯定对此有很大怨念。从长期看,要想成功,做正确的事最重要。

一个成功的技术,现实的优先级必须高于公关,你可以糊弄别人,但糊弄不了自然规律。

——罗杰斯委员会报告(1986)

MySQL的流行度并没有和PgSQL相差太远,然而其功能比起PostgreSQL和Oracle却是差距不小。Oracle与PostgreSQL算诞生于同一时期,再怎么斗,立场与阵营不同,也有点惺惺相惜的老对手的意思:都是扎实修炼了半个世纪内功,厚积薄发的老法师。而MySQL就像心浮气躁耍刀弄枪的二十来岁毛头小伙子,凭着一把蛮力,借着互联网野蛮生长的黄金二十年趁势而起,占山为王。

时代所赋予的红利,也会随时代过去而退潮。在这个变革的时代中,没有先进的功能打底,“流行”也恐怕也难以长久。


发展前景

从个人职业发展前景的角度看,很多数程序员学习一门技术的原因都是为了提高自己的技术竞争力(从而更好占坑赚钱)。PostgreSQL是各种关系型数据库中性价比最高的选择:它不仅可以用来做传统的CRUD OLTP业务,数据分析更是它的拿手好戏。各种特色功能更是提供了切入多种行业以的契机:基于PostGIS的地理时空数据处理分析,基于Timescale的时序金融物联网数据处理分析,基于Pipeline存储过程触发器的流式处理,基于倒排索引全文检索的搜索引擎,FDW对接统一各式各样的外部数据源。可以说,它是真正一专多长的全栈数据库,用它可以实现的功能要比单纯的OLTP数据库要丰富得多,更是为CRUD码农提供了转型和深入的进阶道路。

企业用户的角度来看,PostgreSQL在一个很可观的规模内都可以独立扮演多面手的角色,一个组件当多种组件使。而单一数据组件选型可以极大地削减项目额外复杂度,这意味着能节省很多成本。它让十个人才能搞定的事,变成一个人就能搞定的事。 当然这不是说PG要一个打十个把其他数据库的饭碗都掀翻,专业组件在专业领域的实力是毋庸置疑的。但切莫忘记,为了不需要的规模而设计是白费功夫,实际上这属于过早优化的一种形式。如果真有那么一样技术可以满足你所有的需求,那么使用该技术就是最佳选择,而不是试图用多个组件来重新实现它。

以探探为例,在250WTPS与200TB数据的量级下,单一PostgreSQL选型依然能稳如狗地支撑业务。能在很可观的规模内做到一专多长,除了本职的OLTP,Pg还在相当长的时间里兼任了缓存,OLAP,批处理,甚至消息队列的角色。当然神龟虽寿,犹有竟时。最终这些兼职功能还是要逐渐分拆出去由专用组件负责,但那已经是近千万日活时的事了。

商业生态的角度看,PostgreSQL也有巨大的优势。一来PG技术先进,可称为 “开源版Oracle”。原生的PG基本可以对Oracle的功能做到八九成兼容,EDB更是有96% Oracle兼容的专业PG发行版。因此在抢占去O腾退出的市场中,PostgreSQL及其衍生版本的技术优势是压倒性的。二来PG协议友善,采用了宽松的BSD协议。因此各种数据库厂商,云厂商出品的“自研数据库”,以及很多“云数据库”大体都是基于PgSQL改造的。例如最近HW基于PostgreSQL搞openGaussDB就是一个很明智的选择。不要误会,PG的协议确实允许这样做,而且这样做也确实让PostgreSQL的生态更加繁荣壮大。卖PostgreSQL衍生版是一个很成熟的市场:传统企业不差钱且愿意为此付费买单。开源天才之火有商业利益之油浇灌,因而源源不断地释放出旺盛的生命力。

vs MySQL

作为老对手,MySQL的处境就有些尴尬了。

从个人职业发展上来看,学MySQL主要就是干CRUD。学好增删改查成为一个合格的码农是没问题的,然而谁又愿意一直“数据矿工”的活呢?数据分析才是数据产业链上的暴利肥差。以MySQL孱弱的分析能力,很难支持CURD程序员升级转型发展。此外,PostgreSQL的市场需求摆在那里,但现在却面临供不应求的状况(以至于现在大量良莠不齐的PG培训机构如雨后春笋般冒了出来),MySQL的人确实比PgSQL的人好招,这是不假的。但反过来说MySQL界的内卷程度也要大的多,供不应求方才体现稀缺性,人太多了技能也就贬值了。

从企业用户的角度来看,MySQL就是专用于OLTP的单一功能组件,往往需要ES, Redis, Mongo等其他等等一起配合才能满足完整的数据存储需求,而PG基本就不会有这个问题。此外,MySQL和PgSQL都是开源数据库,都“免费”。免费的Oracle和免费的MySQL用户会选择哪个呢?

从商业生态来看,MySQL面临的最大问题是 叫好不叫座。叫好当然是因为越流行则声音越大,尤其主要的用户互联网企业本身就占据话语权高地。不叫座当然也是因为互联网公司本身对于这类软件付费的意愿是极弱的:怎么算都是养几个MySQL DBA直接用开源的更合算。此外,因为MySQL的GPL协议要求衍生软件也要开源,软件厂商基于MySQL研发的动机也不强,基本都是采用 兼容“MySQL” 协议来分MySQL的市场蛋糕,而不是基于MySQL的代码进行开发与回馈,让人对其生态健康程度产生怀疑。

当然MySQL最大的问题就在于:它的生态位越来越狭窄。论严谨的事务处理与数据分析,PostgreSQL甩开它几条街;论糙猛快,快速出原型,NoSQL全家桶又要比MySQL方便太多。论商业发财,上面有Oracle干爹压着;论开源生态,又不断出现MySQL兼容的新生代产品来尝试替代主体。可以说MySQL处在一种吃老本的位置上,只是凭籍历史积分存量维持着现状的地位。时间是否会站在MySQL这一边,我们拭目以待。

vs NewSQL

最近市场上当然也有一些很亮眼的NewSQL产品,例如TiDB,Cockroachdb,Yugabytedb等等。何如?我认为它们都是很好的产品,有一些不错的技术亮点,都是对开源技术的贡献。但是它们可能同样面临叫好不叫座的困局。

NewSQL的大体特征是:主打“分布式”的概念,通过“分布式”解决水平扩展性容灾高可用两个问题,并因分布式的内在局限性会牺牲许多功能,只能提供较为简单有限的查询支持。分布式数据库在高可用容灾方面与传统主从复制并没有质的区别,因此其特征主要可以概括为“以量换质”。

然而对很多企业而言,牺牲功能换取扩展性很可能是一个伪需求弱需求。在我接触过的为数不少的用户中,绝大多数场景下的的数据量和负载水平完全落在单机Postgres的处理范围内(目前弄过的记录是单库15TB,单集群40万TPS)。从数据量上来讲,绝大多数企业终其生命周期的数据量也超不过这个瓶颈;至于性能就更不重要了,过早优化是万恶之源,很多企业的DB性能余量足够让他们把所有业务逻辑用存储过程编写然后高高兴兴的跑在数据库里。

NewSQL的祖师爷Google Spanner就是为了解决海量数据扩展性的问题,但又有多少企业能有Google的业务数据量?恐怕还是只有典型的互联网公司,或者某些大企业的部分业务会有这种量级的数据存储需求。所以和MySQL一样,NewSQL的问题就回到了谁来买单这个根本问题上。恐怕到最后只能还是由投资人和国资委来买吧。

但最起码,NewSQL的这种尝试始终是值得赞扬的。

vs 云数据库

我想直率地说:多年来,我们就像个傻子一样,他们拿着我们开发的东西大赚了一笔”。

—— Ofer Bengal , Redis Labs 首席执行官

另一个值得关注的“竞争者”是所谓云数据库,包括两种,一种是放在云上托管的开源数据库。例如 RDS for PostgreSQL,另一种是自研的新一代云数据库。

针对前者,主要的问题是“云厂商吸血”。如果云厂商售卖开源软件,实际上会导致就会导致开源软件的相关岗位和利润向云厂商集中,而云厂商是否允许自己的程序员给开源项目做贡献,做多少贡献,其实是很难说的。负责人的大厂通常是会回馈社区,回馈生态的,但这取决于它们的自觉。开源软件还是应当将命运握在自己手中,防止云厂商过分做大形成垄断。相比少量垄断巨头,多数分散的小团体能提供更高的生态多样性,更有利于生态健康发展。

Gartner称2022年75%的数据库将部署至云平台,这个牛逼吹的太大了。(但也有圆的办法,毕竟用一台机器就可以轻松创建几亿个sqlite文件数据库,这算不算?)。因为云计算解决不了一个根本性的问题 —— 信任。实际上在商业活动中,技术牛逼不牛逼是很次要的因素,Trust才是最关键的。数据是很多企业的生命线,云厂商又不是真正的中立第三方,谁能保证数据不会被其偷窥,盗窃,泄漏,甚至直接被卡脖子关停(如各路云厂商锤Parler)?TDE之类的透明加密解决方案也属于鸡肋,充分的恶心了自己,但也防不住真正的有心人。也许要等真正实用的高效全同态加密技术成熟才能解决信任与安全这个问题吧。

另一个根本性的问题在于成本:就目前云厂商的定价策略,云数据库只有在小微规模下有优势。例如一台D740 64核|400G内存|3TB PCI-E SSD的高配机型四年综合成本撑死了十几万块。然而我能找到最大的规格RDS(比这差很多,32核|128GB)一年的价格就这个数了。只要数据量节点数稍微上那么点规模,雇个DBA自建就合算太多了。

云数据库的主要优势还是在于管控,说白了就是用起来方便,点点鼠标。日常运维功能已经覆盖的比较全面,也有一些基础的监控支持。总之下限是摆在那里,如果找不到靠谱的数据库人才,用云数据库起码不至于出太多幺蛾子。 不过这些管控软件虽好,基本都是闭源的,而且与供应商深度绑定。

如果你想找一个开源的PostgreSQL监控管控一条龙解决方案,不妨试试Pigsty。

后一种云数据库以AWS Aurora为代表,也包括一系列类似产品如阿里云PolarDB,腾讯云CynosDB。基本都是采用PostgreSQL与MySQL作为Base和协议层,基于云基础设施(共享存储,S3,RDMA)进行定制化,对扩容速度性能进行了优化。这类产品在技术上肯定是有新颖性和创造性的。但灵魂问题就是,这类产品相比直接使用原生PostgreSQL的收益到底在哪里呢?能看到立竿见影的好处就是集群扩容会快很多(从几小时级到5分钟),不过相比高昂的费用与供应商锁定的问题,实在是挠不到痛点和痒点。

总的来说,云数据库对原生PostgreSQL 构成的威胁是有限的。也不用太担心云厂商的问题,云厂商总的来说还开源软件生态的一份子,对社区和生态是有贡献的。赚钱嘛,不磕碜,大家都有钱赚了,才有余力去搞公益,对不对?


弃暗投明?

通常来说,Oracle的程序员转PostgreSQL不会有什么包袱,因为两者功能类似,大多数经验都是通用的。实际上,很多PostgreSQL生态的成员都是从Oracle阵营转投PG的。例如国内著名的Oracle服务商云和恩墨(由中国第一位Oracle ACE总监盖国强创办),去年就公开宣布“躬身入局”,拥抱PostgreSQL。

也有不少MySQL阵营转投PgSQL的,其实这类用户对两者的区别感受才是最深的:基本上都是一副相见恨晚,弃暗投明的样子。实际上我自己最开始也是先用MySQL😆,能自己选型后就拥抱了PgSQL。不过有些老程序员已经和MySQL形成了深度利益绑定,嚷嚷着MySQL多好多好,还要不忘来碰瓷喷一喷PgSQL(特指某人)。这个其实是可以理解的,触动利益比触动灵魂还难,看到自己擅长的技术日落西山那肯定是愤懑不平😠。毕竟一把年纪投在MySQL上,PostgreSQL🐘再好,让我抛弃我心爱的小海豚🐬,做不到啊。

不过,刚入行的年轻人还是有机会去选择一条更光明的道路的。时间是最公平的裁判,而新生代的选择则是最有代表性的标杆。据我个人观察,在新兴的极有活力的Golang开发者群体中,PostgreSQL的流行程度要显著高于MySQL,不少创业型、创新型的公司现在都选择Go+Pg作为自己的技术栈,例如Instagram,TanTan,Apple都是Go+PG。

我认为这一现象的主要原因就是新生代开发者的崛起,Go之于Java,就像PgSQL之于MySQL。长江后浪推前浪,这其实就是演化的核心机制 —— 新陈代谢。Go和PgSQL慢慢拍扁Java和MySQL,但Go和PgSQL当然也有可能在以后被诸如Rust和某些真正革命性的NewSQL数据库拍扁。但说到底,搞技术还是要搞那些前景光明的,不要去搞那些日暮西山的。(当然下海太早当烈士也不合适)。要去看新生代开发者在用什么,有活力的创业公司、新项目、新团队在用什么,弄这些是没有错的。


PG的问题

当然PgSQL有没有自己的问题?当然也有 —— 流行度

流行度关乎着着用户规模,信任水平,成熟案例数量,有效需求反馈量,开发者数量等等。尽管按目前的流行度发展趋势,PG将在几年后超过MySQL,所以从长期来看,我觉得这并不是问题。但作为PostgreSQL社区的一员,我觉得很有必要去进一步做一些事情,Secure this success,并加快这一进度。而要想让一样技术更加流行,效果最好的方式就是:降低门槛

所以,我做了一个开源软件Pigsty,要把PostgreSQL部署、监控、管理、使用的门槛从天花板砸到地板,它有三个核心目标:

  • 做最顶尖最专业的开源PostgreSQL 监控系统(类tidashboard)
  • 做门槛最低最好用的开源PostgreSQL管控方案(类tiup)
  • 做开箱即用的与数据分析&可视化集成开发环境(类minikube)

当然这里细节限于篇幅就不展开了,详情留待下篇分说。

PostgreSQL好处都有啥

PostgreSQL的Slogan是“世界上最先进的开源关系型数据库”,但我觉得这口号不够响亮,而且一看就是在怼MySQL那个“世界上最流行的开源关系型数据库”的口号,有碰瓷之嫌。要我说最能生动体现PG特色的口号应该是:一专多长的全栈数据库,一招鲜吃遍天嘛。

pggood


全栈数据库

成熟的应用可能会用到许许多多的数据组件(功能):缓存,OLTP,OLAP/批处理/数据仓库,流处理/消息队列,搜索索引,NoSQL/文档数据库,地理数据库,空间数据库,时序数据库,图数据库。传统的架构选型呢,可能会组合使用多种组件,典型的如:Redis + MySQL + Greenplum/Hadoop + Kafuka/Flink + ElasticSearch,一套组合拳基本能应付大多数需求了。不过比较令人头大的就是异构系统集成了:大量的代码都是重复繁琐的胶水代码,干着把数据从A组件搬运到B组件的事情。

在这里,MySQL就只能扮演OLTP关系型数据库的角色,但如果是PostgreSQL,就可以身兼多职,One handle them all,比如:

  • OLTP:事务处理是PostgreSQL的本行

  • OLAP:citus分布式插件,ANSI SQL兼容,窗口函数,CTE,CUBE等高级分析功能,任意语言写UDF

  • 流处理:PipelineDB扩展,Notify-Listen,物化视图,规则系统,灵活的存储过程与函数编写

  • 时序数据:timescaledb时序数据库插件,分区表,BRIN索引

  • 空间数据:PostGIS扩展(杀手锏),内建的几何类型支持,GiST索引。

  • 搜索索引:全文搜索索引足以应对简单场景;丰富的索引类型,支持函数索引,条件索引

  • NoSQL:JSON,JSONB,XML,HStore原生支持,至NoSQL数据库的外部数据包装器

  • 数据仓库:能平滑迁移至同属Pg生态的GreenPlum,DeepGreen,HAWK等,使用FDW进行ETL

  • 图数据:递归查询

  • 缓存:物化视图

ext

以Extension作六器,礼天地四方。

以Greenplum礼天,

以Postgres-XL礼地,

以Citus礼东方,

以TimescaleDB礼南方,

以PipelineDB礼西方,

以PostGIS礼北方。

—— 《周礼.PG》

在探探的旧版架构中,整个系统就是围绕PostgreSQL设计的。几百万日活,几百万全局DB-TPS,几百TB数据的规模下,数据组件只用了PostgreSQL。独立的数仓,消息队列和缓存都是后来才引入的。而且这只是验证过的规模量级,进一步压榨PG是完全可行的。

因此,在一个很可观的规模内,PostgreSQL都可以扮演多面手的角色,一个组件当多种组件使。虽然在某些领域它可能比不上专用组件,至少都做的都还不赖。而单一数据组件选型可以极大地削减项目额外复杂度,这意味着能节省很多成本。它让十个人才能搞定的事,变成一个人就能搞定的事。

为了不需要的规模而设计是白费功夫,实际上这属于过早优化的一种形式。只有当没有单个软件能满足你的所有需求时,才会存在分拆集成的利弊权衡。集成多种异构技术是相当棘手的工作,如果真有那么一样技术可以满足你所有的需求,那么使用该技术就是最佳选择,而不是试图用多个组件来重新实现它。

当业务规模增长到一定量级时,可能不得不使用基于微服务/总线的架构,将数据库的功能分拆为多个组件。但PostgreSQL的存在极大地推后了这个权衡到来的阈值,而且分拆之后依然能继续发挥重要作用。


运维友好

当然除了功能强大之外,Pg的另外一个重要的优势就是运维友好。有很多非常实用的特性:

  • DDL能放入事务中,删表,TRUNCATE,创建函数,索引,都可以放在事务里原子生效,或者回滚。

    这就能进行很多骚操作,比如在一个事务里通过RENAME,完成两张表的王车易位。

  • 能够并发地创建、删除索引,添加非空字段,重整索引与表(不锁表)。

    这意味着可以随时在线上不停机进行重大的模式变更,按需对索引进行优化。

  • 复制方式多样:段复制,流复制,触发器复制,逻辑复制,插件复制等等。

    这使得不停服务迁移数据变得相当容易:复制,改读,改写三步走,线上迁移稳如狗。

  • 提交方式多样:异步提交,同步提交,法定人数同步提交。

    这意味着Pg允许在C和A之间做出权衡与选择,例如交易库使用同步提交,普通库使用异步提交。

  • 系统视图非常完备,做监控系统相当简单。

  • FDW的存在让ETL变得无比简单,一行SQL就能解决。

    FDW可以方便地让一个实例访问其他实例的数据或元数据。在跨分区操作,数据库监控指标收集,数据迁移等场景中妙用无穷。同时还可以对接很多异构数据系统。


生态健康

PostgreSQL的生态也很健康,社区相当活跃。

相比MySQL,PostgreSQL的一个巨大的优势就是协议友好。PG采用类似BSD/MIT的PostgreSQL协议,差不多理解为只要别打着Pg的旗号出去招摇撞骗,随便你怎么搞,换皮出去卖都行。君不见多少国产数据库,或者不少“自研数据库”实际都是Pg的换皮或二次开发产品。

当然,也有很多衍生产品会回馈主干,比如timescaledb, pipelinedb, citus 这些基于PG的“数据库”,最后都变成了原生PG的插件。很多时候你想实现个什么功能,一搜就能找到对应的插件或实现。开源嘛,还是要讲一些情怀的。

PG的代码质量相当之高,注释写的非常清晰。C的代码读起来有种Go的感觉,代码都可以当文档看了。能从中学到很多东西。相比之下,其他数据库,比如MongoDB,看一眼我就放弃了读下去的兴趣。

而MySQL呢,社区版采用的是GPL协议,这其实挺蛋疼的。要不是GPL传染,怎么会有这么多基于MySQL改的数据库开源出来呢?而且MySQL还在乌龟壳的手里,让自己的蛋蛋攥在别人手中可不是什么明智的选择,更何况是业界毒瘤呢?Facebook修改React协议的风波就算是一个前车之鉴了。


问题

当然,要说有什么缺点或者遗憾,那还是有几个的:

  • 因为使用了MVCC,数据库需要定期VACUUM,需要定期维护表和索引避免性能下降。
  • 没有很好的开源集群监控方案(或者太丑!),需要自己做。
  • 慢查询日志和普通日志是混在一起的,需要自己解析处理。
  • 官方Pg没有很好用的列存储,对数据分析而言算一个小遗憾。

当然都是些无关痛痒的小毛小病,不过真正的问题可能和技术无关……

说到底,MySQL确实是最流行的开源关系型数据库,没办法,写Java的,写PHP的,很多人最开始用的都是MySQL…,所以Pg招人相对困难是一个事实,很多时候只能自己培养。不过看DB Engines上的流行度趋势,未来还是很光明的。

dbrank

其他

学PostgreSQL是一件很有趣的事,它让我意识到数据库的功能远远不止增删改查。我学着SQL Server与MySQL迈进数据库的大门。但却是PostgreSQL真正向我展示了数据库的奇妙世界。

之所以写本文,是因为在知乎上的老坟又被挖了出来,让笔者回想起当年邂逅PostgreSQL时的青葱岁月。(https://www.zhihu.com/question/20010554/answer/94999834 )当然,现在我干了专职的PG DBA,忍不住再给这老坟补几铲。“王婆卖瓜,自卖自夸”,夸一夸PG也是应该的。嘿嘿嘿……

全栈工程师就该用全栈数据库嘛。

我自己比较选型过MySQL和PostgreSQL,难得地在阿里这种MySQL的世界中有过选择的自由。我认为单从技术因素上来讲,PG是完爆MySQL的。尽管阻力很大,最后还是把PostgreSQL用了起来,推了起来。我用它做过很多项目,解决了很多需求(小到算统计报表,大到给公司创收个小目标)。大多数需求PG单挑就搞定了,少部分也会再用些MQ和NoSQL(Redis,MongoDB,Cassandra/HBase)。Pg实在是让人爱不释手。

最后实在是对Pg爱不释手,以至于专职去研究PG了。

在我的第一份工作中就深刻尝到了甜头,使用PostgreSQL,一个人的开发效率能顶一个小团队:

  • 后端懒得写怎么办,PostGraphQL直接从数据库模式定义生成GraphQL API,自动监听DDL变更,生成相应的CRUD方法与存储过程包装,对于后台开发再方便不过,类似的工具还有PostgREST与pgrest。对于中小数据量的应用都还堪用,省了一大半后端开发的活。

  • 需要用到Redis的功能,直接上Pg,模拟普通功能不在话下,缓存也省了。Pub/Sub使用Notify/Listen/Trigger实现,用来广播配置变更,做一些控制非常方便。

  • 需要做分析,窗口函数,复杂JOIN,CUBE,GROUPING,自定义聚合,自定义语言,爽到飞起。如果觉得规模大了想scale out可以上citus扩展(或者换greenplum);比起数仓可能少个列存比较遗憾,但其他该有的都有了。

  • 用到地理相关的功能,PostGIS堪称神器,千行代码才能实现的复杂地理需求,一行SQL轻松高效解决

  • 存储时序数据,timescaledb扩展虽然比不上专用时序数据库,但百万记录每秒的入库速率还是有的。用它解决过硬件传感器日志存储,监控系统Metrics存储的需求。

  • 一些流计算的相关功能,可以用PipelineDB直接定义流式视图实现:UV,PV,用户画像实时呈现。

  • PostgreSQL的FDW是一种强大的机制,允许接入各种各样的数据源,以统一的SQL接口访问。它妙用无穷:

    • file_fdw这种自带的扩展,可以将任意程序的输出接入数据表。最简单的应用就是监控系统信息
    • 管理多个PostgreSQL实例时,可以在一个元数据库中用自带的postgres_fdw导入所有远程数据库的数据字典。统一访问所有数据库实例的元数据,一行SQL拉取所有数据库的实时指标,监控系统做起来不要太爽。
    • 之前做过的一件事就是用hbase_fdw和MongoFDW,将HBase中的历史批量数据,MongoDB中的当日实时数据包装为PostgreSQL数据表,一个视图就简简单单地实现了融合批处理与流处理的Lambda架构。
    • 使用redis_fdw进行缓存更新推送;使用mongo_fdw完成从mongo到pg的数据迁移;使用mysql_fdw读取MySQL数据并存入数仓;实现跨数据库,甚至跨数据组件的JOIN;使用一行SQL就能完成原本多少行代码才能实现的复杂ETL,这是一件多么美妙的事情。
  • 各种丰富的类型与方法支持:例如JSON,从数据库直接生成前端所需的JSON响应,轻松而惬意。范围类型,优雅地解决很多原本需要程序处理的边角情况。其他的例如数组,多维数组,自定义类型,枚举,网络地址,UUID,ISBN。很多开箱即用的数据结构让程序员省去了多少造轮子的功夫。

  • 丰富的索引类型:通用的Btree索引;大幅优化顺序访问的Brin索引;等值查询的Hash索引;GIN倒排索引;GIST通用搜索树,高效支持地理查询,KNN查询;Bitmap同时利用多个独立索引;Bloom高效过滤索引;能大幅减小索引大小的条件索引;能优雅替代冗余字段的函数索引。而MySQL就只有那么可怜的几种索引。

  • 稳定可靠,正确高效。MVCC轻松实现快照隔离,MySQL的RR隔离等级实现不完善,无法避免PMP与G-single异常。而且基于锁与回滚段的实现会有各种坑;PostgreSQL通过SSI能实现高性能的可序列化。

  • 复制强大:WAL段复制,流复制(v9出现,同步、半同步、异步),逻辑复制(v10出现:订阅/发布),触发器复制,第三方复制,各种复制一应俱全。

  • 运维友好:可以将DDL放在事务中执行(可回滚),创建索引不锁表,添加新列(不带默认值)不锁表,清理/备份不锁表。各种系统视图,监控功能都很完善。

  • 扩展众多、功能丰富、可定制程度极强。在PostgreSQL中可以使用任意的语言编写函数:Python,Go,Javascript,Java,Shell等等。与其说Pg是数据库,不如说它是一个开发平台。我就试过很多没什么卵用但很好玩的东西:数据库里(in-db) 的爬虫/ 推荐系统 / 神经网络 / Web服务器等等。有着各种功能强悍或脑洞清奇的第三方插件:[https://pgxn.org/)。

  • PostgreSQL的License友好,BSD随便玩,君不见多少数据库都是PG的换皮产品。MySQL有GPL传染,还要被Oracle捏着蛋蛋。

Go数据库教程:database/sql

Go使用SQL与类SQL数据库的惯例是通过标准库database/sql。这是一个对关系型数据库的通用抽象,它提供了标准的、轻量的、面向行的接口。不过database/sql的包文档只讲它做了什么,却对如何使用只字未提。快速指南远比堆砌事实有用,本文讲述了database/sql的使用方法及其注意事项。

1. 顶层抽象

在Go中访问数据库需要用到sql.DB接口:它可以创建语句(statement)和事务(transaction),执行查询,获取结果。

sql.DB并不是数据库连接,也并未在概念上映射到特定的数据库(Database)或模式(schema)。它只是一个抽象的接口,不同的具体驱动有着不同的实现方式。通常而言,sql.DB会处理一些重要而麻烦的事情,例如操作具体的驱动打开/关闭实际底层数据库的连接,按需管理连接池。

sql.DB这一抽象让用户不必考虑如何管理并发访问底层数据库的问题。当一个连接在执行任务时会被标记为正在使用。用完之后会放回连接池中。不过用户如果用完连接后忘记释放,就会产生大量的连接,极可能导致资源耗尽(建立太多连接,打开太多文件,缺少可用网络端口)。

2. 导入驱动

使用数据库时,除了database/sql包本身,还需要引入想使用的特定数据库驱动。

尽管有时候一些数据库特有的功能必需通过驱动的Ad Hoc接口来实现,但通常只要有可能,还是应当尽量只用database/sql中定义的类型。这可以减小用户代码与驱动的耦合,使切换驱动时代码改动最小化,也尽可能地使用户遵循Go的惯用法。本文使用PostgreSQL为例,PostgreSQL的著名的驱动有:

这里以pgx为例,它性能表现不俗,并对PostgreSQL诸多特性与类型有着良好的支持。既可使用Ad-Hoc API,也提供了标准数据库接口的实现:github.com/jackc/pgx/stdlib

import (
	"database/sql"
	_ "github.com/jackx/pgx/stdlib"
)

使用_别名来匿名导入驱动,驱动的导出名字不会出现在当前作用域中。导入时,驱动的初始化函数会调用sql.Register将自己注册在database/sql包的全局变量sql.drivers中,以便以后通过sql.Open访问。

3. 访问数据

加载驱动包后,需要使用sql.Open()来创建sql.DB

func main() {
	db, err := sql.Open("pgx","postgres://localhost:5432/postgres")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
}

sql.Open有两个参数:

  • 第一个参数是驱动名称,字符串类型。为避免混淆,一般与包名相同,这里是pgx
  • 第二个参数也是字符串,内容依赖于特定驱动的语法。通常是URL的形式,例如postgres://localhost:5432
  • 绝大多数情况下都应当检查database/sql操作所返回的错误。
  • 一般而言,程序需要在退出时通过sql.DBClose()方法释放数据库连接资源。如果其生命周期不超过函数的范围,则应当使用defer db.Close()

执行sql.Open()并未实际建立起到数据库的连接,也不会验证驱动参数。第一个实际的连接会惰性求值,延迟到第一次需要时建立。用户应该通过db.Ping()来检查数据库是否实际可用。

if err = db.Ping(); err != nil {
	// do something about db error
}

sql.DB对象是为了长连接而设计的,不要频繁Open()Close()数据库。而应该为每个待访问的数据库创建一个sql.DB实例,并在用完前一直保留它。需要时可将其作为参数传递,或注册为全局对象。

如果没有按照database/sql设计的意图,不把sql.DB当成长期对象来用而频繁开关启停,就可能遭遇各式各样的错误:无法复用和共享连接,耗尽网络资源,由于TCP连接保持在TIME_WAIT状态而间断性的失败等……

4. 获取结果

有了sql.DB实例之后就可以开始执行查询语句了。

Go将数据库操作分为两类:QueryExec。两者的区别在于前者会返回结果,而后者不会。

  • Query表示查询,它会从数据库获取查询结果(一系列行,可能为空)。
  • Exec表示执行语句,它不会返回行。

此外还有两种常见的数据库操作模式:

  • QueryRow表示只返回一行的查询,作为Query的一个常见特例。
  • Prepare表示准备一个需要多次使用的语句,供后续执行用。

4.1 获取数据

让我们看一个如何查询数据库并且处理结果的例子:利用数据库计算从1到10的自然数之和。

func example() {
	var sum, n int32

	// invoke query
	rows, err := db.Query("SELECT generate_series(1,$1)", 10)
    // handle query error
	if err != nil {
		fmt.Println(err)
	}
    // defer close result set
	defer rows.Close()

	// Iter results
	for rows.Next() {
		if err = rows.Scan(&n); err != nil {
			fmt.Println(err)	// Handle scan error
		}
		sum += n				// Use result
	}

	// check iteration error
	if rows.Err() != nil {
		fmt.Println(err)
	}

	fmt.Println(sum)
}
  • 整体工作流程如下:

    1. 使用db.Query()来发送查询到数据库,获取结果集Rows,并检查错误。
    2. 使用rows.Next()作为循环条件,迭代读取结果集。
    3. 使用rows.Scan从结果集中获取一行结果。
    4. 使用rows.Err()在退出迭代后检查错误。
    5. 使用rows.Close()关闭结果集,释放连接。
  • 一些需要详细说明的地方:

    1. db.Query会返回结果集*Rows和错误。每个驱动返回的错误都不一样,用错误字符串来判断错误类型并不是明智的做法,更好的方法是对抽象的错误做Type Assertion,利用驱动提供的更具体的信息来处理错误。当然类型断言也可能产生错误,这也是需要处理的。

      if err.(pgx.PgError).Code == "0A000" {
      // Do something with that type or error
      }
      
    2. rows.Next()会指明是否还有未读取的数据记录,通常用于迭代结果集。迭代中的错误会导致rows.Next()返回false

    3. rows.Scan()用于在迭代中获取一行结果。数据库会使用wire protocal通过TCP/UnixSocket传输数据,对Pg而言,每一行实际上对应一条DataRow消息。Scan接受变量地址,解析DataRow消息并填入相应变量中。因为Go语言是强类型的,所以用户需要创建相应类型的变量并在rows.Scan中传入其指针,Scan函数会根据目标变量的类型执行相应转换。例如某查询返回一个单列string结果集,用户可以传入[]bytestring类型变量的地址,Go会将原始二进制数据或其字符串形式填入其中。但如果用户知道这一列始终存储着数字字面值,那么相比传入string地址后手动使用strconv.ParseInt()解析,更推荐的做法是直接传入一个整型变量的地址(如上面所示),Go会替用户完成解析工作。如果解析出错,Scan会返回相应的错误。

    4. rows.Err()用于在退出迭代后检查错误。正常情况下迭代退出是因为内部产生的EOF错误,使得下一次rows.Next() == false,从而终止循环;在迭代结束后要检查错误,以确保迭代是因为数据读取完毕,而非其他“真正”错误而结束的。遍历结果集的过程实际上是网络IO的过程,可能出现各种错误。健壮的程序应当考虑这些可能,而不能总是假设一切正常。

    5. rows.Close()用于关闭结果集。结果集引用了数据库连接,并会从中读取结果。读取完之后必须关闭它才能避免资源泄露。只要结果集仍然打开着,相应的底层连接就处于忙碌状态,不能被其他查询使用。

    6. 因错误(包括EOF)导致的迭代退出会自动调用rows.Close()关闭结果集(和释放底层连接)。但如果程序自行意外地退出了循环,例如中途break & return,结果集就不会被关闭,产生资源泄露。rows.Close方法是幂等的,重复调用不会产生副作用,因此建议使用 defer rows.Close()来关闭结果集。

以上就是在Go中使用数据库的标准方式。

4.2 单行查询

如果一个查询每次最多返回一行,那么可以用快捷的单行查询来替代冗长的标准查询,例如上例可改写为:

var sum int
err := db.QueryRow("SELECT sum(n) FROM (SELECT generate_series(1,$1) as n) a;", 10).Scan(&sum)
if err != nil {
	fmt.Println(err)
}
fmt.Println(sum)

不同于Query,如果查询发生错误,错误会延迟到调用Scan()时统一返回,减少了一次错误处理判断。同时QueryRow也避免了手动操作结果集的麻烦。

需要注意的是,对于单行查询,Go将没有结果的情况视为错误。sql包中定义了一个特殊的错误常量ErrNoRows,当结果为空时,QueryRow().Scan()会返回它。

4.3 修改数据

什么时候用Exec,什么时候用Query,这是一个问题。通常DDL和增删改使用Exec,返回结果集的查询使用Query。但这不是绝对的,这完全取决于用户是否希望想要获取返回结果。例如在PostgreSQL中:INSERT ... RETURNING *;虽然是一条插入语句,但它也有返回结果集,故应当使用Query而不是Exec

QueryExec返回的结果不同,两者的签名分别是:

func (s *Stmt) Query(args ...interface{}) (*Rows, error)
func (s *Stmt) Exec(args ...interface{}) (Result, error) 

Exec不需要返回数据集,返回的结果是ResultResult接口允许获取执行结果的元数据

type Result interface {
	// 用于返回自增ID,并不是所有的关系型数据库都有这个功能。
	LastInsertId() (int64, error)
	// 返回受影响的行数。
	RowsAffected() (int64, error)
}

Exec的用法如下所示:

db.Exec(`CREATE TABLE test_users(id INTEGER PRIMARY KEY ,name TEXT);`)
db.Exec(`TRUNCATE test_users;`)
stmt, err := db.Prepare(`INSERT INTO test_users(id,name) VALUES ($1,$2) RETURNING id`)
if err != nil {
	fmt.Println(err.Error())
}
res, err := stmt.Exec(1, "Alice")

if err != nil {
	fmt.Println(err)
} else {
	fmt.Println(res.RowsAffected())
	fmt.Println(res.LastInsertId())
}

相比之下Query则会返回结果集对象*Rows,使用方式见上节。其特例QueryRow使用方式如下:

db.Exec(`CREATE TABLE test_users(id INTEGER PRIMARY KEY ,name TEXT);`)
db.Exec(`TRUNCATE test_users;`)
stmt, err := db.Prepare(`INSERT INTO test_users(id,name) VALUES ($1,$2) RETURNING id`)
if err != nil {
	fmt.Println(err.Error())
}
var returnID int
err = stmt.QueryRow(4, "Alice").Scan(&returnID)
if err != nil {
	fmt.Println(err)
} else {
	fmt.Println(returnID)
}

同样的语句使用ExecQuery执行有巨大的差别。如上文所述,Query会返回结果集Rows,而存在未读取数据的Rows其实会占用底层连接直到rows.Close()为止。因此,使用Query但不读取返回结果,会导致底层连接永远无法释放。database/sql期望用户能够用完就把连接还回来,所以这样的用法很快就会导致资源耗尽(连接过多)。所以,应该用Exec的语句绝不可用Query来执行。

4.4 准备查询

在上一节的两个例子中,没有直接使用数据库的QueryExec方法,而是首先执行了db.Prepare获取准备好的语句(prepared statement)。准备好的语句Stmtsql.DB一样,都可以执行QueryExec等方法。

4.4.1 准备语句的优势

在查询前进行准备是Go语言中的惯用法,多次使用的查询语句应当进行准备(Prepare)。准备查询的结果是一个准备好的语句(prepared statement),语句中可以包含执行时所需参数的占位符(即绑定值)。准备查询比拼字符串的方式好很多,它可以转义参数,避免SQL注入。同时,准备查询对于一些数据库也省去了解析和生成执行计划的开销,有利于性能。

4.4.2 占位符

PostgreSQL使用$N作为占位符,N是一个从1开始递增的整数,代表参数的位置,方便参数的重复使用。MySQL使用?作为占位符,SQLite两种占位符都可以,而Oracle则使用:param1的形式。

MySQL               PostgreSQL            Oracle
=====               ==========            ======
WHERE col = ?       WHERE col = $1        WHERE col = :col
VALUES(?, ?, ?)     VALUES($1, $2, $3)    VALUES(:val1, :val2, :val3)

PostgreSQL为例,在上面的例子中:"SELECT generate_series(1,$1)" 就用到了$N的占位符形式,并在后面提供了与占位符数目匹配的参数个数。

4.4.3 底层内幕

准备语句有着各种优点:安全,高效,方便。但Go中实现它的方式可能和用户所设想的有轻微不同,尤其是关于和database/sql内部其他对象交互的部分。

在数据库层面,准备语句Stmt是与单个数据库连接绑定的。通常的流程是:客户端向服务器发送带有占位符的查询语句用于准备,服务器返回一个语句ID,客户端在实际执行时,只需要传输语句ID和相应的参数即可。因此准备语句无法在连接之间共享,当使用新的数据库连接时,必须重新准备。

database/sql并没有直接暴露出数据库连接。用户是在DBTx上执行Prepare,而不是Conn。因此database/sql提供了一些便利处理,例如自动重试。这些机制隐藏在Driver中实现,而不会暴露在用户代码中。其工作原理是:当用户准备一条语句时,它在连接池中的一个连接上进行准备。Stmt对象会引用它实际使用的连接。当执行Stmt时,它会尝试会用引用的连接。如果那个连接忙碌或已经被关闭,它会获取一个新的连接,并在连接上重新准备,然后再执行。

因为当原有连接忙时,Stmt会在其他连接上重新准备。因此当高并发地访问数据库时,大量的连接处于忙碌状态,这会导致Stmt不断获取新的连接并执行准备,最终导致资源泄露,甚至超出服务端允许的语句数目上限。所以通常应尽量采用扇入的方式减小数据库访问并发数。

4.4.4 查询的微妙之处

数据库连接其实是实现了Begin,Close,Prepare方法的接口。

type Conn interface {
        Prepare(query string) (Stmt, error)
        Close() error
        Begin() (Tx, error)
}

所以连接接口上实际并没有ExecQuery方法,这些方法其实定义在Prepare返回的Stmt上。对于Go而言,这意味着db.Query()实际上执行了三个操作:首先对查询语句做了准备,然后执行查询语句,最后关闭准备好的语句。这对数据库而言,其实是3个来回。设计粗糙的程序与简陋实现驱动可能会让应用与数据库交互的次数增至3倍。好在绝大多数数据库驱动对于这种情况有优化,如果驱动实现sql.Queryer接口:

type Queryer interface {
        Query(query string, args []Value) (Rows, error)
}

那么database/sql就不会再进行Prepare-Execute-Close的查询模式,而是直接使用驱动实现的Query方法向数据库发送查询。对于查询都是即拼即用,也不担心安全问题的情况下,直接Query可以有效减少性能开销。

5. 使用事务

事物是关系型数据库的核心特性。Go中事务(Tx)是一个持有数据库连接的对象,它允许用户在同一个连接上执行上面提到的各类操作。

5.1 事务基本操作

通过db.Begin()来开启一个事务,Begin方法会返回一个事务对象Tx。在结果变量Tx上调用Commit()或者Rollback()方法会提交或回滚变更,并关闭事务。在底层,Tx会从连接池中获得一个连接并在事务过程中保持对它的独占。事务对象Tx上的方法与数据库对象sql.DB的方法一一对应,例如Query,Exec等。事务对象也可以准备(prepare)查询,由事务创建的准备语句会显式绑定到创建它的事务。

5.2 事务注意事项

使用事务对象时,不应再执行事务相关的SQL语句,例如BEGIN,COMMIT等。这可能产生一些副作用:

  • Tx对象一直保持打开状态,从而占用了连接。
  • 数据库状态不再与Go中相关变量的状态保持同步。
  • 事务提前终止会导致一些本应属于事务内的查询语句不再属于事务的一部分,这些被排除的语句有可能会由别的数据库连接而非原有的事务专属连接执行。

当处于事务内部时,应当使用Tx对象的方法而非DB的方法,DB对象并不是事务的一部分,直接调用数据库对象的方法时,所执行的查询并不属于事务的一部分,有可能由其他连接执行。

5.3 Tx的其他应用场景

如果需要修改连接的状态,也需要用到Tx对象,即使用户并不需要事务。例如:

  • 创建仅连接可见的临时表
  • 设置变量,例如SET @var := somevalue
  • 修改连接选项,例如字符集,超时设置。

Tx上执行的方法都保证同一个底层连接执行,这使得对连接状态的修改对后续操作起效。这是Go中实现这种功能的标准方式。

5.4 在事务中准备语句

调用Tx.Prepare会创建一个与事务绑定的准备语句。在事务中使用准备语句,有一个特殊问题需要关注:一定要在事务结束前关闭准备语句。

在事务中使用defer stmt.Close()是相当危险的。因为当事务结束后,它会释放自己持有的数据库连接,但事务创建的未关闭Stmt仍然保留着对事务连接的引用。在事务结束后执行stmt.Close(),如果原来释放的连接已经被其他查询获取并使用,就会产生竞争,极有可能破坏连接的状态。

6. 处理空值

可空列(Nullable Column)非常的恼人,容易导致代码变得丑陋。如果可以,在设计时就应当尽量避免。因为:

  • Go语言的每一个变量都有着默认零值,当数据的零值没有意义时,可以用零值来表示空值。但很多情况下,数据的零值和空值实际上有着不同的语义。单独的原子类型无法表示这种情况。

  • 标准库只提供了有限的四种Nullable type::NullInt64, NullFloat64, NullString, NullBool。并没有诸如NullUint64NullYourFavoriteType,用户需要自己实现。

  • 空值有很多麻烦的地方。例如用户认为某一列不会出现空值而采用基本类型接收时却遇到了空值,程序就会崩溃。这种错误非常稀少,难以捕捉、侦测、处理,甚至意识到。

6.1 使用额外的标记字段

database\sql提供了四种基本可空数据类型:使用基本类型和一个布尔标记的复合结构体表示可空值。例如:

type NullInt64 struct {
        Int64 int64
        Valid bool // Valid is true if Int64 is not NULL
}

可空类型的使用方法与基本类型一致:

for rows.Next() {
	var s sql.NullString
	err := rows.Scan(&s)
	// check err
	if s.Valid {
	   // use s.String
	} else {
	   // handle NULL case
	}
}

6.2 使用指针

在Java中通过装箱(boxing)处理可空类型,即把基本类型包装成一个类,并通过指针引用。于是,空值语义可以通过指针为空来表示。Go当然也可以采用这种办法,不过标准库中并没有提供这种实现方式。pgx提供了这种形式的可空类型支持。

6.3 使用零值表示空值

如果数据本身从语义上就不会出现零值,或者根本不区分零值和空值,那么最简便的方法就是使用零值来表示空值。驱动go-pg提供了这种形式的支持。

6.4 自定义处理逻辑

任何实现了Scanner接口的类型,都可以作为Scan传入的地址参数类型。这就允许用户自己定制复杂的解析逻辑,实现更丰富的类型支持。

type Scanner interface {
  		// Scan 从数据库驱动中扫描出一个值,当不能无损地转换时,应当返回错误
  		// src可能是int64, float64, bool, []byte, string, time.Time,也可能是nil,表示空值。
        Scan(src interface{}) error
}

6.5 在数据库层面解决

通过对列添加NOT NULL约束,可以确保任何结果都不会为空。或者,通过在SQL中使用COALESCE来为NULL设定默认值。

7. 处理动态列

Scan()函数要求传递给它的目标变量的数目,与结果集中的列数正好匹配,否则就会出错。

但总有一些情况,用户事先并不知道返回的结果到底有多少列,例如调用一个返回表的存储过程时。

在这种情况下,使用rows.Columns()来获取列名列表。在不知道列类型情况下,应当使用sql.RawBytes作为接受变量的类型。获取结果后自行解析。

cols, err := rows.Columns()
if err != nil {
	// handle this....
}

// 目标列是一个动态生成的数组
dest := []interface{}{
	new(string),
	new(uint32),
	new(sql.RawBytes),
}

// 将数组作为可变参数传入Scan中。
err = rows.Scan(dest...)
// ...

8. 连接池

database/sql包里实现了一个通用的连接池,它只提供了非常简单的接口,除了限制连接数、设置生命周期基本没有什么定制选项。但了解它的一些特性也是很有帮助的。

  • 连接池意味着:同一个数据库上的连续两条查询可能会打开两个连接,在各自的连接上执行。这可能导致一些让人困惑的错误,例如程序员希望锁表插入时连续执行了两条命令:LOCK TABLEINSERT,结果却会阻塞。因为执行插入时,连接池创建了一个新的连接,而这条连接并没有持有表锁。

  • 在需要时,而且连接池中没有可用的连接时,连接才被创建。

  • 默认情况下连接数量没有限制,想创建多少就有多少。但服务器允许的连接数往往是有限的。

  • db.SetMaxIdleConns(N)来限制连接池中空闲连接的数量,但是这并不会限制连接池的大小。连接回收(recycle)的很快,通过设置一个较大的N,可以在连接池中保留一些空闲连接,供快速复用(reuse)。但保持连接空闲时间过久可能会引发其他问题,比如超时。设置N=0则可以避免连接空闲太久。

  • db.SetMaxOpenConns(N)来限制连接池中打开的连接数量。

  • db.SetConnMaxLifetime(d time.Duration)来限制连接的生命周期。连接超时后,会在需要时惰性回收复用。

9. 微妙行为

database/sql并不复杂,但某些情况下它的微妙表现仍然会出人意料。

9.1 资源耗尽

不谨慎地使用database/sql会给自己挖许多坑,最常见的问题就是资源枯竭(resource exhaustion):

  • 打开和关闭数据库(sql.DB)可能会导致资源枯竭;
  • 结果集没有读取完毕,或者调用rows.Close()失败,结果集会一直占用池里的连接;
  • 使用Query()执行一些不返回结果集的语句,返回的未读取结果集会一直占用池里的连接;
  • 不了解准备语句(Prepared Statement)的工作原理会产生许多额外的数据库访问。

9.2 Uint64

Go底层使用int64来表示整型,使用uint64时应当极其小心。使用超出int64表示范围的整数作为参数,会产生一个溢出错误:

// Error: constant 18446744073709551615 overflows int
_, err := db.Exec("INSERT INTO users(id) VALUES", math.MaxUint64) 

这种类型的错误非常不容易发现,它可能一开始表现的很正常,但是溢出之后问题就来了。

9.3 不合预期的连接状态

连接的状态,例如是否处于事务中,所连接的数据库,设置的变量等,应该通过Go的相关类型来处理,而不是通过SQL语句。用户不应当对自己的查询在哪条连接上执行作任何假设,如果需要在同一条连接上执行,需要使用Tx

举个例子,通过USE DATABASE改变连接的数据库对于不少人是习以为常的操作,执行这条语句,只影响当前连接的状态,其他连接仍然访问的是原来的数据库。如果没有使用事务Tx,后续的查询并不能保证仍然由当前的连接执行,所以这些查询很可能并不像用户预期的那样工作。

更糟糕的是,如果用户改变了连接的状态,用完之后它成为空连接又回到了连接池,这会污染其他代码的状态。尤其是直接在SQL中执行诸如BEGINCOMMIT这样的语句。

9.4 驱动的特殊语法

尽管database/sql是一个通用的抽象,但不同的数据库,不同的驱动仍然会有不同的语法和行为。参数占位符就是一个例子。

9.5 批量操作

出乎意料的是,标准库没有提供对批量操作的支持。即INSERT INTO xxx VALUES (1),(2),...;这种一条语句插入多条数据的形式。目前实现这个功能还需要自己手动拼SQL。

9.6 执行多条语句

database/sql并没有对在一次查询中执行多条SQL语句的显式支持,具体的行为以驱动的实现为准。所以对于

_, err := db.Exec("DELETE FROM tbl1; DELETE FROM tbl2") // Error/unpredictable result

这样的查询,怎样执行完全由驱动说了算,用户并无法确定驱动到底执行了什么,又返回了什么。

9.7 事务中的多条语句

因为事务保证在它上面执行的查询都由同一个连接来执行,因此事务中的语句必需按顺序一条一条执行。对于返回结果集的查询,结果集必须Close()之后才能进行下一次查询。用户如果尝试在前一条语句的结果还没读完前就执行新的查询,连接就会失去同步。这意味着事务中返回结果集的语句都会占用一次单独的网络往返。

10. 其他

本文主体基于[[Go database/sql tutorial]]([Go database/sql tutorial]),由我翻译并进行一些增删改,修正过时错误的内容。转载保留出处。