PostgreSQL统计采集器是否会统计所有索引的使用情况?
核心结论
PostgreSQL 的统计收集器会捕获所有索引扫描记录,不管索引是在顶层SQL中调用、SQL/PLPGSQL函数内部调用,还是表达式索引(也就是你提到的含函数调用的索引)被匹配使用,都会正常累加idx_scan计数,没有遗漏。
你观测到大量索引idx_scan=0的常见原因
- 索引识别查询存在逻辑漏洞
你当前的查询仅通过indexrelname关联pg_class和pg_stat_all_indexes,如果不同schema、不同表存在同名索引,关联结果会错乱,非常容易把实际有扫描的索引错判为0扫描。
正确的关联逻辑应该通过唯一的索引OID关联,修正后的查询如下:
SELECT s.schemaname, s.relname AS table_name, c.relname AS index_name, pg_size_pretty(pg_total_relation_size(c.oid)) AS size, -- 直接转成可读大小更方便 s.idx_scan, s.idx_tup_read, s.idx_tup_fetch FROM pg_class c JOIN pg_stat_all_indexes s ON s.indexrelid = c.oid WHERE c.relkind = 'i' ORDER BY pg_total_relation_size(c.oid) DESC;
- 统计数据周期不足
pg_stat_all_indexes的计数会在以下场景清零:
- PostgreSQL实例重启
- 手动执行了
pg_stat_reset()或pg_stat_reset_shared('bgwriter')等统计重置命令
如果统计数据的累计周期没有覆盖完整的业务周期(比如没包含月末结算、定时任务等低频业务场景),就会出现低频使用的索引被误判为未使用的情况。
你可以通过SELECT pg_postmaster_start_time();查询实例启动时间,确认统计周期是否足够。
表达式索引从未被优化器匹配
你提到的含函数调用的索引属于表达式索引,只有当查询中的过滤/排序条件和索引定义的表达式完全一致,且函数不可变性符合要求时,优化器才会选择走该索引。如果业务SQL从来没有写出能匹配该索引的条件,那索引确实从未被使用,idx_scan自然为0。索引仅用于约束校验,从未被查询扫描
idx_scan仅统计查询执行时主动扫描索引的次数,索引的维护操作(插入/更新/删除时修改索引内容、唯一约束校验等)不会被计入该字段。如果一个索引是唯一约束、主键约束对应的索引,且从来没有查询使用过它做过滤/排序,也会出现idx_scan=0的情况。
安全清理未使用索引的建议
如果是PostgreSQL 12及以上版本,对拿不准的索引可以先设置为不可见:
ALTER INDEX 索引名 SET (invisible = on);
不可见索引会被优化器忽略,但依然会在数据写入时正常维护,观察1~2个完整业务周期,确认没有性能下降、业务报错后再执行删除操作,出现问题可以随时执行ALTER INDEX 索引名 SET (invisible = off);恢复。
内容的提问来源于stack exchange,提问作者M. Andersen
相关产品推荐
相关产品推荐

