一、pg_stat_statements的“小脾气”:统计信息为啥总丢

你是不是遇到过这种情况:平时用PostgreSQL的pg_stat_statements查慢查询定位性能瓶颈,某天数据库重启后,所有关于语句执行时间、调用次数、IO消耗的统计全没了,连前一天的性能基线都找不到?或者不小心执行了重置函数,把有用的业务语句统计全清了?这就是pg_stat_statements的“小脾气”——统计信息频繁丢失,根源是它默认的存储和重置逻辑太“随性”,我们得对症下药,用持久化和正确的手动重置把脾气压下去。

二、先搞懂:统计信息的“存放仓库”在哪里

要解决丢失问题,得先明白这些统计数据待在哪儿,就像你找不到钥匙要先想刚才放哪儿了。

2.1 临时仓库(内存):默认的“易失性”存储

PostgreSQL启动时,会分配一块共享内存给pg_stat_statements扩展,所有查询的统计数据(比如某条SELECT语句总耗时100秒,调用了50次)都暂时存在这里,就像你把临时记的待办贴在冰箱门上,方便随时看,但冰箱断电(数据库重启)、或者你随手撕了(调用重置函数),这些便签就全没了——这就是默认配置下统计丢失的核心原因。

2.2 永久仓库(磁盘):解决丢失的关键

要让统计数据“不被撕、不消失”,就得把便签抄到日记本(磁盘)上。PostgreSQL专门给pg_stat_statements配了持久化逻辑:当数据库执行checkpoint(定时刷脏页的操作)或者正常关机时,会把内存里的统计数据写到磁盘的pg_stat_statements.stat文件;下次启动时,会自动从这个文件加载统计,就像下次打开日记本还能看到之前记的内容。

三、持久化配置:让统计信息“落地生根”

想避免重启后统计丢失,必须开启持久化配置,这是最根本的解决方式,而且操作很简单,改几个参数就行。

3.1 核心配置参数梳理

所有配置都写在PostgreSQL的核心配置文件postgresql.conf里,需要改的参数都围绕“开启持久化、控制存储量”,具体如下:

# postgresql.conf 配置示例:pg_stat_statements持久化核心配置
shared_preload_libraries = 'pg_stat_statements'  # 必须项:把扩展加载到共享内存,不然后面的参数全白设
pg_stat_statements.max = 15000                   # 可选项:最多存15000条不同查询的统计,比实际不同查询数多20%就行,避免后续查询覆盖老数据
pg_stat_statements.track = all                   # 可选项:追踪所有语句,包括管理类的CREATE、ALTER,按需调整
pg_stat_statements.save = on                      # 必须项:开启这个才会把统计写到磁盘,重启自动加载,是解决丢失的关键
pg_stat_statements.track_planning = on            # 可选项:额外追踪执行计划的统计,适合调优复杂SQL的场景

3.2 配置生效的两种方式

  • 如果是本地自建的PostgreSQL,改完配置后,用pg_ctl restart命令重启数据库,让配置生效;
  • 如果是云数据库(比如阿里云RDS PostgreSQL),不能直接改本地文件,得去云控制台的“参数组管理”里,把上面的参数改完后,重启实例即可,云厂商会帮你同步配置。

四、手动重置的正确姿势:别把日记本撕了

除了重启丢失,很多人是误操作重置函数导致统计丢失——比如直接执行全量重置,把所有统计清了,不管是生产库还是测试库,都会影响性能排查,所以得用正确的重置姿势。

4.1 常见的错误重置:全量清空(不推荐)

很多人第一次用pg_stat_statements,看到重置函数就直接执行,这个操作会删掉所有统计,相当于把日记本全撕了,除非是全新环境的初始化,否则绝对别用:

-- 错误示范:全量重置,丢失所有统计
SELECT pg_stat_statements_reset();

4.2 正确的细粒度重置:按需清理(推荐)

PostgreSQL 11及以上版本支持带参数的重置函数,可以指定重置的范围,比如只重置当前数据库、某个用户的统计,甚至匹配特定查询的统计,这样不会误删其他有用的数据,示例如下:

-- PostgreSQL 11+ 版本的细粒度重置示例
-- 1. 先获取当前数据库的唯一标识(OID),用于精准指定重置范围
SELECT oid, datname FROM pg_database WHERE datname = current_database();

-- 2. 只重置当前业务库的统计,保留其他测试库、日志库的统计
SELECT pg_stat_statements_reset((SELECT oid FROM pg_database WHERE datname = current_database()));

-- 进阶:如果要重置特定用户的语句,比如测试用户的统计,用用户OID(可通过SELECT oid FROM pg_user WHERE usename='test_user'获取)
SELECT pg_stat_statements_reset((SELECT oid FROM pg_user WHERE usename='test_user'));

4.3 什么时候该手动重置?

手动重置不是不能用,而是要选对场景:比如测试环境上线前,想清空之前的测试数据,看新功能的真实性能;或者每周定时重置一次,统计本周的性能变化趋势;还有清理无效的管理语句(比如临时建的测试表的统计),保留核心业务语句的统计——这些场景下用细粒度重置就不会出问题。

五、避坑指南:你必须知道的细节

5.1 持久化的优缺点分析

优点:彻底解决重启后统计丢失的问题,支持长期性能趋势分析,适合生产库的性能基线搭建;缺点:磁盘占用极小(比如15000条统计才占几KB,几乎可以忽略),checkpoint时的IO开销也可以忽略,完全不影响数据库性能。

5.2 手动重置的注意事项

  • 别在业务高峰期重置,哪怕是细粒度重置,也会占用少量CPU资源,尽量选凌晨低峰期操作;
  • 如果是PostgreSQL 10及以下老版本,没有参数化重置功能,只能用全量重置,这时候一定要确认后再操作,最好提前备份统计数据(可以用pg_dump导出相关的系统视图数据);
  • 一定要确认pg_stat_statements.save参数是on,如果关了,就算配置了其他参数,持久化也不会生效,很多人踩过这个坑。

5.3 常见的踩坑案例

  • 忘了把pg_stat_statements加到shared_preload_libraries里,结果扩展没加载,持久化配置白改,重启后还是丢失;
  • pg_stat_statements.max设得太小,比如只设100,导致新的查询会覆盖老的重要统计,比如慢查询的统计被新的测试语句覆盖,影响排查。

六、总结

解决pg_stat_statements统计信息频繁丢失的核心逻辑是两个:一是开启持久化配置,让统计数据落地磁盘,重启后自动恢复,这是生产库必须做的;二是用细粒度手动重置,别全量清空所有统计,只清理需要的部分,这是日常维护的关键。另外,还要注意参数的版本兼容性(老版本PostgreSQL没有参数化重置),以及配置的生效方式(本地库和云数据库的操作不一样),这样就能彻底解决统计丢失的问题,放心用pg_stat_statements做性能监控和排查。