1. 从一次排查说起为什么系统表和系统视图值得单独拎出来讲刚接触 GaussDB 那会儿我跟很多人一样觉得系统表、系统视图这种东西属于用到再查的范畴平时写业务 SQL 根本碰不到。直到有一次线上一个批量任务突然变慢日志里没有任何报错业务代码也没改过排查了半天没头绪。后来一位做数据库内核的同事让我去查pg_stat_activity和pg_locks几分钟就定位到是一个长事务持有了锁把后面的 DDL 全堵住了。那次之后我才真正意识到系统表和系统视图不是锦上添花的知识点而是数据库运维和性能排查的听诊器。GaussDB 作为一款企业级关系型数据库它的系统表、系统视图体系相当庞大既有从 PostgreSQL 继承过来的pg_catalog那一套也有自己扩展出来的gs_*、pg_stat_*、pgxc_*等视图。很多人第一次打开pg_catalog下面的表列表看到几百个对象直接懵了——到底哪些是常用的哪些是排查问题必看的哪些字段是真正有意义的这篇内容我就想把这些年实际用下来最有价值的部分梳理清楚从体系结构讲到具体查询再到常见坑尽量做到你照着就能用。这篇适合三类人看一是刚上手 GaussDB、想搞清楚系统表体系的新手二是做运维或 DBA、需要经常排查性能问题的从业者三是写业务代码但想更深入理解数据库行为的开发者。不管你是哪一类读完至少能做到知道去哪张表查什么信息能自己写出排查 SQL遇到常见问题不慌。2. 系统表与系统视图的整体设计思路拆解2.1 系统表、系统视图、系统函数到底怎么分工在 GaussDB 里这三者的边界其实挺清晰的但新手容易混。我用一个生活化的类比来说明系统表像是仓库里的原始账本记录的是最底层、最原始的数据系统视图像是基于账本整理出来的报表把多张表的数据关联、聚合、过滤之后呈现给你系统函数则是你手里的计算器输入参数给你算出结果。具体来说系统表以pg_开头为主比如pg_class、pg_attribute、pg_proc、pg_type。它们存储的是数据库对象的元数据是数据的数据。这些表通常不建议直接改只读为主。系统视图以pg_stat_、pg_开头比如pg_stat_activity、pg_stat_user_tables、pg_roles、pg_settings。它们大多是对系统表和其他视图的封装提供更友好的查询接口。系统函数比如pg_relation_size()、pg_stat_get_activity()、pg_get_viewdef()。它们用来计算或获取特定信息常和系统视图配合使用。为什么要这么设计核心原因是解耦。底层存储结构可能随版本变化但视图和函数提供的接口相对稳定。你直接查pg_class的物理字段跨版本可能出问题但你查pg_tables视图基本能保证兼容。这也是为什么官方文档一直建议优先用视图而不是直接查系统表。2.2 为什么 GaussDB 的系统表体系比原生 PostgreSQL 更复杂GaussDB 在 PostgreSQL 基础上做了大量分布式扩展所以你会看到很多pgxc_开头的视图比如pgxc_node、pgxc_thread_wait_status以及gs_开头的对象比如gs_wlm_session_info、gs_wlm_user_resource_info。这些是分布式架构带来的额外信息层。打个比方单机 PostgreSQL 像一家只有一个收银台的店你查账只需要看一个账本GaussDB 分布式部署像连锁店每个分店有自己的账本总部还有一本汇总账。pgxc_系列视图就是那个汇总账它通过协调节点CN去收集各个数据节点DN的信息再聚合返回。理解这一点很关键因为它直接影响你的查询行为查pgxc_*视图往往比查本地pg_*视图慢因为它涉及跨节点通信。如果你只关心当前节点就别用pgxc_如果你要全局视角那必须用pgxc_。2.3 权限与可见性为什么你查不到某些数据很多人第一次查pg_stat_activity会发现只能看到自己的会话看不到别人的。这不是 bug是设计。普通用户默认只能看到自己的活动会话只有拥有MONADMIN或SYSADMIN权限的角色才能看到全部。这是出于安全考虑避免普通用户窥探其他会话的 SQL 内容。同理pg_stat_user_tables只显示当前用户有权限的表gs_wlm_session_info这类负载管理视图通常需要特定权限。所以当你发现视图里没数据时第一反应应该是查权限而不是怀疑视图坏了。提示排查权限问题时可以先执行SELECT current_user, session_user;确认当前身份再用\du或查pg_roles看角色属性。3. 核心系统表与视图的实操解析3.1 pg_class、pg_attribute、pg_type对象元数据的三驾马车这三张表是理解数据库对象结构的基石几乎所有这个表有哪些列这个列是什么类型的问题都要靠它们。pg_class存储所有关系对象表、索引、视图、序列等。关键字段字段含义实用场景relname对象名称按名字找对象relnamespace所属 schema 的 OID关联 pg_namespace 过滤 schemarelkind对象类型r普通表, i索引, v视图, S序列reltuples估算行数快速看表大小不精确relpages占用页数估算物理大小pg_attribute存储每个对象的列信息。关键字段attrelid所属对象 OID、attname列名、atttypid类型 OID、attnum列序号、attnotnull是否非空。pg_type存储数据类型定义typname是类型名typlen是长度。一个典型查询——查某张表的所有列及类型SELECT a.attname AS column_name, t.typname AS data_type, a.attnotnull AS not_null, a.attnum AS column_order FROM pg_attribute a JOIN pg_type t ON a.atttypid t.oid JOIN pg_class c ON a.attrelid c.oid JOIN pg_namespace n ON c.relnamespace n.oid WHERE c.relname your_table_name AND n.nspname public AND a.attnum 0 AND NOT a.attisdropped ORDER BY a.attnum;这里有几个细节值得说attnum 0是为了排除系统列比如 ctid、xmin 这些负数列NOT a.attisdropped是为了排除被删除但物理上还占位的列。这两个条件不加结果会多出莫名其妙的行这是新手最容易踩的坑。3.2 pg_stat_activity会话与慢查询排查的第一现场这张视图的重要性怎么强调都不过分。它记录了当前所有会话的状态包括正在执行的 SQL、等待事件、连接来源等。关键字段pid进程 IDdatname数据库名usename用户名state会话状态active、idle、idle in transaction 等query当前或最近执行的 SQLquery_start查询开始时间wait_event_type/wait_event等待事件类型和具体事件backend_start会话建立时间排查慢查询的经典 SQLSELECT pid, usename, datname, state, now() - query_start AS duration, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state active AND now() - query_start interval 10 seconds ORDER BY duration DESC;这条 SQL 能帮你快速找出执行超过 10 秒的活动查询。wait_event_type特别有价值如果是Lock说明在等锁如果是IO说明在等磁盘如果是Client说明在等客户端响应。不同等待类型指向完全不同的优化方向。注意query字段可能被截断受track_activities和track_activity_query_size参数影响。如果 SQL 很长建议调大track_activity_query_size否则你看到的可能是半截 SQL。3.3 pg_locks 与锁等待分析定位阻塞源头锁问题是生产环境最常见的性能杀手之一。pg_locks记录了当前所有锁的信息但它本身不太直观需要和其他视图关联。关键字段locktype锁类型、relation关系 OID、mode锁模式、granted是否已获得、pid持有或等待的进程。一个实用的锁等待查询SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_query FROM pg_stat_activity blocked JOIN pg_locks bl ON blocked.pid bl.pid AND NOT bl.granted JOIN pg_locks kl ON bl.locktype kl.locktype AND bl.database IS NOT DISTINCT FROM kl.database AND bl.relation IS NOT DISTINCT FROM kl.relation AND bl.page IS NOT DISTINCT FROM kl.page AND bl.tuple IS NOT DISTINCT FROM kl.tuple AND bl.virtualxid IS NOT DISTINCT FROM kl.virtualxid AND bl.transactionid IS NOT DISTINCT FROM kl.transactionid AND bl.classid IS NOT DISTINCT FROM kl.classid AND bl.objid IS NOT DISTINCT FROM kl.objid AND bl.objsubid IS NOT DISTINCT FROM kl.objsubid AND kl.granted JOIN pg_stat_activity blocking ON kl.pid blocking.pid WHERE blocked.pid blocking.pid;这段 SQL 看起来复杂核心逻辑是找到那些没拿到锁的进程再找到持有对应锁的进程配对展示。IS NOT DISTINCT FROM是为了正确处理 NULL 值比较这是 PostgreSQL 系数据库里处理可能为 NULL 的字段的标准写法。3.4 pg_stat_user_tables 与 pg_statio_user_tables表和 IO 的体检报告这两张视图是做性能优化的体检表。pg_stat_user_tables提供表的访问统计pg_statio_user_tables提供 IO 统计。pg_stat_user_tables关键字段字段含义优化启示seq_scan顺序扫描次数高说明可能缺索引idx_scan索引扫描次数低说明索引没被用上n_tup_ins/upd/del增删改行数判断表活跃度n_live_tup活行数估算快速看表规模n_dead_tup死行数高说明需要 vacuumlast_vacuum/last_autovacuum上次 vacuum 时间判断清理是否及时一个找全表扫描过多的查询SELECT schemaname, relname, seq_scan, idx_scan, n_live_tup, CASE WHEN seq_scan 0 THEN round(100.0 * idx_scan / (seq_scan idx_scan), 2) ELSE 0 END AS idx_usage_pct FROM pg_stat_user_tables WHERE seq_scan 100 ORDER BY seq_scan DESC LIMIT 20;idx_usage_pct是我自己加的一个指标表示索引使用占比。如果一张大表这个值很低说明大部分查询都在走全表扫描值得检查索引设计。3.5 gs_wlm_session_infoGaussDB 特有的负载管理视图这是 GaussDB 相对 PostgreSQL 的一个显著增强。gs_wlm_session_info记录了历史会话的执行信息包括执行时间、CPU 时间、内存使用、下盘情况等。对于事后分析昨天那个慢查询到底慢在哪特别有用。关键字段query、start_time、finish_time、duration、cpu_time、peak_memory、spill_size下盘大小、status。SELECT start_time, finish_time, duration, cpu_time, peak_memory, spill_size, substr(query, 1, 100) AS query_snippet FROM gs_wlm_session_info WHERE duration 5000 ORDER BY duration DESC LIMIT 20;spill_size这个字段特别值得关注。如果一条查询的下盘量很大说明内存不够用数据被临时写到磁盘了性能会急剧下降。这时候要么调大work_mem要么优化 SQL 减少中间结果集。提示gs_wlm_session_info的数据保留时间受enable_resource_track和相关参数控制默认可能只保留一段时间。如果需要长期分析建议定期导出。4. 完整实操从零搭建一套系统视图监控查询4.1 场景设定与需求拆解假设你负责一套 GaussDB 集群的日常运维需要每天巡检以下内容当前活动会话、慢查询、锁等待、表膨胀情况、索引使用情况。手动一条条查太累我们把它做成一套可复用的查询脚本。需求拆解会话总览按状态分组统计会话数慢查询 Top 10当前执行超过阈值的查询锁等待当前阻塞关系表膨胀死行占比高的表索引使用全表扫描多的表4.2 逐条实现与参数说明会话总览SELECT state, count(*) AS session_count, max(now() - state_change) AS max_idle_duration FROM pg_stat_activity GROUP BY state ORDER BY session_count DESC;state_change是状态最后一次变化的时间用它算空闲时长比query_start更准确因为一个会话可能执行完查询后进入 idle 状态很久。慢查询 Top 10SELECT pid, usename, datname, now() - query_start AS duration, wait_event_type, wait_event, substr(query, 1, 200) AS query_snippet FROM pg_stat_activity WHERE state active AND pid pg_backend_pid() ORDER BY duration DESC LIMIT 10;pid pg_backend_pid()是为了排除当前查询自己否则你每次都会看到自己这条 SQL 排在最前面。锁等待用 3.3 节那条配对查询即可这里不再重复。表膨胀SELECT schemaname, relname, n_live_tup, n_dead_tup, CASE WHEN n_live_tup n_dead_tup 0 THEN round(100.0 * n_dead_tup / (n_live_tup n_dead_tup), 2) ELSE 0 END AS dead_pct, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup 1000 ORDER BY dead_pct DESC LIMIT 20;死行占比超过 20% 基本就该考虑手动 vacuum 了。last_autovacuum为空说明从来没被自动清理过需要检查 autovacuum 是否开启。索引使用用 3.4 节那条查询。4.3 把查询串成巡检脚本实际运维中我会把这些查询写成一个.sql文件用psql -f批量执行或者用定时任务每天跑一次输出到日志。这里给一个简单的 shell 封装思路#!/bin/bash # 假设已配置好连接信息 psql -h $HOST -p $PORT -U $USER -d $DB -f inspection.sql /var/log/gaussdb_inspect_$(date %Y%m%d).log 21inspection.sql里把上面几条查询按顺序放好每条前面加个\echo标记方便日志里区分。注意巡检脚本里的查询本身也会消耗资源建议在业务低峰期执行并且给慢查询查询加上合理的阈值避免把正常查询也捞出来。5. 常见问题与排查技巧实录5.1 视图查不到数据或数据不全这是最高频的问题。排查顺序建议如下查权限SELECT current_user;确认身份再查pg_roles看是否有MONADMIN、SYSADMIN等属性。查参数某些统计视图受参数控制比如track_counts、track_activities、track_io_timing。如果这些是 off对应视图就是空的。查节点分布式环境下pg_stat_*只反映当前节点要全局数据得用pgxc_stat_*系列。现象可能原因解决方向pg_stat_activity 只有自己权限不足申请 MONADMINpg_stat_user_tables 为空track_countsoff开启参数pgxc_* 视图报错非分布式部署改用本地视图统计数字不更新统计收集器未运行检查 stats collector5.2 统计信息滞后怎么办系统视图里的统计数字不是实时的它们由统计收集器周期性刷新。如果你发现n_live_tup和实际count(*)差很多别慌这是正常的。n_live_tup是估算值不是精确值。要精确值只能SELECT count(*)但大表上这个操作很慢。折中方案是用pg_class.reltuples做快速估算或者定期跑ANALYZE让统计更准。提示ANALYZE只更新统计信息不清理死行VACUUM才清理死行。两者经常一起用但作用不同别搞混。5.3 查询系统表本身很慢这通常发生在分布式环境查pgxc_*视图时。因为这些视图要跨节点收集数据节点越多越慢。优化思路尽量缩小查询范围加WHERE条件过滤避免在高峰期查全局视图如果只需要当前节点数据用本地pg_*视图5.4 几个容易踩的坑坑一直接改系统表。系统表是只读的强行改可能导致数据库元数据损坏。要改对象属性用ALTER语句要改统计信息用ANALYZE。坑二用pg_stat_activity的query字段做精确匹配。这个字段可能被截断也可能因为参数设置而只保留部分内容。要做 SQL 分析建议开启track_activity_query_size调大。坑三忽略attisdropped。查pg_attribute时不加这个条件会看到已经删除的列导致结果混乱。坑四把reltuples当精确行数。它是估算值可能和实际差很多尤其是刚做完大批量操作还没 ANALYZE 的时候。6. 我个人在实际操作中的几点体会系统表和系统视图这块我的经验是用进废退。你不需要背下所有表和字段但要把最常用的那十几个视图的用途和关键字段记牢剩下的用到再查文档。我自己的习惯是维护一个个人笔记把每次排查问题时用到的 SQL 记下来时间长了就形成了一套自己的工具箱。另外GaussDB 的版本迭代比较快系统视图的字段和含义可能随版本变化。升级数据库后建议重新过一遍常用的巡检 SQL确认没有字段被废弃或改名。我遇到过升级后某个统计字段语义变了导致监控告警误报的情况排查起来挺费劲。最后分享一个小技巧如果你不确定某个视图有哪些字段除了查文档还可以直接\d view_name在 gsql 客户端里或者查pg_attribute关联pg_class找relkindv的对象。这比翻文档快而且能看到当前版本的真实结构。