PostgreSQL 19:第一部分 或 CommitFest 2025-07
来源: habr.com
我们正在启动一个关于 PostgreSQL 19 即将到来变化的新系列文章。第一篇文章聚焦于去年夏天的七月 CommitFest 中的更新内容。
📑 目录
- 连接服务文件:支持 libpq 参数和 psql 变量
- regdatabase:用于数据库标识符的类型
- pg_stat_statements:通用计划与自定义计划计数器
- pg_stat_statements:FETCH 命令规范化
- pg_stat_statements:IN 子句中的参数列表命令规范化
- EXPLAIN:Memoize 节点估算
- btree_gin:整数类型的比较运算符
- pg_upgrade:优化大对象迁移
- 优化临时表截断
- 规划器:Append 和 MergeAppend 节点中的增量排序
- 域约束验证不再阻塞 DML 操作
- CHECKPOINT 命令参数
- COPY FROM:跳过初始行
- pg_dsm_registry_allocations:动态共享内存使用
🔌 连接服务文件:支持 libpq 参数和 psql 变量
提交: [092f3c63efc6], [6b1c4d326b06]
当使用连接服务文件连接到服务器时,现在可以通过连接字符串中的新参数 servicefile 指定文件名。
例如,当前目录下有一个 demo.conf 文件:
bash
$ cat ./demo.conf
[demo]
host=localhost
port=5401
user=postgres
dbname=demo
options=-c search_path=bookings
你可以使用它进行连接:
bash
$ psql 'servicefile=./demo.conf service=demo'
psql (19devel)
Type "help" for help.
postgres@demo(19.0)=#
在 psql 中,新增了一个 SERVICEFILE 变量用于指定服务文件名:
sql
postgres@demo(19.0)=# \echo :SERVICEFILE
./demo.conf
🏷️ regdatabase:用于数据库标识符的类型
提交: [bd09f024a]
对象标识符别名类型家族的新成员。regdatabase 类型支持数据库名称与其标识符之间的双向转换。
sql
SELECT current_database()::regdatabase,
current_database()::regdatabase::oid
\gx
-[ RECORD 1 ]----+------
current_database | demo
current_database | 16561
📊 pg_stat_statements:通用计划与自定义计划计数器
提交: [3357471cf]
pg_stat_statements 视图现在新增两列,用于跟踪查询执行中选择通用计划和自定义计划的次数:
sql
SELECT pg_stat_statements_reset();
SELECT * FROM bookings WHERE book_ref = $1 \bind 'NWQI2S' \g /dev/null
SELECT * FROM bookings WHERE book_ref = $1 \bind 'WF2DGZ' \g /dev/null
SELECT query, calls, generic_plan_calls, custom_plan_calls
FROM pg_stat_statements
WHERE query ~ 'bookings'
\gx
-[ RECORD 1 ]------+-------------------------------------------
query | SELECT * FROM bookings WHERE book_ref = $1
calls | 2
generic_plan_calls | 0
custom_plan_calls | 2
🔄 pg_stat_statements:FETCH 命令规范化
提交: [bee23ea4d]
在规范化 FETCH 命令时,获取行数现在被替换为常量。这意味着获取不同行数的 FETCH 命令将共享相同的查询标识符,并在 pg_stat_statements 中显示为单一条目:
sql
SELECT pg_stat_statements_reset();
BEGIN;
DECLARE cur CURSOR FOR SELECT * FROM flights;
FETCH 1 cur\g /dev/null
FETCH 2 cur\g /dev/null
FETCH -1 cur\g /dev/null
COMMIT;
SELECT queryid, query, calls
FROM pg_stat_statements
WHERE query ~ '^FETCH'
\gx
-[ RECORD 1 ]----------------
queryid | 4164749676997500190
query | FETCH $1 cur
calls | 3
📝 pg_stat_statements:IN 子句中的参数列表命令规范化
提交: [c2da1a5d6]
带有 IN 子句中常量列表的命令已在版本 18 中实现了规范化。在版本 19 中,这一规范化不仅适用于常量,还适用于参数列表:
sql
SELECT pg_stat_statements_reset();
SELECT * FROM flights WHERE flight_id IN (1,2) \g /dev/null
SELECT * FROM flights WHERE flight_id IN (1,2,3) \g /dev/null
SELECT * FROM flights WHERE flight_id IN ($1,$2) \bind 11 12 \g /dev/null
SELECT * FROM flights WHERE flight_id IN ($1,$2,$3) \bind 21 22 23 \g /dev/null
SELECT * FROM flights WHERE flight_id IN ($1,$2,$3,$4) \bind 31 32 33 34 \g /dev/null
SELECT queryid, query, calls
FROM pg_stat_statements
WHERE query ~ 'flights'
-[ RECORD 1 ]-----------------------------------------------------
queryid | -5928905334469394952
query | SELECT * FROM flights WHERE flight_id IN ($1 /*, ... */)
calls | 5
这对于使用扩展查询协议的应用程序特别有用。
🔍 EXPLAIN:Memoize 节点估算
提交: [4bc62b86849]
为了帮助理解规划器为何选择使用 Memoize 节点,EXPLAIN 输出现在包含该节点的规划器估算值(显示在 Estimates 行中):
sql
EXPLAIN
SELECT * FROM routes r
JOIN airports_data a ON r.departure_airport = a.airport_code
WHERE r.days_of_week = '{1,2,3,4,5,6,7}';
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.29..212.71 rows=898 width=293)
-> Seq Scan on routes r (cost=0.00..111.12 rows=898 width=103)
Filter: (days_of_week = '{1,2,3,4,5,6,7}'::integer[])
-> Memoize (cost=0.29..1.08 rows=1 width=190)
Cache Key: r.departure_airport
Cache Mode: logical
Estimates: capacity=73 distinct keys=73 lookups=898 hit percent=91.87%
-> Index Scan using airports_data_pkey on airports_data a (cost=0.28..1.07 rows=1 width=190)
Index Cond: (airport_code = r.departure_airport)
(9 rows)
此行显示规划器对以下指标的估算:
- 哈希表大小
- 哈希表中唯一元素的数量
- 哈希表被查找的次数
- 命中率
使用 EXPLAIN 的 analyze 参数运行可显示估算与实际结果之间的差异。以下是同一查询执行计划中的两个相关行:
...
Estimates: capacity=73 distinct keys=73 lookups=898 hit percent=91.87%
Hits: 830 Misses: 68 Evictions: 0 Overflows: 0 Memory Usage: 19kB
...
🔧 btree_gin:整数类型的比较运算符
提交: [e2b64fcef35], [fc896821c44]
为 btree_gin 扩展的用户提供的小便利。
sql
CREATE EXTENSION btree_gin;
CREATE TABLE t (a bigint, b text);
INSERT INTO t SELECT random(1,100), random()::text FROM generate_series(1, 100000);
CREATE INDEX idx ON t USING GIN (a, b gin_trgm_ops);
VACUUM ANALYZE t;
SELECT * FROM t WHERE a = 42 AND b LIKE '*42*';
以前,对于此类查询,规划器只会使用列 b 条件的索引,而不会将 a = 42 纳入索引搜索条件,因为列 a 的类型是 bigint,而 42 默认为 int。这需要在查询中显式指定 a = 42::bigint。
现在 btree_gin 已得到改进,int2、int4 和 int8 将自动转换为适当的类型以供索引使用。
sql
EXPLAIN SELECT * FROM t WHERE a = 42 AND b LIKE '*42*';
QUERY PLAN
--------------------------------------------------------------------
Bitmap Heap Scan on t (cost=229.49..233.51 rows=1 width=27)
Recheck Cond: ((a = 42) AND (b ~~ '*42*'::text))
-> Bitmap Index Scan on idx (cost=0.00..229.49 rows=1 width=0)
Index Cond: ((a = 42) AND (b ~~ '*42*'::text))
(4 rows)
🚀 pg_upgrade:优化大对象迁移
提交: [161a3e8b682], [3bcfcd815e1]
当从版本 12 及以上版本升级时,带有 --binary-upgrade 参数的 pg_dump 会为大对象生成 COPY 命令,将数据直接插入 pg_largeobject_metadata 和 pg_shdepend 表中。
从这样的转储中恢复比执行 ALTER LARGE OBJECT 命令分配所有权和 GRANT 命令设置权限要快得多。因此,pg_upgrade 升级包含大量大对象的服务器将花费更少的时间。
从版本 16 及更高版本升级(第二个提交)将更快,因为 pg_largeobject_metadata 表文件将像普通用户表文件一样被迁移。
不带 --binary-upgrade 参数的 pg_dump 继续像往常一样使用 SQL 命令导出大对象元数据。
⚡ 优化临时表截断
提交: [78ebda66bf2]
以前截断临时表需要为每个分支(主关系、可见性映射和空闲空间映射)扫描本地缓冲区缓存(temp_buffers)三次。现在一次扫描就足够了。这对于大量使用临时表的应用程序来说是一个非常有价值的优化。
普通表的类似优化早在 PostgreSQL 13 中就已实现。
📐 规划器:Append 和 MergeAppend 节点中的增量排序
提交: [55a780e9476]
规划器现在可以为嵌套在 Append 或 MergeAppend 中的节点利用增量排序。当嵌套节点可以产生部分排序的数据时,规划器将倾向于增量完成排序,而不是对整个结果集执行完整排序。
🔓 域约束验证不再阻塞 DML 操作
提交: [16a0039dc0d]
ALTER DOMAIN ... VALIDATE CONSTRAINT ... 命令现在使用较轻的 SHARE UPDATE EXCLUSIVE 锁,而不是之前的 SHARE 锁。这允许对使用该域的表进行并发修改而不会阻塞。
⚙️ CHECKPOINT 命令参数
提交: [cd8324cc89a], [bb938e2c3c7], [a4f126516e6], [2f698d7f4b7], [8d33fbacbac]
CHECKPOINT 命令现在支持参数:
sql
=# \h checkpoint
Command: CHECKPOINT
Description: force a write-ahead log checkpoint
Syntax:
CHECKPOINT [ ( option [, ...] ) ]
where option can be one of:
FLUSH_UNLOGGED [ boolean ]
MODE { FAST | SPREAD }
URL: https://www.postgresql.org/docs/devel/sql-checkpoint.html
当启用 FLUSH_UNLOGGED 时,未记录对象(表、索引等)的修改缓冲区将被刷新到磁盘。此参数默认禁用。
此参数有助于在大量使用未记录表时减少服务器重启时间。在关闭服务器之前,可以手动运行 CHECKPOINT,以便在关闭期间后续的检查点完成得更快。以前,未记录对象的脏缓冲区不会被检查点进程刷新到磁盘------这仅在关闭期间发生。现在,通过手动运行带有 FLUSH_UNLOGGED ON 的检查点,可以在服务器停止前卸载更多工作。
为了避免给系统带来过多的写入负载,可以使用另一个新参数 MODE SPREAD 运行预备检查点。检查点将随时间分散进行,并考虑 checkpoint_completion_target 值。这类似于 pg_basebackup 工具中的 --checkpoint={fast|spread} 参数。
📄 COPY FROM:跳过初始行
提交: [bc2f348e87c]
COPY 命令的 header 参数现在除了接受 true/false 和 match 之外,还可以接受一个正数值。这个数字指定命令在开始将数据加载到表之前应跳过的初始行数。
sql
CREATE TABLE test(id int);
=# COPY test FROM stdin (header 3);
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> first header row
>> second header row
>> third, then data
>> 1
>> 2
>> \.
COPY 2
SELECT * FROM test;
id
----
1
2
(2 rows)
请注意,在这种情况下,您将无法使用 header match 选项在文件和表之间映射列。
📊 pg_dsm_registry_allocations:动态共享内存使用
提交: [167ed8082f4]
新的系统视图 pg_dsm_registry_allocations 提供有关动态共享内存分配的信息。
sql
=# \d pg_dsm_registry_allocations
View "pg_catalog.pg_dsm_registry_allocations"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+---------
name | text | | |
type | text | | |
size | bigint | | |