news 2026/10/15 20:44:55

PostgreSQL高频SQL实战合集:查询、优化、运维一次搞定

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL高频SQL实战合集:查询、优化、运维一次搞定

1. 先说点实在的:这份合集到底解决什么问题

做后端开发和数据库运维这几年,我最大的感受就是:PostgreSQL 的功能太强了,但常用的就那几十条 SQL,每次都去翻官方文档实在浪费生命。昨天刚写过的查询,过两周又要想半天语法;MySQL 转过来的同事,天天把 LIMIT 1 挂在嘴边,完全忘了 PostgreSQL 的 FETCH FIRST 才是标准写法。这种零散的记忆碎片,攒着攒着就丢了一大半。

所以我决定把日常工作中真正高频使用的 SQL 整理成一份合集,按照使用场景分类,每一条都附上适用场景和踩坑记录。这份合集不是教科书,不是让你背语法,而是给你一种"关键时刻能直接抄作业"的底气。适用对象包括三类人:

  • 后端开发者:每天都在跟数据打交道,需要快速查询表结构、分析慢查询、处理数据更新。
  • 数据库运维/ DBA:需要排查连接异常、检查锁冲突、监控数据库膨胀状态。
  • 数据分析师或刚接触 PostgreSQL 的转岗同学:对系统表不熟悉,想快速找到"别人口中那条查询数据量的 SQL"。

从 PostgreSQL 9.6 到 15、16 我都实际用过,这套合集里的绝大多数 SQL 都能跨版本运行,个别新特性我会单独标出最低版本要求。文章会持续更新,每一批都在原有的分类下补充新的实战语句。

2. 查信息、看家底:连接库表的基础查询集合

2.1 版本、库与连接数检查

拿到一套陌生的环境,第一件事永远是搞清楚"我在哪、库有多大、连接有没有打满"。我刚接手一套业务系统的时候,第一步就是跑这些查询:

-- 当前 PostgreSQL 版本 SELECT version(); -- 当前连接数 SELECT count(*) FROM pg_stat_activity; -- 各库大小(单位 MB) SELECT datname, pg_size_pretty(pg_database_size(datname)) AS db_size FROM pg_database ORDER BY pg_database_size(datname) DESC;

这里有个容易踩的坑:连接数不要只看count(*),很多连接可能是 idle 状态,完全不影响性能。要判断连接是否够用,得看state的分布:

SELECT state, count(*) AS conn_count FROM pg_stat_activity GROUP BY state ORDER BY conn_count DESC;

正常情况下大部分连接应该是idle或active。如果出现大量idle in transaction,那基本就是应用代码里事务没及时提交,这种问题常见于使用连接池忘记关闭事务的情况。

pg_size_pretty()这个函数是 PostgreSQL 独有的,能把字节数自动转成 KB、MB、GB 这种可读格式,比我之前用 MySQL 时自己写除法换算方便太多了。库大小的查询在做迁移评估、容量规划时特别有用。

2.2 表大小、行数与膨胀判断

业务表到底有多大、多少行、膨胀了多少,这是 DBA 和开发者都关心的问题。下面这条 SQL 会把当前模式下的表按总大小排序,一次性看清楚所有表的"体重":

SELECT schemaname AS schema_name, tablename AS table_name, pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size, pg_size_pretty(pg_relation_size(schemaname || '.' || tablename)) AS table_size, pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename) - pg_relation_size(schemaname || '.' || tablename)) AS index_size FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC;

注意pg_total_relation_size包含表数据加索引加 TOAST,pg_relation_size只算表本身的堆文件。两者之差基本就是索引占用的空间。这是判断"到底是表大还是索引大"的最快手段。

关于行数:count(*)在大表上会全表扫描,生产环境不建议频繁执行。PostgreSQL 的统计信息里维护了一个行数估算值,可以这样拿:

SELECT relname AS table_name, n_live_tup AS estimate_rows FROM pg_stat_user_tables WHERE relname = 'your_table_name';

这个数字不是精确值,可能有百分之几甚至更多的偏差,但用来做容量评估足够。如果要精确行数,只能count(*),建议在业务低峰期执行。我在一次大促前评估订单表容量时,就是用这个统计信息快速判断出了哪个分区表膨胀最严重,省去了扫表的耗时。

2.3 表结构与字段信息

开发同学最常问的问题是:"这张表的字段都有啥?"PostgreSQL 里查字段信息靠的是information_schema.columns或者系统表pg_attribute。日常我用后者更多,因为可以顺便拿到注释:

SELECT a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type, col_description(a.attrelid, a.attnum) AS column_comment FROM pg_attribute a WHERE a.attrelid = 'your_table_name'::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum;

这里有个细节:a.attnum > 0是为了排除系统隐藏列,NOT a.attisdropped排除被删除的列。如果你写过 ALTER TABLE DROP COLUMN,就知道 PostgreSQL 其实不是物理删除列,而是标记一下,所以这个过滤条件不能少。

查外键关系的 SQL 也经常用,特别是拆表、拆库的时候:

SELECT tc.constraint_name, kcu.column_name AS source_column, ccu.table_name AS target_table, ccu.column_name AS target_column FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = 'your_table_name';

在数据迁移时,外键关系是必须提前梳理的,不然导入顺序一错就会报主键冲突。

3. 索引与性能诊断的黄金搭配

3.1 查看索引与索引使用率

索引是 PostgreSQL 性能的核心。我见过不少线上表,索引建得密密麻麻,但实际查询根本没走到。一条 SQL 看出一张表的所有索引:

SELECT indexname, pg_size_pretty(pg_relation_size(indexname::regclass)) AS index_size, indexdef FROM pg_indexes WHERE tablename = 'your_table_name';

indexdef列会输出完整的 CREATE INDEX 语句,这对排查"索引走了没走"非常有帮助,因为你可以看到她到底是 B-tree、GIN 还是 BRIN。

比看索引更重要的,是看索引的使用率:

SELECT schemaname, relname AS table_name, indexrelname AS index_name, idx_scan AS index_scan_times, idx_tup_read AS rows_read_by_index, idx_tup_fetch AS rows_fetched_by_index FROM pg_stat_user_indexes ORDER BY idx_scan ASC;

执行频率排在最后的索引,大概率是没用的索引。我处理过一次线上磁盘占用告警,排查下来发现某张订单历史表有 6 个索引,其中 3 个的idx_scan是 0。跟业务方确认后直接 DROP,磁盘压力瞬间缓解。当然,删除索引前一定要在测试环境复现查询计划,确认没有 SQL 依赖这个索引。

3.2 慢查询与正在执行的 SQL

PostgreSQL 排查慢查询,第一站永远是pg_stat_statements。这个模块默认没开启,需要配置shared_preload_libraries = 'pg_stat_statements'并重启库。开启后查询累计耗时最长的 SQL:

SELECT queryid, calls AS exec_count, round(total_exec_time::numeric, 2) AS total_ms, round(mean_exec_time::numeric, 2) AS avg_ms, round(max_exec_time::numeric, 2) AS max_ms, query FROM pg_stat_statements WHERE query NOT LIKE '%pg_stat_statements%' ORDER BY total_exec_time DESC LIMIT 20;

注意版本差异:PostgreSQL 13 之前的时间字段叫total_time、mean_time,13 之后改成了total_exec_time、mean_exec_time。我在 11 升 14 的时候被这个变化坑过一次,脚本直接报字段不存在。

如果不想等统计模块,也可以直接抓正在执行的慢 SQL:

SELECT pid, now() - query_start AS duration, state, wait_event_type || ': ' || wait_event AS wait_info, query FROM pg_stat_activity WHERE state = 'active' AND query NOT LIKE '%pg_stat_activity%' ORDER BY duration DESC;

这个查询非常实用。生产环境突然 CPU 飙升,十有八九是某条 SQL 在裸跑大表,用这条 SQL 一看就知道是谁在捣乱。duration字段用now() - query_start计算,比query_start裸看直观得多。

3.3 锁冲突与死锁快速定位

业务卡住了,大概率不是慢查询,而是锁冲突。PostgreSQL 查锁的经典视图是pg_locks,但直接看pg_locks太碎,我习惯用下面的关联查询,直接找出"谁堵了谁":

SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.query AS blocking_query, blocked.wait_event_type || ': ' || blocked.wait_event AS wait_info FROM pg_stat_activity blocked JOIN pg_stat_activity blocking ON blocked.pid = ANY( SELECT pid FROM pg_stat_activity WHERE pg_blocking_pids(blocked.pid) @> ARRAY[blocked.pid] ) WHERE blocked.wait_event_type = 'Lock';

这段 SQL 写起来有一点绕,我解释一下思路:pg_blocking_pids(blocked.pid)是 PostgreSQL 自带的函数,返回阻塞指定 pid 的进程数组。通过@>数组包含操作符找到阻塞源,再 self join 拿到两边各自的完整 SQL。

拿到阻塞源后,对症下手。state = 'idle in transaction'的会话往往是只开了事务没提交,可以直接pg_terminate_backend(blocking_pid)。但话放在前面:终止连接是外科手术式操作,先通知业务方,确认不是正在跑的关键任务再动手。

3.4 表膨胀与 VACUUM 状态

表膨胀是 PostgreSQL 特有的运维问题。更新和删除都会产生死元组,autovacuum 干活不及时,表就会越来越肿。查膨胀状态:

SELECT relname AS table_name, n_live_tup AS live_rows, n_dead_tup AS dead_rows, CASE WHEN n_live_tup > 0 THEN round(n_dead_tup::numeric / n_live_tup, 2) ELSE 0 END AS dead_ratio FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;

dead_ratio超过 0.2 就该关注了,超过 0.5 说明 autovacuum 已经跟不上了。这时可以手动执行VACUUM (VERBOSE, ANALYZE) your_table_name;来兜底。注意生产环境执行 VACUUM 会消耗 IO,尽量在业务低峰期做。

4. 高频 DML:更新、删除、去重的最佳实践

4.1 INSERT ... ON CONFLICT 做 upsert 的妙用

PostgreSQL 的ON CONFLICT语法是我从 MySQL 转过来后觉得最好用的功能之一。一条 SQL 搞定"存在就更新,不存在就插入",再也不用先 SELECT 判断再决定走 INSERT 还是 UPDATE:

INSERT INTO user_points (user_id, points, updated_at) VALUES (1001, 50, now()) ON CONFLICT (user_id) DO UPDATE SET points = user_points.points + EXCLUDED.points, updated_at = now();

EXCLUDED是 PostgreSQL 里的特殊行引用,表示那些"因为冲突而被丢弃的候选插入行"。上面的写法可以用在积分累加场景:同一用户有新积分进来,直接叠加。如果只想忽略冲突、不做更新,就写成DO NOTHING。

这里有一个隐藏的坑:ON CONFLICT需要唯一索引或主键支撑。冲突判断依据就是你在括号里写的那个字段上的唯一约束。如果没建唯一索引,执行时直接报there is no unique or exclusion constraint matching the ON CONFLICT specification。所以建表时一定要把业务上"唯一"的字段声明成唯一约束。

4.2 用 CTE 串联更新步骤,告别嵌套子查询

复杂业务逻辑里,经常需要"先算出一个结果集,再根据结果更新主表"。用 WITH 子句能大幅提高可读性:

WITH recent_orders AS ( SELECT customer_id, sum(amount) AS total_amount FROM orders WHERE created_at >= now() - interval '7 days' GROUP BY customer_id ) UPDATE customers c SET last_7d_order_amount = r.total_amount FROM recent_orders r WHERE c.id = r.customer_id;

PostgreSQL 的 UPDATE 支持FROM子句,可以直接跟另一个表或 CTE 做关联更新。这在 MySQL 里写起来要麻烦得多。同类写法还可以用在删除上:DELETE FROM ... USING ... WHERE ...。

4.3 删除重复数据,只保留一条

业务表缺唯一约束导致重复数据,这种场景我见过太多次了。CTID 是 PostgreSQL 里的物理行标识,每一行都有独立的 ctid,利用它删除重复记录特别巧妙:

DELETE FROM user_emails e USING user_emails e2 WHERE e.email = e2.email AND e.ctid < e2.ctid;

原理:e.ctid < e2.ctid表示在同一个 email 分组下,保留 ctid 最大的那条,删除其余所有。这比先查出来再逐条 DELETE 效率高一个数量级。执行前务必先跑一遍 SELECT 确认要删的数量:

SELECT count(*) FROM user_emails e JOIN user_emails e2 ON e.email = e2.email WHERE e.ctid < e2.ctid;

4.4 批量删除历史数据,控制单次删除大小

一次性 DELETE 几百万行数据,通常会锁表锁很久,还把 WAL 日志撑爆。稳妥的方案是分批删除。用ctid范围或者时间窗口都可以:

-- 每批删除 1 万条,循环执行,直到影响行数为 0 DELETE FROM audit_logs WHERE created_at < now() - interval '90 days' AND ctid IN ( SELECT ctid FROM audit_logs WHERE created_at < now() - interval '90 days' LIMIT 10000 );

分批删除为什么有效?因为每次 DELETE 事务都很短,锁持有时间就短,其他业务查询的阻塞时间会显著下降。ctid子查询的方式能保证每次删除的行分散在物理存储上,避免连续删除造成页面热点。

我自己在大促后清理过期订单流水时,会配合pg_sleep在每批之间暂停几秒,给主从同步留出缓冲时间。循环脚本可以用 shell 或任何语言写,核心就是"一批删完看影响行数,不为 0 就继续"。

4.5 RETURNING 拿到操作后的实时数据

UPDATE 或 INSERT 之后需要立刻用新数据继续业务逻辑,很多人会选择重新 SELECT 一次。其实 PostgreSQL 的RETURNING可以直接把变更后的行返回:

UPDATE inventory SET quantity = quantity - 5 WHERE product_id = 99 RETURNING product_id, quantity AS new_quantity;

应用层代码里就可以省掉一次查询。比如扣库存后要返回剩余数量给前端,不用再查库,代码路径短了一半。

5. 函数与特殊类型:日期、JSON、数组的高频玩法

5.1 日期时间处理:date_trunc 与 interval

PostgreSQL 的日期时间函数非常强大,date_trunc用来按天、按小时、按月分组统计特别方便:

SELECT date_trunc('day', created_at) AS day, count(*) AS order_count FROM orders GROUP BY date_trunc('day', created_at) ORDER BY day DESC LIMIT 30;

date_trunc支持'minute'、'hour'、'day'、'week'、'month'、'quarter'、'year'等粒度,做报表时几乎取代了繁琐的DATE_FORMAT+GROUP BY组合。注意它返回的是 timestamp 类型,不是字符串,所以排序和后续计算都更自然。

算两个时间点之间的天数差:

SELECT extract(epoch FROM (now() - created_at)) / 86400 AS days_ago FROM orders ORDER BY created_at DESC LIMIT 1;

extract(epoch FROM interval)会把时间间隔转成秒数,再除以 86400 换算成天。这个方法比直接减时间再转天数要精确得多,不受夏令时等时区变化影响。

5.2 JSONB 操作:取值、更新、查询一次说清

PostgreSQL 的 JSONB 类型几乎是半个文档数据库。日常操作主要集中在四个运算符:

  • ->返回 JSONB 类型,用于继续链路操作。
  • ->>返回文本类型,用于普通字符串比较。
  • #> 或 #>>根据路径取深层值。
  • @>判断 JSONB 是否包含某个结构。

实际场景,从字段里取用户信息:

SELECT >UPDATE user_events SET data = jsonb_set(data, '{address, city}', '"新城市"', true) WHERE id = 1;

这里有个 version 注意点:jsonb_set的第四个参数create_missing控制路径不存在时是否自动创建。PostgreSQL 14 之前必须显式传,14 之后默认值变更为true,不传也能自动创建。这是升级后行为变化的典型案例,代码在 13 上能跑,到 14 上行为突然不一样了。

5.3 数组与聚合:unnest、array_agg、string_agg

unnest是处理数组和 JSON 数组的利器。比如某张 tags 字段存的是逗号分隔的字符串,想拆成多行查统计:

SELECT tag, count(*) AS tag_count FROM user_articles, unnest(string_to_array(tags, ',')) AS tag GROUP BY tag ORDER BY tag_count DESC;

LATERAL是隐式发生的,unnest在 FROM 中展开后自然与主表形成关联。string_to_array负责把 CSV 转数组,再由unnest展开成行。

反过来,把多行聚合成一行用array_agg或string_agg:

SELECT order_id, string_agg(product_name, '、' ORDER BY product_name) AS product_list FROM order_items GROUP BY order_id;

string_agg的排序参数是可选的,但建议总是带上。不指定顺序时,字符串拼接顺序不可控,常见于多并发执行时结果不稳定。

6. 权限、备份与日常维护的必背 SQL

6.1 用户与权限快速查询

新同学接手系统,搞不清谁对哪个库有什么权限?下面这条直接列出所有非系统用户的角色和归属:

SELECT r.rolname AS role_name, r.rolsuper AS is_super, r.rolcreatedb AS can_create_db, r.rolcanlogin AS can_login, ARRAY(SELECT b.rolname FROM pg_auth_members m JOIN pg_roles b ON b.oid = m.roleid WHERE m.member = r.oid ) AS member_of FROM pg_roles r WHERE r.rolname NOT LIKE 'pg_%' ORDER BY r.rolname;

给业务账号只读权限是标配操作:

GRANT CONNECT ON DATABASE your_db TO readonly_user; GRANT USAGE ON SCHEMA public TO readonly_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user; -- 对将来新建的表也自动授予权限 ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_user;

ALTER DEFAULT PRIVILEGES这行特别关键。如果只执行前三条,新表创建后只读账号依然看不到。我在一个数据分析平台上吃过这个亏:报表账号头一天还能查,第二天开发建了新表,查询直接报权限不足。找了一圈原因才发现漏了默认权限。

6.2 VACUUM 与 ANALYZE 不只是命令

很多人把 VACUUM 和 ANALYZE 当成同一个概念,其实分工不同。VACUUM 回收死元组占用的存储空间,ANALYZE 更新统计信息供查询计划器使用。二者经常连用,但触发时机不一样:

-- 只更新统计信息,不加锁,适合频繁执行 ANALYZE your_table_name; -- 回收空间并更新统计信息,依旧在线,不会锁表阻塞读写 VACUUM (ANALYZE, VERBOSE) your_table_name;

关于 VACUUM 有个经典误解:VACUUM 不回收空间给操作系统,而是交还给 PostgreSQL 内部。所以执行完成后用ls -lh看文件大小可能没变化,只有pg_size_pretty(pg_total_relation_size(...))变小才说明真正回收了。真要缩文件给操作系统,只能VACUUM FULL,但这会拿到 ACCESS EXCLUSIVE 锁,生产环境必须极其谨慎,我通常只在维护窗口使用。

Tracking autovacuum 是否卡住,看这个视图:

SELECT schemaname, relname, last_autovacuum, last_autoanalyze FROM pg_stat_user_tables WHERE last_autovacuum IS NULL OR last_autoanalyze IS NULL;

长时间没有 autovacuum 记录的表,要么是大表因长事务被跳过,要么是配置问题导致 worker 不足。这是排查"表越来越肥"的起点。

6.3 快速生成批量清理脚本的思路

维护环境里经常要批量对几十张表执行同样操作,比如把某模式下所有表的所有者改掉:

SELECT format('ALTER TABLE %I.%I OWNER TO new_owner;', schemaname, tablename) FROM pg_tables WHERE schemaname = 'public';

把查询结果导出成 SQL 脚本再执行即可。format函数里的%I是安全的标识符占位符,会自动处理表名中的特殊字符,防止注入问题。同理,批量授权、批量开启 RLS、批量检查膨胀都能用这种"查询生成 SQL"的模式。

批量给所有表添加外键约束,也可以先把候选语句查出来,人工审一遍再执行,比手写几十条 UPDATE 安全得多。

7. 生产环境高频报错的排查实录

7.1 连接数打满:remaining connection slots

报错信息一般是FATAL: sorry, too many clients already或者remaining connection slots are reserved for non-replication superuser connections。这是生产环境最常见的故障之一。

排查思路是固定的:

  1. 先看当前连接数分布,找出占用大头:SELECT state, count(*) FROM pg_stat_activity GROUP BY state;
  2. 重点找idle in transaction会话:SELECT pid, state, query_start, xact_start FROM pg_stat_activity WHERE state = 'idle in transaction';
  3. 找到问题会话后,跟业务方确认再终止:SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - xact_start > interval '10 minutes';

这里谈一个经验点:与其等连接满了再清理,不如在应用层设置连接池最大连接数,并给 PostgreSQL 的max_connections留出 20% 的冗余量。连接池打满和数据库打满,处理难度完全是两个级别。

7.2 主键冲突背后的序列断层

插入数据时报duplicate key value violates unique constraint ...,很多新人第一反应是数据重复,但还有一种隐蔽原因:表主键用了序列,但序列的当前值小于表内已有最大 id。

这种问题多发生在手工导数据、跨环境迁移数据之后。排查语句:

SELECT nextval('your_table_id_seq'); -- 当前序列值 SELECT max(id) FROM your_table_name; -- 表内实际最大 id

如果序列值小于等于表内最大 id,修复方式:

SELECT setval('your_table_id_seq', (SELECT max(id) FROM your_table_name));

注意 setval 的第二个参数是最小目标值,执行后下一次 nextval 会返回 max+1。这种坑在迁移数据时特别常见,我在恢复备份后就踩过,之后养成了迁移后必查序列的习惯。

7.3 慢查询与执行计划异常的处理路径

某条 SQL 昨天还快,今天突然慢了?第一反应不是加索引,而是看执行计划变没变:

EXPLAIN ANALYZE SELECT ...;

关键看 Seq Scan vs Index Scan 的占比。统计信息过期导致错误选择全表扫描的情况,十有八九是 ANALYZE 没跟上。这时先执行一遍ANALYZE your_table_name;,再重查执行计划,很多时候问题直接消失。

如果 ANALYZE 之后依然走全表扫描,再思考:是不是条件列的数据分布倾斜,或者索引本身选择性不足?用pg_stats查看字段分布:

SELECT attname, n_distinct, most_common_vals, most_common_freqs FROM pg_stats WHERE tablename = 'your_table_name' AND attname = 'the_column';

一张表的数据只有几百行时,索引扫描反而不如全表扫描快,这是优化器基于代价做出的合理选择,无需强行干预。

7.4 字符集与编码相关的隐性问题

PostgreSQL 的库是在初始化时确定编码的,最常见的默认值是 UTF8。乱码问题大多发生在客户端连接编码不对,而不是数据库本身出问题。遇到乱码,先看:

SHOW client_encoding; SHOW server_encoding;

如果二者不一致,执行SET client_encoding TO 'UTF8';再重新查询。另一种情况是locale和排序规则不一致导致的索引失效,常见于从旧库"克隆"数据而不是逻辑导入。这类问题排查起来特别隐性,页面端显示正常,排序顺序却是乱的。

稳妥做法:同构迁移时保持 LC_COLLATE / LC_CTYPE 一致,异构迁移完成立即抽查中文字段排序和索引走没走。

8. 这份合集的使用方式与维护计划

我个人在使用和维护这份合集时的几条真实体会,或许对你有参考价值。

体会一:SQL 合集不是越全越好,而是越"场景化"越好。这份文档里我坚持每条 SQL 都配上"什么时候用、为什么不这么写就会出问题"。有了场景锚点,记忆效率会大幅提升。你在收藏别人整理的大段文档时,也可以尝试用自己的业务场景重新注释一遍。

体会二:系统表和视图的记忆有规律可循。pg_stat_*系列全是运行时统计,比如pg_stat_activity当前活跃会话、pg_stat_user_tables表的统计信息、pg_stat_user_indexes索引使用统计。pg_*不带stat的是元数据。information_schema是标准 SQL 视图,跨数据库兼容性好。我用一个本地笔记本把这些视图的关系画出来之后,查任何系统信息都能快速定位正确视图。

体会三:这些 SQL 可以落成监控脚本。我目前是把关键几条,比如连接数分布、死锁阻塞链、表膨胀比例,做成了定时巡检任务,每天凌晨跑一遍,异常就推告警。效果比事后处理强很多。

这份合集我不打算一次性写完,而是按专题持续分批补充。下一批计划补以下方向:

  • 分区表维护相关的管理 SQL,包括分区拆分的流程和查各分区数据占比。
  • 逻辑复制和订阅状态监控,排查复制延迟的专用语句。
  • 更细的锁等待分析,区分行锁、页锁、咨询锁,以及锁超时配置建议。
  • SQL 调优时使用EXPLAIN各类选项的实际解读案例。

你可以把这篇文章当作一个长期维护的手册来用,每次遇到新问题、沉淀出可复用的语句,我都会同步更新进来。如果有你觉得特别实用但这里没有的语句,也欢迎在评论区留言,下一批更新我会优先补上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 20:44:15

邮件礼仪与Outlook技巧:从收件人到延迟发送的避坑指南

简介&#xff1a;这份PPT资料聚焦电子邮件礼仪与Outlook使用技巧&#xff0c;面向职场人士、行政人员及需要频繁进行邮件沟通的高校师生&#xff0c;帮助解决邮件回复不及时、收件人选择不当、标题含糊、内容冗长等常见沟通问题&#xff0c;同时提升Outlook客户端的实操效率。资…

作者头像 李华
网站建设 2026/10/15 20:43:24

NTRIP客户端实战:从协议到源码,打通RTK厘米级定位的最后一公里

简介&#xff1a;资源包提供一份基于C语言编写的NTRIP客户端源码&#xff0c;用于与NTRIP服务器实时通信并获取RTCM差分校正数据&#xff0c;从而支持厘米级实时动态定位&#xff0c;主要面向测绘、导航与物联网领域开发者&#xff0c;适合协议学习、算法验证及二次开发。压缩包…

作者头像 李华
网站建设 2026/10/15 20:42:41

单频测向插件地图与表盘实现

单频测向插件地图与表盘实现下面按 地图 / 表盘&#xff08;BearingMeter&#xff09; 两块说明&#xff0c;并单独把 paintEvent 的“图层”顺序 说清楚。1. 地图&#xff08;UIFIXDFPlugin&#xff09; 入口&#xff1a;InitControl() 里给 quickWidget 加载 qrc:/Resource/m…

作者头像 李华
网站建设 2026/10/15 20:40:36

【论文解读】OLED IR Drop机理及数字补偿

1 论文信息 论文题目 A Novel Method to Reduce Luminance Variation Due to IR-drop in Active Matrix OLED Displays作者单位 samsung论文内容 详见文章附件 2 论文解读 2.1 IR Drop机理 如下图右侧&#xff0c;OLED 像素驱动TFT为P mos&#xff0c;G级连接Vdata&#xff0c;…

作者头像 李华