PostgreSQL 18 实锤:默认排序规则正在白白浪费 30%~400% 的
刘峰DBA
2026年02月02日 16:27

在 PostgreSQL 中,排序规则(Collation)往往是最容易被忽略,却最致命的性能选项之一。

很多人建表时习惯性写下:

text COLLATE "en_US.utf8"

然后就再也没管过它。

直到某一天你发现:

  • 索引构建慢得离谱

  • ORDER BY CPU 飙升

  • 数据量一上来,性能线性崩塌

你可能会怀疑:

👉 是数据量太大?

👉 是索引设计不合理?

👉 是 PostgreSQL 本身“不够快”?

但在 PostgreSQL 18 里,真相可能非常简单——你只是选错了排序规则。

本文将通过 500 万行真实压测数据,

从内核原理到实战结果,彻底回答三个关键问题:

  • 为什么 OS Locale 会成为 CPU 杀手?

  • Built-in Collation 到底快在哪里?

  • PostgreSQL 18 下,排序规则该怎么选才不踩坑?

如果你正在使用 PG 18,这可能是你今年最值得花 10 分钟读完的一篇文章。

01

深度原理:为什么排序规则是

性能的“命门”?

在 PostgreSQL 中,Collation (排序规则) 决定了字符串如何进行比较(Compare)和排序(Sort)。

这不仅仅是“谁排在前、谁排在后”的问题,它直接决定了数据库底层调用哪套 C 语言函数库。

在 PG 18 中,我们主要面临三种选择,它们的底层机制天差地别:

1

COLLATE "C" (Raw / Memcmp)

  • 底层机制

按字节逐位比较 (Byte-wise)。

  • 核心函数

直接调用 C 标准库的 memcmp()。

  • 原理详解

它完全忽略字符集、语言习惯和 Unicode 规则。

它只看二进制编码的大小。

例如,在 ASCII 码中 Z (90) 小于 a (97)。

  • 性能特征

这是 CPU 指令级别的比较,没有任何逻辑开销,代表了物理硬件的极限速度。

但缺点是无法处理人类语言的复杂排序习惯(如中文拼音)。

2

COLLATE "en_US.utf8" (OS Libc / Glibc)

  • 底层机制

依赖操作系统的语言学比较。

  • 核心函数

调用操作系统库(如 Linux 的 glibc)的 strcoll()。

  • 原理详解

这是最重的实现方式。

PG 进程必须将字符串传递给操作系统,操作系统需要进行复杂的计算:

a. 标准化 (Normalization):处理 Unicode 等价性(如字符 é 可以是单字符,也可以是 e + ´ 组合)。

b. 多级权重 (Weighting):查表处理主次权重(忽略大小写、处理重音、处理连字等)。

c. 上下文切换:从 PG 用户态到 OS 库函数的频繁调用。

  • 性能特征

这是绝大多数生产环境的默认配置,也是CPU 杀手。

3

COLLATE "pg_c_utf8" (PG 18 Built-in)

  • 底层机制

PG 内核内置实现 (Provider: b)。

  • 核心函数

PG 源码中的 uni_strcoll() 等内部实现。

  • 原理详解

这是 PostgreSQL 17/18 的革命性改进。

PG 不再把任务外包给操作系统,而是自己实现了一套高效的 UTF-8 排序逻辑。

1)去重

剔除了 glibc 中许多不常用的、沉重的语言学规则。

2)内联

减少了外部库调用的上下文切换开销。

3)不可变 (Immutable)

这是最大的架构优势。

OS 升级(如 glibc 版本变化)会导致索引损坏,而 Built-in 永远不变。

  • 性能特征

试图在“正确处理 UTF-8”和“高性能”之间找到最佳平衡点。

02

实验环境与数据准备

我们构建了一张包含 500 万行数据的宽表,确保三列数据完全一致。

1

建表与数据灌入

03

实战 A —— 索引构建 (Index

Build) 三轮压测

这是 CPU 密集型操作的大考。

为了消除偶发波动,我们进行了三轮完整的测试。

1

实验背景:寻找 V18 的性能红利

在 PostgreSQL 18 中,社区引入了 Built-in Collation Provider,旨在摆脱操作系统 glibc 的性能束缚。

为了验证这一特性的真实威力,我们在同一环境下进行了三轮严苛的索引构建(Index Build)测试。

  • 测试对象

500 万行随机文本数据。

  • 测试动作

CREATE INDEX (CPU 密集型操作)。

  • 对比选手:

a. Raw "C"

物理极限(仅比较字节)。

b. Built-in "pg_c_utf8"

PG 18 内置实现(UTF-8 感知)。

c. OS Libc "en_US.utf8"

操作系统默认实现(传统方式)。

2

实战数据:三轮测试全记录

第一轮测试 (冷启动/Warm-up)

环境状态:数据刚写入,可能部分未进缓存。

第二轮测试 (稳定期)

环境状态:缓存预热中。

第三轮测试 (热数据巅峰)

环境状态:完全热数据,性能释放。

我们对同一批数据进行了三次反复的索引构建,以消除缓存预热(Warm-up)和磁盘 I/O 的偶发影响。

数据汇总表

3

深度技术剖析

OS Libc 的全面溃败 (平均 13.47s)

这是本实验最震撼的发现。使用操作系统默认的 en_US.utf8,索引构建速度平均约为 13.5秒。

  • 原因

每次字符串比较,PG 都要切换上下文调用操作系统的 strcoll(),并进行繁重的语言学权重计算(Weighting)。

  • 代价

相比 Built-in,它慢了接近 3 倍。

这意味着如果你的生产库使用默认配置,浪费了 65% 的 CPU 资源在无意义的排序逻辑上。

Built-in 的惊人表现 (平均 4.71s)

  • 无限逼近物理极限

在第三轮测试中,Built-in (3.5s) 甚至跑赢了 Raw C (3.8s)。

虽然这是系统调度波动,但足以证明 Built-in 的开销已经低到可以忽略不计。

  • 技术红利

PG 18 通过内置实现,剔除了 OS 库中冗余的逻辑,实现了与 memcmp (C) 几乎同级的效率,同时还保证了 UTF-8 的正确性。

Raw "C" 的基准地位 (平均 4.17s)

它依然是最稳的基准线。

但在 V18 时代,Built-in 已经追平了它。

考虑到 Built-in 能处理多字节字符的边界问题,"C" 的唯一优势只剩下处理非 UTF-8 数据的纯字节流场景。

4

为什么第三轮 Built-in 会反超 C?

在第三轮中,Built-in (3.54s) 竟然比 C (3.83s) 还快,这看似不科学(逻辑比较怎么可能快过字节比较?),但在高并发或热数据场景下是有可能的:

CPU 流水线与分支预测

PG 18 的 Built-in 实现经过高度优化,代码路径可能更利于现代 CPU 的分支预测。

内存对齐

在特定的内存页分布下,Built-in 处理的数据可能恰好命中更多的 L1/L2 Cache。

结论

这说明两者处于同一重量级。

在 V18 中,Built-in 就是带了 UTF-8 校验功能的 "C"。

04

实战 B —— 查询与排序

(Select & Sort)

我们继续对“读性能”进行压测,分为 走索引 和 不走索引 两个极端场景。

1

场景 1:索引扫描 (Index Scan) ——

毫秒级较量

实验背景:微秒级的极致响应

本次测试我们将视角转向数据库最常见的 OLTP 场景:利用 B-Tree 索引进行的极速点查。

我们将环境锁定为 全内存热数据 (Hot Cache),确保索引页(Index Pages)完全驻留在内存中。

  • 数据量:500 万行。

  • 内存配置:shared_buffers = 1GB (确保 Heap 和 Index 全部热加载进内存,无物理 I/O)。

  • 测试场景:ORDER BY ... LIMIT 1 (利用 B-Tree 索引直接定位最小值)。

  • 核心看点:

  • 当算法复杂度从 O(N)O(N) 降维打击到 O(log⁡N)O(logN) 时,查询响应时间被压缩到了 微秒级 (0.x ms)。

  • 在这种极致的低延迟下,Collation 函数调用的启动开销 (Overhead) 和CPU 流水线效率将成为决胜的关键。我们试图捕捉那 0.1 毫秒的微小差异。

实验过程:索引扫描场景

实验结论:三轮实测数据汇总 (单位: 毫秒)

深度结论分析

a. 微秒级的战争 (0.2ms - 0.4ms)

在热数据下,三个选手的差距已经缩小到了 0.1毫秒 级别。

这说明当使用了 B-Tree 索引后,PG 只需要读取极少量的内存页(Page),排序规则的 CPU 开销被最大程度摊薄了。

b. Built-in 的惊人反超 (0.252 ms)

在第二轮和第三轮中,Built-in 居然跑出了全场最快的数据,甚至快于 Raw C。

  • 原因推测

虽然理论上 C 最快,但在微秒尺度下,CPU 缓存命中率(Cache Locality)和指令流水线优化起主导作用。PG 18 的 Built-in 实现代码可能更加“现代”和紧凑,导致其在特定场景下的分支预测极其高效。

  • 核心意义

这证明了 Built-in Collation 没有任何运行时负担。

它就是“带了 UTF-8 校验功能的 C”。

2

场景 2:全表扫描 + 内存排序 (Seq Scan +

Sort) —— CPU 算力大考

实验背景:纯粹的 CPU 算力大考

在之前的测试中,我们分别看到了 IO 瓶颈(冷启动)和全量排序(Sort)的表现。 本次测试我们将环境锁定为:内存热数据 (Hot Cache)。

  • 数据量

500 万行。

  • 内存配置

shared_buffers = 1GB (足以容纳全表)。

  • 测试场景

ORDER BY ... LIMIT 1(全表扫描 + Top-1 堆排序)。

  • 核心看点

当磁盘 I/O 不再是瓶颈时,三种排序规则在 CPU 指令层面的微小差异将被放大 500 万倍。

实验过程:内存热数据排序压测

为了排除磁盘 I/O 干扰,我们利用刚才跑过的数据(已在内存中),测试纯 CPU 性能。

实战数据:三轮热数据测试全记录

为了确保数据准确,我们连续进行了三轮测试,结果极其稳定。

数据汇总表 (单位: ms)

深度技术剖析

1)Raw "C" 的回归 (374ms)

在排除了磁盘 I/O 的干扰后,COLLATE "C" 终于回到了它应有的王座。

  • 性能基准

374ms 代表了遍历 500 万行数据并进行 500 万次 memcmp 操作的物理时间下限。

  • 对比意义

它是所有其他算法想要追求的“光速”。

2)Built-in 的惊艳表现 (393ms)

这是本次实验最令人振奋的数据!

  • 差距仅 5%

PG 18 的内置实现(393ms)仅比纯字节比较(374ms)慢了 19毫秒!

  • 这意味着什么?

这意味着 PostgreSQL 18 的内核团队已经将 UTF-8 的处理逻辑优化到了极致。

它在保证了“UTF-8 字符正确性”的同时,性能几乎没有损耗。

  • 技术红利

你可以放心地使用 Built-in 来处理中文、日文或 emoji,而不用担心比 Raw C 慢多少。

3)OS Libc 的严重滞后 (516ms)

  • 落后 31%

相比 Built-in,操作系统默认的 glibc 实现慢了 123毫秒。

  • 为什么慢?

在 500 万次比较中,每一次调用操作系统的 strcoll() 都要比调用 PG 内部函数多消耗一点点 CPU 周期(用于查权重表、处理语言学规则)。

积少成多,最终导致了 30% 的性能惩罚。

05

最终技术总结

通过这场涵盖 500万行数据 的全场景压测,PostgreSQL 18 的性能表现已无可争议:

基于以上三轮索引构建和三轮全表排序的详实数据,我们对 PostgreSQL 18 的性能表现得出最终结论:

  • Built-in (pg_c_utf8) 是通用文本的最佳选择

在索引构建中,它比 OS Libc 快 4 倍 (3.5s vs 15.9s);在全表排序中,它比 OS Libc 快 30%。它兼顾了高性能和 UTF-8 正确性。

  • Raw (C) 依然是物理极限

对于不需要 UTF-8 校验的内部编码(UUID/Hash),它配合索引能提供 2.9ms 的极致响应。

  • OS Libc (en_US.utf8) 慎重使用

无论是在写(索引构建)还是读(排序)场景下,它都是最慢的。

在 V18 中继续使用它是对硬件资源的极大浪费。

V18 最佳实践铁律

1)通用文本字段,无脑选 Built-in

实验数据证明,COLLATE "pg_c_utf8"在索引构建上比 OS 快 400%,在查询上甚至能反超 Raw C。

它是 V18 时代通用文本处理的唯一真神。

2)慎重使用 OS Locale

en_US.utf8 在所有测试中均垫底。它不仅慢,还存在升级导致索引损坏的风险。

3) Raw C 依然是底座

对于不需要 UTF-8 校验的内部编码(UUID/Hash),COLLATE "C" 依然是稳健的选择。

写在最后

这场覆盖 索引构建、索引扫描、全表排序 的完整实测,其实已经给 PostgreSQL 18 用户一个非常明确的答案:

排序规则,不再只是“语言习惯”的选择,而是纯粹的性能决策。

在 PG 18 之前,我们几乎别无选择,只能把排序逻辑外包给操作系统,忍受 glibc 带来的性能损耗和升级风险。

但在 PostgreSQL 18 之后,情况已经彻底改变:

  • Built-in Collation 用接近 memcmp 的速度,完成 UTF-8 正确排序

  • 索引构建速度提升 3~4 倍

  • 全表排序 CPU 成本直接下降 30%

  • 还顺手解决了“OS 升级导致索引失效”的历史顽疾

对今天的 DBA 和架构师来说,继续无脑使用 en_US.utf8,已经不再是“稳妥”,而是一种浪费。

PostgreSQL 18 真正带来的改变,不是“又快了一点”,而是让我们终于可以:

👉 把性能关键路径,牢牢掌握在数据库内核本身。

如果说 PG 18 有什么“必用新特性”,Built-in Collation,毫无疑问就在第一梯队。

原文链接:网页链接​