PostgreSQL 18 十三组实测:部分索引省 95% 空间,BRIN 索引 24KB 就够用

举报
Eveline 发表于 2026/11/09 12:43:22 2026/11/09
【摘要】 PostgreSQL 18 到底快在哪?作者在一台 MacBook Air 上用 200 万行订单表做了十三组小实测:部分索引把 14.2MB 压到 0.7MB,BRIN 索引只用 24KB 就跟上 B 树,外键缺索引让级联删除慢约 277 倍;写入、排序、并行各有几组反直觉的数字。没有大道理,全是能直接照着复现的测量结果。

原文信息:本文译自《Thirteen small measurements on PostgreSQL 18》,作者 Hollis Varden,发布于 2026 年 11 月 8 日。原文链接:https://explainmeasured.com/posts/thirteen-measurements-pg18/ 。原文未注明许可信息。本文为翻译,译者在文末补充了译者注。

译者导语:PostgreSQL 18 发布一个多月,官方发布说明里列了不少新特性,但真正落到日常查询上,哪些设置值得动、哪些索引值得建,光看文档心里还是没底。Hollis Varden 干脆自己动手,在一台 MacBook Air 上用 200 万行订单表做了十三组小测量:索引、写入、排序、并行,每组都有具体数字。没有宏大结论,但每组结果都够直接拿去对照自己的库。

测试环境

PostgreSQL 18.6,从源码编译,默认配置(work_mem 4MB、shared_buffers 128MB、每次 Gather 2 个并行 worker)。机器是 MacBook Air M5,10 核、24GB 内存、内置 SSD。这台机器跟作者以前文章用的不是同一台,所以不要拿这里的耗时跟他以前的文章横向比较。

多数测试用同一张 200 万行订单表:5 万个客户,5% 的行状态为 open,created_at 每 10 秒递增一条,note 字段里是一串 md5 字符串。查询耗时取 EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) 的执行时间,中位数:预热一次后跑 5 次取中位数,慢的测试跑 3 次。Buffers 取顶层计划节点的数值。大小单位是十进制 MB。测试脚本和原始结果见文末。

索引

部分索引(partial index)只存 WHERE 子句允许的行。

customer_id 上的索引 大小 查一个客户的未完成订单所用 Buffers
全部行 14.2 MB 43
WHERE status = 'open' 0.7 MB 2

只有查询的 WHERE 条件跟索引条件对得上时,这个索引才会被用上。

BRIN 索引不给每行都建条目,而是按块范围(block range)存一份摘要。测试数据是按 created_at 顺序插进去的,正是 BRIN 最擅长的场景。

created_at 上的索引 大小 统计一天的数据(8,640 行)
B 树 44.9 MB 0.55 ms,134 buffers
BRIN 24 kB 0.63 ms,137 buffers

表本身是 202.3 MB。作者没测打乱顺序的数据——那种情况下 BRIN 拿不出有用的摘要。

INCLUDE 可以让索引带上一个不参与排序的列。统计某一个客户 40 行的 sum(amount):

索引 大小 Buffers 耗时
(customer_id) 14.2 MB 43 0.073 ms
(customer_id) INCLUDE (amount) 45.0 MB 4 0.038 ms

体积暴涨的原因是去重(deduplication)。从 PostgreSQL 13 起,B 树会把重复的键只存一次,后面挂一串行指针,而 INCLUDE 索引做不到这一点。手动关掉去重再测:

索引 开启去重 关闭去重
status(2 个取值) 13.9 MB 45.0 MB
customer_id(50,000 个取值) 14.2 MB 45.0 MB
主键(全部唯一) 44.9 MB

索引仅扫描(index-only scan)也不是真的完全不碰表:只要还有页面没被 VACUUM 标成全部可见(all-visible),扫描时就得回表确认。在一台刚导完数据、关了 autovacuum 的表上扫描 200,001 行:

Heap fetches Buffers 耗时
VACUUM 之前 200,001 1,917 7.1 ms
VACUUM 之后 0 553 4.4 ms

两次的执行计划都写着 Index Only Scan。

PostgreSQL 会给主键自动建索引,但不会给外键列建。10,000 个父行、1,000,000 个子行,ON DELETE CASCADE,删除 200 个父行(连带 20,000 个子行):child.parent_id 上没有索引花了 3.6 秒,有索引只花了 0.013 秒(相差约 277 倍)。

写入

用 psql 通过本地 socket 导 200,000 行,每次一个事务,取 3 次中位数:

方法 耗时
\copy 0.039 s
INSERT,每条语句 1,000 行 0.133 s
INSERT,每条语句 1 行 2.1 s

往空表插 2,000,000 行:

表 耗时 WAL
普通表 1.78 s 192.6 MB
UNLOGGED 表 1.00 s 0 MB

UNLOGGED 表崩溃后会被清空,而且不参与复制。

再看提交:用 pgbench 跑 10,000 次单行 INSERT,一个客户端,每条语句自成一个事务。synchronous_commit = on 时每秒 28,101 个事务,off 时 64,478 个。关掉它,崩溃时可能丢掉最后几次提交,但不会损坏数据。这台 Mac 的 fsync 很便宜(wal_sync_method 是 open_datasync),换到真正会落盘的服务器磁盘上,差距可能不一样——作者没测那一种。

最后是对一张没有索引的表全表 UPDATE 每一行,表大小的变化:

步骤 表大小
更新前 202.5 MB
UPDATE 之后 404.7 MB
VACUUM 之后 404.7 MB
第二次全表 UPDATE 之后 404.7 MB

每次更新都会写一个新版本的行。VACUUM 没有把空间还给操作系统,但让第二次 UPDATE 复用了这些空间。

排序与并行

关掉并行,对 200 万行按 note 排序。ORDER BY note LIMIT 10 花了 119 ms,用的是 26 kB 的 top-N 堆排序(heapsort);全量排序花了 571 ms。两者都要读遍每一行。

同样的全量排序,换三档 work_mem:

work_mem 耗时 排序方式
4MB(默认) 433 ms 外部归并,74 MB 落盘
64MB 297 ms 快速排序,50 MB 在内存里
256MB 295 ms 快速排序,50 MB 在内存里

超过排序实际需要的内存,再加也没用。注意 work_mem 是按每个查询里的每次排序或哈希单独算的,所以全局设得太大,代价会成倍放大。

最后是对全部 200 万行做 LIKE '%ab%' 过滤再 GROUP BY:

max_parallel_workers_per_gather 耗时
0 151 ms
2(默认) 54 ms
4 44 ms

每次运行读的页面都一样。两个并行 worker 拿走了大部分收益。

测试脚本与原始数据

测试脚本和原始结果见原文链接:run.py:上面所有测试,按顺序;brin_size.py:BRIN 索引的精确字节数(主测试里四舍五入成了 0.0 MB);results.json:原始输出,包括每一次运行。

译者注

  1. 数字都是相对值:测试机是 MacBook Air M5、默认配置,作者自己也提醒别拿这组数字跟他以前的文章横向比。落到自己的环境,重点看趋势和量级——比如部分索引省 95% 的空间、外键缺索引让级联删除慢两百多倍,这些结论换机器也大体成立,具体毫秒数会变。
  2. 几个容易踩的坑:BRIN 只适合按顺序写入的列(时间戳、自增 ID),数据打乱了就没用;索引仅扫描不是真的不碰表,没 VACUUM 过的页面照样要回表查可见性;UNLOGGED 表崩了就清空、也不参与复制,别拿它存重要数据。
  3. 和华为云的结合点:GaussDB 同样支持部分索引、覆盖索引这类思路,文中的实测方法可以直接复用;华为云数据库的慢 SQL 诊断、索引推荐能力,能帮你把“凭感觉加索引”换成“按数据说话”。

相关阅读(同方向旧文,纯文本链接): - AI 智能体直接改数据库,约束谁来守?PostgreSQL 约束索引让跨表校验提速最高 2.41 倍:https://bbs.huaweicloud.com/blogs/829a4a73dede49a0b89ffd0a1f9bff3e - 学习型基数估计如何在动态数据库里落地:SWatcher+ 的选择性监控与主动纠偏:https://bbs.huaweicloud.com/blogs/e6f5085214054f46bb2c54e52ee9fd20

原文:Thirteen small measurements on PostgreSQL 18,Hollis Varden,Explain, Measured,2026-11-08。原文链接:https://explainmeasured.com/posts/thirteen-measurements-pg18/

【版权声明】本文为华为云社区用户翻译文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容, 举报邮箱:cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。