PostgreSQL系统表探秘:手把手教你用pg_database和pg_class理清库、Schema和表的关系

张开发
2026/4/21 16:58:25 15 分钟阅读

分享文章

PostgreSQL系统表探秘:手把手教你用pg_database和pg_class理清库、Schema和表的关系
PostgreSQL系统表实战指南从pg_database到pg_class的元数据管理艺术刚接手一个遗留的PostgreSQL数据库时我面对上百张杂乱无章的表完全无从下手。直到一位资深DBA告诉我系统表就是你的地图。确实当你掌握了pg_database、pg_class这些系统表的查询技巧就像获得了数据库的X光透视能力——不仅能看清库、Schema、表之间的脉络关系还能在性能优化和故障排查时直击要害。1. PostgreSQL逻辑结构的三重境界想象你走进一座图书馆实例里面有不同的阅览室数据库每个阅览室有多个书架Schema书架上放着各种书籍表。这就是PostgreSQL的层次结构实例(Instance)最高层级包含所有数据库集群数据库(Database)相互隔离的数据容器相当于独立的工作空间Schema数据库内的命名空间用于组织数据库对象表(Table)实际存储数据的结构这种分层设计带来了几个关键特性完全隔离性不同数据库之间无法直接互访需要dblink或FDW独立权限体系每个层级都有独立的权限控制灵活的命名空间Schema允许同名对象共存-- 查看实例中所有数据库 SELECT datname, datcollate, encoding FROM pg_database WHERE datistemplate false;执行结果示例datnamedatcollateencodingpostgresen_US.UTF-86ordersen_US.UTF-86hrzh_CN.UTF-86注意pg_database存储的是实例级别的数据库信息不包含具体表数据2. 系统表探秘pg_class与pg_namespace的黄金组合pg_class是PostgreSQL最核心的系统表之一它记录了几乎所有类表对象普通表relkind r索引relkind i序列relkind S视图relkind v物化视图relkind m而pg_namespace则存储了Schema信息两者结合可以精准定位任何数据库对象。-- 查找特定Schema下的所有用户表 SELECT c.relname AS table_name, n.nspname AS schema_name, c.reltuples AS row_estimate FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid WHERE c.relkind r AND n.nspname NOT IN (pg_catalog, information_schema) AND n.nspname !~ ^pg_toast ORDER BY c.reltuples DESC;这个查询能帮你排除系统Schema获取每张表的行数估算按表大小排序快速定位大表3. 实战演练解决五个典型运维问题3.1 问题一如何找出所有没有主键的表-- 查找没有主键的用户表 SELECT n.nspname AS schema_name, c.relname AS table_name FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid LEFT JOIN pg_constraint co ON co.conrelid c.oid AND co.contype p WHERE c.relkind r AND n.nspname NOT IN (pg_catalog, information_schema) AND co.oid IS NULL;3.2 问题二如何统计各Schema的表数量和总大小-- Schema级统计报表 SELECT n.nspname AS schema_name, COUNT(*) AS table_count, pg_size_pretty(SUM(pg_total_relation_size(c.oid))) AS total_size FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid WHERE c.relkind r AND n.nspname NOT IN (pg_catalog, information_schema) GROUP BY n.nspname ORDER BY SUM(pg_total_relation_size(c.oid)) DESC;3.3 问题三如何找出所有外键关系-- 可视化数据库外键关系网 SELECT conname AS constraint_name, n1.nspname AS source_schema, c1.relname AS source_table, a1.attname AS source_column, n2.nspname AS target_schema, c2.relname AS target_table, a2.attname AS target_column FROM pg_constraint co JOIN pg_class c1 ON co.conrelid c1.oid JOIN pg_namespace n1 ON c1.relnamespace n1.oid JOIN pg_class c2 ON co.confrelid c2.oid JOIN pg_namespace n2 ON c2.relnamespace n2.oid JOIN pg_attribute a1 ON a1.attnum co.conkey[1] AND a1.attrelid c1.oid JOIN pg_attribute a2 ON a2.attnum co.confkey[1] AND a2.attrelid c2.oid WHERE co.contype f;3.4 问题四如何识别重复索引-- 发现可能冗余的索引 SELECT indrelid::regclass AS table_name, array_agg(indexrelid::regclass) AS indexes, array_agg(indkey::text) AS index_columns, count(*) AS duplicate_count FROM pg_index GROUP BY indrelid, indkey HAVING count(*) 1;3.5 问题五如何追踪表的历史修改-- 查看表的最后修改时间需要开启track_commit_timestamp SELECT c.relname AS table_name, n.nspname AS schema_name, pg_xact_commit_timestamp(xmin) AS created, pg_xact_commit_timestamp(xmax) AS last_modified FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid WHERE c.relkind r AND n.nspname public;4. 高级技巧系统表联查的四种模式4.1 星型查询以pg_class为中心-- 获取表的完整元数据画像 SELECT c.relname AS object_name, CASE c.relkind WHEN r THEN table WHEN i THEN index WHEN S THEN sequence WHEN v THEN view WHEN m THEN materialized view ELSE c.relkind::text END AS object_type, n.nspname AS schema_name, u.usename AS owner, pg_size_pretty(pg_total_relation_size(c.oid)) AS size, obj_description(c.oid, pg_class) AS comment FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid JOIN pg_user u ON c.relowner u.usesysid WHERE n.nspname NOT IN (pg_catalog, information_schema);4.2 雪花模型多表深度关联-- 深度分析表存储特性 SELECT c.relname AS table_name, n.nspname AS schema_name, a.attname AS column_name, t.typname AS data_type, a.attnotnull AS not_null, pg_get_expr(d.adbin, d.adrelid) AS default_value FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid JOIN pg_attribute a ON a.attrelid c.oid JOIN pg_type t ON a.atttypid t.oid LEFT JOIN pg_attrdef d ON (d.adrelid a.attrelid AND d.adnum a.attnum) WHERE c.relkind r AND n.nspname public AND a.attnum 0 ORDER BY c.relname, a.attnum;4.3 反向查询从属性找对象-- 查找包含特定列名的所有表 SELECT n.nspname AS schema_name, c.relname AS table_name, a.attname AS column_name FROM pg_class c JOIN pg_namespace n ON c.relnamespace n.oid JOIN pg_attribute a ON a.attrelid c.oid WHERE c.relkind r AND a.attname LIKE %price% AND n.nspname NOT IN (pg_catalog, information_schema);4.4 统计洞察系统表数据分析-- 数据库对象类型分布统计 SELECT CASE relkind WHEN r THEN table WHEN i THEN index WHEN S THEN sequence WHEN v THEN view WHEN m THEN materialized view ELSE relkind::text END AS object_type, COUNT(*) AS count, ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) AS percentage FROM pg_class GROUP BY relkind ORDER BY COUNT(*) DESC;5. 性能优化系统表查询的七个最佳实践限制结果集始终添加LIMIT子句避免意外返回大量数据SELECT * FROM pg_class LIMIT 100;使用OID连接系统表间连接应使用OID而非名称效率更高-- 优 JOIN pg_namespace n ON c.relnamespace n.oid -- 劣 JOIN pg_namespace n ON n.nspname public避免全表扫描为常用查询条件创建索引CREATE INDEX ON pg_class(relnamespace) WHERE relkind r;使用视图简化将复杂查询封装为视图CREATE VIEW user_tables AS SELECT ... FROM pg_class c JOIN ...;定期收集统计信息确保系统表统计信息准确ANALYZE pg_class;利用pg_catalog模式直接查询pg_catalog获取最新元数据SELECT * FROM pg_catalog.pg_tables;谨慎更新系统表直接修改系统表可能导致数据库损坏警告除非绝对必要否则永远不要直接UPDATE或DELETE系统表在最近一次数据库迁移项目中通过组合查询pg_class、pg_index和pg_attribute我成功识别出了20多个未被使用的索引为数据库减负超过50GB空间。这种精准的手术刀式优化正是系统表知识带来的实实在在的价值。

更多文章