PostgreSQL 16: 追踪表或索引的最后扫描时间

John Doe 七月 23, 2025

你想知道数据库中的哪些表和索引很少被访问吗?或者,有哪些表和索引访问较为频繁吗?

非洲乞力马扎罗山角下的大象

特性提交日志

pgstat:追踪关系的最后扫描时间。

了解一个关系(表或索引等)最后一次被使用的时间可能很有用,例如在评估某个索引是否仍然需要时。此前,虽然可以通过追踪诸如 pg_stat_all_indexes.idx_scan 等指标随时间的变化来推断最后一次使用的时间,但实际上很少有人会这样做。

为了更方便地检测关系最后一次被扫描的时间,我们在每个关系的 pgstat 条目中记录该时间。为了将开销降至最低,采取了以下措施:

a) 仅当后端待处理的统计条目刷新到共享统计信息时,才更新时间戳;

b) 使用最后一个访问事务的结束时间作为该时间戳。

讨论:https://postgr.es/m/CA+OCxozrVHNFVEPkweUHMZje+t1tfY816d9MZYc6eZwOOusOaQ@mail.gmail.com

示例

根据上面的特性提交信息,下面将介绍pg_stat_*_tables系统视图中的新增统计信息。

我们创建一个简单的小表和一个索引:

create table t1 ( a int, b text );

insert into t1 select i, i::text from generate_series(1,500000) i;

create index i1 on t1 (a);

在 PostgreSQL 之前的版本中,已有的部分统计信息如下:

select seq_tup_read,n_tup_ins,n_live_tup from pg_stat_all_tables where relname = 't1';
 seq_tup_read | n_tup_ins | n_live_tup 
--------------+-----------+------------
       500000 |    500000 |     500000
(1 row)

这表明已读取、插入了 500000 个元组,且有 500000 个活跃元组,因为我们刚刚插入了这些数据。PostgreSQL 新版本新增了一项统计信息,就是特定表最后一次全表扫描的时间:

select last_seq_scan from pg_stat_all_tables where relname = 't1';
         last_seq_scan         
-------------------------------
 2025-07-05 09:50:11.252371+08
(1 row)

该字段已有值,对应我们创建索引时扫描表的时间。另一项统计信息是最后一次索引扫描的时间:

select last_idx_scan from pg_stat_all_tables where relname = 't1';
 last_idx_scan 
---------------
  
(1 row)

目前该字段为空,因为我们尚未通过索引进行读取,但当我们执行索引扫描后,该字段会被填充:

select * from t1 where a = 5;
 a | b 
---+---
 5 | 5
(1 row)

select last_idx_scan from pg_stat_all_tables where relname = 't1';
         last_idx_scan         
-------------------------------
 2025-07-05 09:58:49.721493+08
(1 row)

跟踪访问时间这一功能已经很实用了,但 PostgreSQL 中还有另一项非常有趣的统计信息。当 PostgreSQL 更新元组且该元组无法放入原数据块时,元组的新版本会被添加到另一个数据块中。这种情况通常不希望频繁发生,因为会避免 HOT 更新(热更新)。新增的统计信息 “n_tup_newpage_upd” 会告诉你元组需要在另一个数据块而非当前数据块中创建的次数。由于我们在创建上述表时未指定填充因子,因此使用默认值 100。这意味着除最后一个数据块外,所有数据块中元组的更新都会将元组移至另一个数据块:

update t1 set b = 'xxx' where a = 5;

select n_tup_newpage_upd from pg_stat_all_tables where relname = 't1';
 n_tup_newpage_upd 
-------------------
                 1
(1 row)

非常不错的体验,感谢所有参与的社区人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/c037471832e1ec3327f81eebbd8892e5c1042fe0