由 John Doe 四月 14, 2025
你有没有注意到 PostgreSQL 进行某些变更操作后,会出现统计信息失效的情况?现在,PostgreSQL 提供了可以调整关系的统计信息的函数。
特性提交日志
创建函数pg_set_relation_stats
、pg_clear_relation_stats
。
这些函数用于调整任何关系的统计信息,前提是用户对关系拥有维护特权,或者是数据库所有者。
讨论:https://postgr.es/m/CADkLM=eErgzn7ECDpwFcptJKOk9SxZEk5Pot4d94eVTZsvj3gw@mail.gmail.com
示例
到目前为止,PostgreSQL 中一直没有官方的手动调整优化器统计信息的方法。虽然在其他关系数据库中,这是相当常见的操作,但 PostgreSQL 此前并未提供相关功能。如今,PostgreSQL 18 为实现这一功能迈出了第一步。
在了解新增的两个函数之前,我们先创建一个小的测试表和一个索引:
create table t ( a int, b text );
insert into t values (1,'aa');
insert into t select i, 'bb' from generate_series(2,100) i;
analyze t;
create index i on t(b);
postgres=# \dt
Table "public.t"
Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+---------
a | integer | | |
b | text | | |
Indexes:
"i" btree (b)
目前,该表的页数和行数显示如下:
select relpages,reltuples from pg_class where relname = 't';
relpages | reltuples
----------+-----------
1 | 100
(1 row)
对表进行一个简单的查询,在b
列上限制为'aa'
,并查看其查询计划,会得到顺序扫描的结果:
explain select * from t where b = 'aa';
QUERY PLAN
-------------------------------------------------
Seq Scan on t (cost=0.00..2.25 rows=1 width=7)
Filter: (b = 'aa'::text)
(2 rows)
由于该表仅由一个数据块组成,即使只有一行符合条件,读取整个表也比使用索引更快。提交的特性中引入了两个新函数,一个是pg_set_relation_stats
:
postgres=# \x
Expanded display is on.
postgres=# \df pg_set_relation_stats
List of functions
-[ RECORD 1 ]----------------+------------------------------------------------------------------------------------------------------------------------------------------
Schema | pg_catalog
Name | pg_set_relation_stats
Result data type | boolean
Argument data types | relation regclass, relpages integer DEFAULT NULL::integer, reltuples real DEFAULT NULL::real, relallvisible integer DEFAULT NULL::integer
Type | func
postgres=# \x
Expanded display is off.
可以很容易看出,这个函数将关系名、页数、行数和全可见标志作为输入参数。这使我们能够调整统计信息,让优化器选择索引扫描而非顺序扫描:
select * from pg_set_relation_stats('t'::regclass, 1, 1000000 );
pg_set_relation_stats
-----------------------
t
(1 row)
select relpages,reltuples from pg_class where relname = 't';
relpages | reltuples
----------+-----------
1 | 1e+06
(1 row)
explain select * from t where b = 'aa';
QUERY PLAN
-----------------------------------------------------------------
Index Scan using i on t (cost=0.17..183.18 rows=10000 width=7)
Index Cond: (b = 'aa'::text)
(2 rows)
现在,我们可以通过修改这些统计信息来调试优化器的决策。需要注意的是,使用这个函数设置的统计信息有点类似于临时调整。任何手动或自动的分析操作都会覆盖它们:
analyze t;
select relpages,reltuples from pg_class where relname = 't';
relpages | reltuples
----------+-----------
1 | 100
(1 row)
explain select * from t where b = 'aa';
QUERY PLAN
-------------------------------------------------
Seq Scan on t (cost=0.00..2.25 rows=1 width=7)
Filter: (b = 'aa'::text)
(2 rows)
提供的第二个函数可以将统计信息重置为表创建时的状态:
select * from pg_clear_relation_stats('t'::regclass);
pg_clear_relation_stats
-------------------------
t
(1 row)
select relpages,reltuples from pg_class where relname = 't';
relpages | reltuples
----------+-----------
0 | -1
(1 row)
非常不错的改进。感谢社区的所有相关人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/e839c8ecc9352b7754e74f19ace013c0c0d18613