|
| 1 | + |
| 2 | +:sectnums: |
| 3 | +:sectnumlevels: 5 |
| 4 | + |
| 5 | += pg_track_settings |
| 6 | + |
| 7 | +== 概述 |
| 8 | +pg_track_settings 是一个仅由约500行 PL/pgSQL 编写的 PG 扩展,可以实现对 PostgreSQL 配置变更的跟踪。 |
| 9 | + |
| 10 | +它提供了一个函数 (pg_track_settings_snapshot()),必须定期调用。每次调用时,它都会存储自上次调用以来更改的设置。如果 PostgreSQL 的启动时间与上次不同,它还会跟踪本次启动时间。pg_track_settings 通常需要和 Cron 或 PoWA 等工具配合使用,以便在生产环境中定期采样。 |
| 11 | + |
| 12 | +IvorySQL 的 PG 模式和 Oracle 兼容模式都已经适配 pg_track_settings。 |
| 13 | + |
| 14 | +项目地址:<https://github.com/rjuju/pg_track_settings> |
| 15 | + |
| 16 | +开源协议:PostgreSQL License |
| 17 | + |
| 18 | +== 函数一览 |
| 19 | + |
| 20 | +=== 全局参数 |
| 21 | + |
| 22 | +[options="header"] |
| 23 | +|=== |
| 24 | +| 函数 | 作用 |
| 25 | +| pg_track_settings_snapshot() | 采集当前配置,记录差异 |
| 26 | +| pg_track_settings(timestamptz) | 返回指定时刻的全量配置;省略参数则为当前时刻 |
| 27 | +| pg_track_settings_diff(timestamptz, timestamptz) | 返回两个时刻之间发生变化的所有参数 |
| 28 | +| pg_track_settings_log(text) | 返回某个指定参数的完整变更历史 |
| 29 | +|=== |
| 30 | + |
| 31 | +=== 库级/角色级覆盖参数 |
| 32 | + |
| 33 | +[options="header"] |
| 34 | +|=== |
| 35 | +| 函数 | 作用 |
| 36 | +| pg_track_db_role_settings(timestamptz) | 指定时刻的全部覆盖配置 |
| 37 | +| pg_track_db_role_settings_diff(timestamptz, timestamptz) | 两个时刻之间变化的覆盖配置 |
| 38 | +| pg_track_db_role_settings_log(text) | 某个覆盖参数的变更历史 |
| 39 | +|=== |
| 40 | + |
| 41 | +=== 维护 |
| 42 | + |
| 43 | +[options="header"] |
| 44 | +|=== |
| 45 | +| 函数 | 作用 |
| 46 | +| pg_track_settings_reset() | 清空全部历史记录 |
| 47 | +|=== |
| 48 | + |
| 49 | +== 安装启用 |
| 50 | + |
| 51 | +=== 源码编译 |
| 52 | + |
| 53 | +执行源码编译和安装: |
| 54 | +[literal, bash] |
| 55 | +---- |
| 56 | +# 构建并安装 pg_track_settings |
| 57 | +cd ivorysql |
| 58 | +git clone https://github.com/rjuju/pg_track_settings.git contrib/pg_track_settings |
| 59 | +make -C contrib/pg_track_settings install |
| 60 | +---- |
| 61 | + |
| 62 | +=== 安装扩展 |
| 63 | + |
| 64 | +PG 模式与 Oracle 模式会话下命令相同: |
| 65 | +[literal, sql] |
| 66 | +---- |
| 67 | +postgres=# CREATE EXTENSION pg_track_settings; |
| 68 | +postgres=# SELECT extname, extversion FROM pg_extension WHERE extname = 'pg_track_settings'; |
| 69 | + extname | extversion |
| 70 | +-------------------+------------ |
| 71 | + pg_track_settings | 2.1.2 |
| 72 | +---- |
| 73 | + |
| 74 | +== 使用流程 |
| 75 | + |
| 76 | +[TIP] |
| 77 | +==== |
| 78 | +以下输出为示意,用于说明各函数的返回形式。 |
| 79 | +==== |
| 80 | + |
| 81 | +先做一次快照,建立基线: |
| 82 | + |
| 83 | +[literal, sql] |
| 84 | +---- |
| 85 | +postgres=# SELECT pg_track_settings_snapshot(); |
| 86 | + pg_track_settings_snapshot |
| 87 | +---------------------------- |
| 88 | + t |
| 89 | +(1 row) |
| 90 | +---- |
| 91 | +
|
| 92 | +此时历史表里已经有了第一批记录: |
| 93 | +
|
| 94 | +[literal, sql] |
| 95 | +---- |
| 96 | +postgres=# SELECT DISTINCT ts FROM pg_track_settings_history; |
| 97 | + ts |
| 98 | +------------------------------- |
| 99 | + 2026-09-08 10:00:37.449846+08 |
| 100 | +(1 row) |
| 101 | +---- |
| 102 | + |
| 103 | +假设现在有人改了配置并重载: |
| 104 | + |
| 105 | +[literal, sql] |
| 106 | +---- |
| 107 | +postgres=# ALTER SYSTEM SET work_mem = '32MB'; |
| 108 | +postgres=# SELECT pg_reload_conf(); |
| 109 | +---- |
| 110 | + |
| 111 | +再采集一次,然后查看这段时间内的变更: |
| 112 | + |
| 113 | +[TIP] |
| 114 | +==== |
| 115 | +在Oracle兼容模式下需要使用 make_interval(mins => 10) 来代替 interval '10 minutes',否则会报错。 |
| 116 | +==== |
| 117 | + |
| 118 | +[literal, sql] |
| 119 | +---- |
| 120 | +postgres=# SELECT pg_track_settings_snapshot(); |
| 121 | +
|
| 122 | +postgres=# SELECT * FROM pg_track_settings_diff(now() - interval '10 minutes', now()); |
| 123 | + name | from_setting | from_exists | to_setting | to_exists |
| 124 | +----------+--------------+-------------+------------+----------- |
| 125 | + work_mem | 4096 | t | 32768 | t |
| 126 | +(1 row) |
| 127 | +---- |
| 128 | + |
| 129 | +from_exists / to_exists 这两列用来表达参数在两个时间点是否存在。参数被新增时 from_exists 为 false,被移除时 to_exists 为 false。 |
| 130 | + |
| 131 | +查看单个参数的完整历史: |
| 132 | + |
| 133 | +[literal, sql] |
| 134 | +---- |
| 135 | +postgres=# SELECT * FROM pg_track_settings_log('work_mem'); |
| 136 | + ts | name | setting_exists | setting |
| 137 | +-------------------------------+----------+----------------+--------- |
| 138 | + 2026-09-08 10:06:42.581682+08 | work_mem | t | 32768 |
| 139 | + 2026-09-08 10:00:37.449846+08 | work_mem | t | 4096 |
| 140 | +(2 rows) |
| 141 | +---- |
| 142 | + |
| 143 | +回溯任意时刻的完整配置: |
| 144 | + |
| 145 | +[literal, sql] |
| 146 | +---- |
| 147 | +postgres=# SELECT * FROM pg_track_settings('2026-09-08 10:03:00'); |
| 148 | + name | setting |
| 149 | +------------------------------+--------- |
| 150 | + [...] |
| 151 | + checkpoint_completion_target | 0.9 |
| 152 | + checkpoint_timeout | 300 |
| 153 | + work_mem | 4096 |
| 154 | + [...] |
| 155 | +---- |
| 156 | + |
| 157 | +查看覆盖参数的历史: |
| 158 | + |
| 159 | +[literal, sql] |
| 160 | +---- |
| 161 | +postgres=# SELECT * FROM pg_track_db_role_settings_log('statement_timeout'); |
| 162 | + ts | dbname | rolname | name | setting_exists | setting |
| 163 | +-------------------------------+----------+----------+-------------------+----------------+--------- |
| 164 | + 2026-09-08 11:15:03.112094+08 | appdb | | statement_timeout | t | 30s |
| 165 | +---- |
| 166 | + |
| 167 | +查看实例重启历史: |
| 168 | + |
| 169 | +[literal, sql] |
| 170 | +---- |
| 171 | +postgres=# SELECT * FROM pg_reboot; |
| 172 | + ts |
| 173 | +------------------------------- |
| 174 | + 2026-09-08 09:39:43.609195+08 |
| 175 | +(1 row) |
| 176 | +---- |
| 177 | +
|
| 178 | +== 注意事项 |
| 179 | +
|
| 180 | +**关于备份:** 所有历史数据都存在普通表里,因此会被 pg_dump / pg_dumpall 完整备份。这通常是好事,但如果积累了很长的历史又不想带进备份,需要使用 pg_track_settings_reset() 清理。 |
| 181 | +
|
| 182 | +* **关于时间戳:** 历史表的 ts 列是 timestamptz 类型,存的是绝对时间点,显示时按会话时区换算。跨时区团队查历史时,注意各自会话的 TimeZone 设置可能导致看到的字符串不同,但指向的是同一时刻。 |
| 183 | +
|
| 184 | +* **关于采集间隔:** 扩展记录的是「发现变更的时刻」,不是「变更实际发生的时刻」。如果两次快照之间某个参数改了又改回来,中间状态会被完全丢失。对配置审计要求严格的场景,应该配合 log_statement = 'ddl' 或专门的审计扩展一起用。 |
| 185 | +
|
| 186 | +* **关于权限:** control 文件中 superuser = false,即非超级用户也可以创建该扩展(前提是拥有目标 schema 的权限)。但采集函数需要能读取 pg_settings 和 pg_db_role_setting,实际执行时仍建议用有足够权限的角色。 |
| 187 | +
|
| 188 | +* **它不是什么:** 这个扩展只做记录,不做告警,也不会阻止任何配置修改。它是事后排查的工具,不是准入控制。 |
| 189 | +
|
| 190 | +== 适合场景 |
| 191 | +
|
| 192 | +* 多人共同运维、配置变更缺乏统一流程的团队 |
| 193 | +* 需要做性能回归分析,想确认「变慢是不是因为改了参数」的 DBA |
| 194 | +* 管理大量实例、需要集中掌握配置漂移情况的场景(配合 PoWA) |
| 195 | +* 有合规审计要求,需要证明某个时间点配置状态的环境 |
0 commit comments