一、概述
tencentdb_wal_stat 是云数据库 PostgreSQL 提供的 WAL 日志来源分析扩展,支持在线按 database、schema、table 三个维度统计 WAL 日志的产生量,帮助你快速定位产生大量 WAL 的对象,及时干预避免磁盘空间告急。
核心价值:
·精准定位:按 database → schema → table 逐层下钻,快速找到 WAL 产生大户。
·在线分析:通过 SQL 直接调用,无需离线解析 WAL 文件。
·多维聚合:同时展示 RMGR 类型(Heap/Btree/Transaction 等)、FPI 统计等详细信息。
·生产安全:只读分析,不影响业务运行。
典型场景:
·SaaS 多租户环境下,某租户突发大量写入导致 WAL 堆积。
·逻辑复制消费端积压,需要找出 WAL 产生量最大的表进行限流。
·日常运维监控 WAL 产生趋势,预防磁盘空间问题。
二、环境要求
| 项目 | 要求 |
| 云数据库 PostgreSQL 版本 | 15.x 及以上 |
| 扩展版本 | tencentdb_wal_stat v1.0 |
| 执行权限 | pg_tencentdb_superuser 角色成员 |
| 执行环境 | 仅支持在主库(Primary)上执行 |
四、安装扩展
CREATE EXTENSION IF NOT EXISTS tencentdb_wal_stat;
五、验证安装
输入:
SELECT extname, extversion FROM pg_extension WHERE extname = 'tencentdb_wal_stat';
输出:
| extname | extversion |
| tencentdb_wal_stat | 1.0 |
七、函数
tencentdb_wal_stat(wal_num integer)RETURNS SETOF record
参数:
| 参数 | 类型 | 范围 | 说明 |
| wal_num | integer | 1 ~ 500 | 要分析的 WAL 文件数量,数值越大分析范围越广 |
注意:说明:
注意:wal_num 越大,分析时间越长。建议在业务低峰期执行大范围分析。
八、输出字段
| 字段 | 类型 | 说明 |
| database_oid | oid | 数据库 OID(共享对象和系统级 WAL 为0) |
| database_name | text | 数据库名称 |
| schema_oid | oid | Schema OID |
| schema_name | text | Schema 名称(仅当前数据库和共享 catalog 可解析) |
| relation_oid | oid | 关系(表/索引)OID |
| relation_name | text | 关系名称(已删除的对象显示为 <filenode:N>) |
| relation_kind | text | 关系类型:table index toast sequence matview shared_table shared_index system unknown |
| rmgr_name | text | WAL 资源管理器名称(Heap、Btree、Transaction 等) |
| wal_records | bigint | WAL 记录条数 |
| wal_bytes | bigint | WAL 字节数(不含 FPI) |
| wal_fpi | bigint | Full Page Image(FPI)记录数 |
| wal_fpi_bytes | bigint | FPI 字节数 |
注意:说明:
注意:WAL 总大小 = wal_bytes + wal_fpi_bytes。FPI 是 checkpoint 后首次修改页面时写入的完整页面副本,通常占 WAL 总量的20%~40%。
十、场景一:磁盘告警,快速定位哪个数据库产生了最多 WAL
输入:
— 按数据库汇总 WAL 产生量(分析最近50个 WAL 文件)SELECT database_name, SUM(wal_records) AS total_records, pg_size_pretty(SUM(wal_bytes + wal_fpi_bytes)::bigint) AS total_size, ROUND(SUM(wal_bytes + wal_fpi_bytes) * 100.0 / SUM(SUM(wal_bytes + wal_fpi_bytes)) OVER (), 1) AS pctFROM tencentdb_wal_stat(50)GROUP BY database_nameORDER BY SUM(wal_bytes + wal_fpi_bytes) DESC;
输出:
| database_name | total_records | total_size | pct |
| func_veri | 447033 | 49 MB | 79.6 |
| postgres | 53164 | 7812 KB | 12.3 |
| business_analysis | 8 | 49 KB | 0.1 |
| monitor | 7 | 44 KB | 0.1 |
| instance | 7 | 44 KB | 0.1 |
| pg_instance | 14 | 34 KB | 0.1 |
十一、场景二:定位到库后,进一步查看哪个 Schema 产生最多 WAL
输入:
— 按 schema 维度汇总SELECT database_name, schema_name, SUM(wal_records) AS total_records, pg_size_pretty(SUM(wal_bytes + wal_fpi_bytes)::bigint) AS total_sizeFROM tencentdb_wal_stat(50)WHERE database_name = 'func_veri' — 替换为目标数据库名 AND schema_name IS NOT NULL AND schema_name != ''GROUP BY database_name, schema_nameORDER BY SUM(wal_bytes + wal_fpi_bytes) DESC;
输出:
| database_name | schema_name | total_records | total_size |
| func_veri | public | 185846 | 19 MB |
| func_veri | pg_catalog | 9110 | 2230 KB |
| func_veri | test_schema | 20521 | 1443 KB |
| func_veri | pg_toast | 190 | 179 KB |
十二、场景三:精确到表级别,找出 WAL 产生量 TOP10的表
输入:
— 查看产生 WAL 最多的表 TOP10SELECT database_name, schema_name, relation_name, relation_kind, wal_records, pg_size_pretty(wal_bytes::bigint) AS wal_size, pg_size_pretty(wal_fpi_bytes::bigint) AS fpi_size, pg_size_pretty((wal_bytes + wal_fpi_bytes)::bigint) AS total_sizeFROM tencentdb_wal_stat(50)WHERE relation_kind IN ('table', 'index')ORDER BY (wal_bytes + wal_fpi_bytes) DESCLIMIT 10;
输出:
| database_name | schema_name | relation_name | relation_kind | wal_records | wal_size | fpi_size | total_size |
| func_veri | public | idx_test_orders | table | 50000 | 4932 KB | 0 bytes | 4932 KB |
| func_veri | public | idx_test_products | table | 30000 | 3757 KB | 0 bytes | 3757 KB |
| func_veri | public | idx_test_orders_pkey | index | 50411 | 3226 KB | 0 bytes | 3226 KB |
| func_veri | public | idx_test_orders_customer_id_status_id_product_name_amount_o_idx | index | 444 | 21 KB | 3166 kB | 3187 KB |
| func_veri | public | idx_test_products_pkey | index | 30246 | 1935 KB | 0 bytes | 1935 KB |
| postgres | pg_catalog | pg_attribute | table | 2475 | 1023 KB | 142 kB | 1165 KB |
| func_veri | public | idx_test_customers | table | 10000 | 1034 KB | 0 bytes | 1034 KB |
| func_veri | test_schema | orders | table | 10000 | 732 KB | 0 bytes | 732 KB |
| func_veri | test_schema | orders_pkey | index | 10084 | 645 KB | 0 bytes | 645 KB |
| func_veri | public | idx_test_customers_pkey | index | 10084 | 645 KB | 0 bytes | 645 KB |
十三、场景四:分析 WAL 类型分布(Heap 写入 vs Index 写入)
输入:
— 按 RMGR 类型查看 WAL 分布SELECT rmgr_name, SUM(wal_records) AS total_records, pg_size_pretty(SUM(wal_bytes)::bigint) AS wal_size, ROUND(SUM(wal_records) * 100.0 / SUM(SUM(wal_records)) OVER (), 1) AS pctFROM tencentdb_wal_stat(50)GROUP BY rmgr_nameORDER BY SUM(wal_records) DESC;
输出:
| rmgr_name | total_records | wal_size | pct |
| Btree | 246781 | 16 MB | 44.9 |
| Heap | 229351 | 25 MB | 41.7 |
| Heap2 | 46408 | 3726 KB | 8.4 |
| Generic | 11119 | 4349 KB | 2.0 |
| Transaction | 7120 | 2015 KB | 1.3 |
| Sequence | 6288 | 608 KB | 1.1 |
| Standby | 1737 | 73 KB | 0.3 |
| XLOG | 1163 | 49 KB | 0.2 |
| Storage | 116 | 4912 bytes | 0.0 |
| CLOG | 1 | 30 bytes | 0.0 |
注意:说明:
注意:Btree 和 Heap 类型占比高说明表写入(INSERT/UPDATE/DELETE)及其关联的索引维护是 WAL 的主要来源。如果 Btree 占比异常高,可考虑减少不必要的索引。
十四、场景五:查看 FPI(Full Page Image)占比
输入:
— FPI 占比分析SELECT pg_size_pretty(SUM(wal_bytes)::bigint) AS wal_data_size, pg_size_pretty(SUM(wal_fpi_bytes)::bigint) AS fpi_size, pg_size_pretty(SUM(wal_bytes + wal_fpi_bytes)::bigint) AS total_size, ROUND(SUM(wal_fpi_bytes) * 100.0 / NULLIF(SUM(wal_bytes + wal_fpi_bytes), 0), 1) AS fpi_pctFROM tencentdb_wal_stat(50);
输出:
| wal_data_size | fpi_size | total_size | fpi_pct |
| 51MB | 11MB | 62MB | 17.5 |
注意:说明:
注意:如果 FPI 占比超过40%,可以考虑调大 max_wal_size 来减少 checkpoint 频率,从而降低 FPI 产生量。
十六、WAL 堆积应急处理流程
当收到磁盘空间告警或发现 WAL 堆积时,按照以下步骤排查:
步骤1: 查看整体 WAL 产生概况 └── tencentdb_wal_stat(50) 按 database 汇总 │步骤2: 定位目标数据库 └── 按 schema 汇总,找到异常 schema │步骤3: 精确到表 └── 按 relation 查看 TOP10表 │步骤4: 分析原因 ├── Heap 类型多 → 大量 INSERT/UPDATE/DELETE ├── Btree 类型多 → 索引维护开销大 └── FPI 占比高 → checkpoint 过于频繁 │步骤5: 采取措施 ├── 限制异常租户写入速率 ├── 暂停非核心批量任务 └── 优化索引策略
十七、日常监控建议
建议定期执行以下查询,记录 WAL 产生趋势:
— 每小时记录各数据库 WAL 产生量(可写入监控表)INSERT INTO wal_monitor_log (check_time, database_name, wal_records, wal_bytes)SELECT now(), database_name, SUM(wal_records), SUM(wal_bytes + wal_fpi_bytes)FROM tencentdb_wal_stat(10)GROUP BY database_name;
十八、wal_num 参数选择建议
| 场景 | 推荐 wal_num | 说明 |
| 紧急排查 | 5 ~ 10 | 快速返回,查看最近的 WAL 来源 |
| 日常巡检 | 20 ~ 50 | 覆盖较长时间范围,数据更具代表性 |
| 深度分析 | 100 ~ 200 | 需要全面了解 WAL 分布时使用 |
注意:说明:
注意:wal_num 越大,分析时间越长。建议在业务低峰期执行大范围分析。
十九、名词解释
| 名词 | 解释 |
| WAL | Write-Ahead Logging,预写式日志。PostgreSQL 通过先写日志再写数据文件的方式来保证事务的持久性和崩溃恢复能力 |
| LSN | Log Sequence Number,日志序列号。唯一标识 WAL 日志中的位置 |
| RMGR | Resource Manager,资源管理器。WAL 按不同的操作类型分类,如 Heap(表数据)、Btree(B-tree 索引)、Transaction(事务) |
| FPI | Full Page Image,全页镜像。checkpoint 后首次修改某个数据页时,会将该页完整内容写入 WAL,用于崩溃恢复时避免部分写入问题 |
| Replication Slot | 复制槽。用于保留 WAL 日志直到下游订阅者消费完成。如果下游消费慢,会导致 WAL 无法被清理 |
20、注意事项
·仅主库可用:在备库上执行会报错,这是因为备库没有独立的 WAL 写入。
·权限要求:需要 pg_tencentdb_superuser 角色成员权限。
·参数范围:wal_num 取值1 ~ 500,超出范围会报错。
·名称解析:其他数据库中的对象名称可能显示为 <oid:N> 或 <filenode:N>,这是正常现象(跨库无法解析 catalog)。
·性能影响:该函数读取 WAL 文件进行统计,wal_num 较大时执行时间会增加,建议避免在高峰期使用大 wal_num。
·WAL 可用性:仅能分析尚未被清理的 WAL 文件,已回收的历史 WAL 无法分析。