由 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