PostgreSQL 18 预览: 调整关系统计信息

John Doe 四月 14, 2025

你有没有注意到 PostgreSQL 进行某些变更操作后,会出现统计信息失效的情况?现在,PostgreSQL 提供了可以调整关系的统计信息的函数。

欢快奔跑的大象

特性提交日志

创建函数pg_set_relation_statspg_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