📝 摘要 :MySQL 用户第一次进 psql 就懵------
SHOW DATABASES、USE全不认。本文把 PostgreSQL 最常用命令一篇讲清:psql 反斜杠元命令、库 / schema / 表管理、用户权限、\copy与pg_dump导入导出、运维排查三板斧(pg_stat_activity看连接、pg_terminate_backend杀查询、pg_stat_statements抓慢 SQL),每类附 MySQL 对照。
MySQL 老司机第一次连上 PostgreSQL,通常三秒破防:敲
SHOW DATABASES;------ 报错;敲
USE mydb;------ 还是报错;敲
SHOW TABLES;------ 继续报错。那一刻的心理活动是:「这数据库怎么啥都不会?」
------不是它不会,是 PostgreSQL 把这些操作交给了 psql 的反斜杠元命令。搞懂这套「暗号」,你就从「啥都报错」瞬间切换到「行云流水」。这篇就是那本暗号手册。

一、连接与 psql 起步
bash
# 基本连接:-h 主机 -p 端口(默认5432) -U 用户 -d 库名
psql -h 127.0.0.1 -p 5432 -U postgres -d mydb
# 也可以用连接串(URI)
psql "postgresql://user:password@127.0.0.1:5432/mydb"
# 不进交互界面,直接跑一条 SQL 拿结果(运维 / 脚本最常用)
psql -h 127.0.0.1 -U postgres -d mydb -c "SELECT count(*) FROM app.account;"
# 数据库跑在 Docker 里时,套一层 docker exec 即可
docker exec -it <容器名> psql -U postgres -d mydb -c "SELECT ...;"
进到 psql 交互界面后,先记住这几个「保命键」:
| 命令 | 作用 |
|---|---|
\? |
psql 元命令帮助(反斜杠命令一览) |
\h |
SQL 语法帮助,如 \h SELECT、\h CREATE TABLE |
\conninfo |
当前连的哪个库、哪个用户、哪个端口 |
\q |
退出 |
⚠️ 关键分工:
\h查的是 SQL 语句 语法,\?查的是 psql 自己的反斜杠命令。两个帮助别搞混。
二、psql 元命令速查(最核心,最 PG 特色)
MySQL 里的 SHOW xxx 在 PostgreSQL 里基本都对应一个反斜杠命令。这张对照表是全文最该收藏的:
| psql 元命令 | 作用 | MySQL 对应 |
|---|---|---|
\l |
列出所有数据库 | SHOW DATABASES; |
\c dbname |
切换数据库 | USE dbname; |
\dt |
列出当前 schema 的表 | SHOW TABLES; |
\d 表名 |
查看表结构 | DESC 表名; |
\d+ 表名 |
表结构 + 大小 + 详情 | SHOW CREATE TABLE(近似) |
\dn |
列出所有 schema | ------ |
\du |
列出所有角色 / 用户 | SELECT user FROM mysql.user; |
\di / \dv / \df |
列出索引 / 视图 / 函数 | ------ |
\dt *.* |
跨 schema 列表 | ------ |
\x |
切换「竖排显示」(宽表神器) | \G(行尾) |
\timing |
开关「显示每条 SQL 耗时」 | ------ |
\e |
用编辑器写 SQL | ------ |
\i 文件 |
执行 SQL 脚本文件 | source 文件 |
\! 命令 |
执行 shell 命令 | \! 命令 |
几个高频到必须形成肌肉记忆的:
\x:字段多的宽表,横着看到吐血?敲一下\x,切成一行一个字段的竖排,瞬间清爽(等价于 MySQL 的行尾\G)。\timing:开一次,之后每条 SQL 都自动报耗时,随手测性能。\dtS/\dS:命令加大写S会连系统对象一起显示(S = System),排查系统表时用得上。
三、库 / schema / 表管理(DDL)
这块和标准 SQL 基本一致,但有个 MySQL 用户绕不开的概念差异------PostgreSQL 多了一层 schema:
text
MySQL: 实例 → database(≈schema) → table
Postgres:实例 → database → schema(默认 public) → table
sql
-- 建库
CREATE DATABASE mydb;
-- 建 schema(一个库下可分多个命名空间)
CREATE SCHEMA app;
-- 建表(注意自增列写法和 MySQL 不同)
CREATE TABLE app.account (
id BIGSERIAL PRIMARY KEY, -- 自增:SERIAL/BIGSERIAL,或 GENERATED ... AS IDENTITY
username VARCHAR(64) NOT NULL,
balance NUMERIC(12,2) DEFAULT 0, -- 金额用 NUMERIC,别用 float
created_at TIMESTAMPTZ DEFAULT now()
);
-- 改表
ALTER TABLE app.account ADD COLUMN status SMALLINT DEFAULT 0;
-- 搜索路径:设了之后可以不写 schema 前缀直接用表名
SET search_path TO app, public;
search_path就是 PostgreSQL 版的「默认 schema」。设成app后,SELECT * FROM account就等价于app.account------省去每次写 schema 前缀。
四、用户与权限
PostgreSQL 里用户(USER)本质就是带登录权限的角色(ROLE) ,CREATE USER = CREATE ROLE ... LOGIN:
sql
-- 建用户
CREATE USER app_rw WITH PASSWORD 'secret';
-- 授权:库、schema、表逐层给
GRANT CONNECT ON DATABASE mydb TO app_rw;
GRANT USAGE ON SCHEMA app TO app_rw;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_rw;
-- 让「以后新建的表」也自动带上权限(否则新表要重新授权)
ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_rw;
-- 改密码(psql 内推荐用 \password,密码不会明文进历史)
\password app_rw
一个经典坑:给了
ALL TABLES权限,但之后新建的表 app_rw 又没权限了。解药就是上面的ALTER DEFAULT PRIVILEGES------给「未来的表」预授权。
五、导入导出
这里有个 MySQL 用户最容易踩的坑:\copy 和 COPY 长得像,权限却完全不同。
| 命令 | 在哪执行 | 读写哪台机器的文件 | 权限 |
|---|---|---|---|
\copy(psql 元命令) |
psql 客户端 | 你本地的文件 | 普通用户即可 |
COPY(SQL 语句) |
服务端 | 数据库服务器上的文件 | 需超级用户 / 特定角色 |
sql
-- 导出一张表到本地 CSV(客户端,最常用)
\copy app.account TO '/tmp/account.csv' WITH CSV HEADER
-- 从本地 CSV 导入
\copy app.account FROM '/tmp/account.csv' WITH CSV HEADER
-- 导出查询结果
\copy (SELECT id, username FROM app.account WHERE status = 0) TO '/tmp/act.csv' CSV HEADER
整库 / 逻辑备份用 pg_dump(命令行,不是 psql 内):
bash
# 自定义格式备份(推荐,支持并行 + 选择性恢复)
pg_dump -h 127.0.0.1 -U postgres -d mydb -Fc -f mydb.dump
# 恢复
pg_restore -h 127.0.0.1 -U postgres -d mydb_new mydb.dump
# 纯 SQL 文本备份(可读、可 grep)
pg_dump -h 127.0.0.1 -U postgres -d mydb -f mydb.sql
# 备份所有库 + 全局角色
pg_dumpall -h 127.0.0.1 -U postgres -f all.sql
图形化迁移(DBeaver)的姿势可参考 《MySQL 数据库迁移/复制实战:DBeaver 图形化 + 命令行两种方式》,思路在 PostgreSQL 上同样适用。
六、运维与排查(线上救命)
这是线上真正救命的部分。MySQL 的 SHOW PROCESSLIST + KILL 那套,在 PostgreSQL 里换了套写法。先记「三板斧」------看连接(pg_stat_activity)、杀查询(pg_terminate_backend)、抓慢 SQL(pg_stat_statements),够应付大多数现场;再往下的抓锁、看大小、EXPLAIN、VACUUM、调参兜底是进阶补充。整条排查路径串起来是这样:
#mermaid-svg-8V6G3oj9va3RB4Ot{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8V6G3oj9va3RB4Ot .error-icon{fill:#552222;}#mermaid-svg-8V6G3oj9va3RB4Ot .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8V6G3oj9va3RB4Ot .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8V6G3oj9va3RB4Ot .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8V6G3oj9va3RB4Ot .marker.cross{stroke:#333333;}#mermaid-svg-8V6G3oj9va3RB4Ot svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8V6G3oj9va3RB4Ot p{margin:0;}#mermaid-svg-8V6G3oj9va3RB4Ot .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot .cluster-label text{fill:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot .cluster-label span{color:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot .cluster-label span p{background-color:transparent;}#mermaid-svg-8V6G3oj9va3RB4Ot .label text,#mermaid-svg-8V6G3oj9va3RB4Ot span{fill:#333;color:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot .node rect,#mermaid-svg-8V6G3oj9va3RB4Ot .node circle,#mermaid-svg-8V6G3oj9va3RB4Ot .node ellipse,#mermaid-svg-8V6G3oj9va3RB4Ot .node polygon,#mermaid-svg-8V6G3oj9va3RB4Ot .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8V6G3oj9va3RB4Ot .rough-node .label text,#mermaid-svg-8V6G3oj9va3RB4Ot .node .label text,#mermaid-svg-8V6G3oj9va3RB4Ot .image-shape .label,#mermaid-svg-8V6G3oj9va3RB4Ot .icon-shape .label{text-anchor:middle;}#mermaid-svg-8V6G3oj9va3RB4Ot .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8V6G3oj9va3RB4Ot .rough-node .label,#mermaid-svg-8V6G3oj9va3RB4Ot .node .label,#mermaid-svg-8V6G3oj9va3RB4Ot .image-shape .label,#mermaid-svg-8V6G3oj9va3RB4Ot .icon-shape .label{text-align:center;}#mermaid-svg-8V6G3oj9va3RB4Ot .node.clickable{cursor:pointer;}#mermaid-svg-8V6G3oj9va3RB4Ot .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8V6G3oj9va3RB4Ot .arrowheadPath{fill:#333333;}#mermaid-svg-8V6G3oj9va3RB4Ot .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8V6G3oj9va3RB4Ot .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8V6G3oj9va3RB4Ot .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8V6G3oj9va3RB4Ot .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8V6G3oj9va3RB4Ot .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8V6G3oj9va3RB4Ot .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8V6G3oj9va3RB4Ot .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8V6G3oj9va3RB4Ot .cluster text{fill:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot .cluster span{color:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8V6G3oj9va3RB4Ot .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8V6G3oj9va3RB4Ot rect.text{fill:none;stroke-width:0;}#mermaid-svg-8V6G3oj9va3RB4Ot .icon-shape,#mermaid-svg-8V6G3oj9va3RB4Ot .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8V6G3oj9va3RB4Ot .icon-shape p,#mermaid-svg-8V6G3oj9va3RB4Ot .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8V6G3oj9va3RB4Ot .icon-shape .label rect,#mermaid-svg-8V6G3oj9va3RB4Ot .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8V6G3oj9va3RB4Ot .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8V6G3oj9va3RB4Ot .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8V6G3oj9va3RB4Ot :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 某条 SQL 跑太久
会话互相卡住
反复慢 · 找共性
线上出问题:慢 / 卡 / 连接满
① 看谁在跑
pg_stat_activity
(对标 SHOW PROCESSLIST)
什么现象?
② 杀查询
先 pg_cancel_backend,不行再 pg_terminate_backend
雪崩时按 库 / 时长 / ILIKE 批量杀
抓锁
pg_blocking_pids(pid)
看谁挡着谁
③ 抓慢 SQL
pg_stat_statements
按 total_exec_time 排序
EXPLAIN (ANALYZE, BUFFERS)
Seq Scan → 缺索引
CREATE INDEX CONCURRENTLY 不锁表
兜底止血
statement_timeout 封顶
max_parallel_workers_per_gather=0
ALTER SYSTEM + pg_reload_conf 免重启
治本
VACUUM 治膨胀 · 加索引 · OLAP 别塞 OLTP
图:PostgreSQL 运维排查三板斧流程------先用 pg_stat_activity 看谁在跑,按现象分流到「杀查询 / 抓锁 / 抓慢 SQL」,EXPLAIN 定位缺索引,statement_timeout 等参数兜底止血,最后 VACUUM 与加索引治本。
6.1 看谁在跑、看连接数(对标 SHOW PROCESSLIST)
sql
-- 当前所有会话 / 正在跑的 SQL
SELECT pid, usename, state, wait_event_type,
now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY duration DESC;
-- 连接数总览(排查连接打满)
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
⚠️
query字段有两个必知的坑(实战里一眼就会撞上):
SQL 显示不全 :
query默认被track_activity_query_size截断到 1024 字节 ,长 SQL 直接被砍一半。要看全,调大它(改postgresql.conf后需重启 ):
conftrack_activity_query_size = 8192 # 或更大SQL 里出现
$1、$2::timestamp:这是预编译 / 参数化查询 (ORM、JDBC PreparedStatement 常见)的占位符------pg_stat_activity按设计只存参数化后的语句、不存实际参数值 ,所以你看到的是$1而不是真实值。想拿到带参数值的完整 SQL,得走日志 而不是这张视图:
conflog_min_duration_statement = 1000 # 记录 >1s 的慢语句 log_parameter_max_length = -1 # PG13+:日志里完整打印绑定参数值($1 = '...')然后去 PostgreSQL 日志里看,慢语句会连
parameters: $1 = 'xxx'一起打出来。另外,
state = 'idle'的行,query显示的是它最后一次执行 的语句(不是正在跑)------排查"当前在跑什么"记得WHERE state <> 'idle'过滤掉。
6.2 杀查询 / 杀连接(对标 KILL)
sql
-- 温柔取消一条正在跑的 SQL(只取消查询,连接保留)
SELECT pg_cancel_backend(12345); -- 12345 = 上面查到的 pid
-- 强制杀掉整个连接(连接一起断)
SELECT pg_terminate_backend(12345);
记忆法:
cancel= 撤销这条 SQL(相当于 Ctrl+C),terminate= 掐断整个连接。优先 cancel,不行再 terminate。
慢查询并发把 CPU 打爆时,一个个 pid 杀太慢,直接批量杀------按库、时长、语句类型筛出来一次清掉:
sql
-- 一键杀掉某库里所有「跑超过 30 秒的 SELECT」
SELECT pid, pg_terminate_backend(pid), left(query, 60) AS killed
FROM pg_stat_activity
WHERE datname = 'mydb' -- 只动这个库
AND state = 'active'
AND now() - query_start > interval '30 seconds'
AND query ILIKE 'SELECT%' -- 只杀读查询,不碰写入 / INSERT
AND pid <> pg_backend_pid(); -- 别把执行这条命令的自己也杀了
三个安全阀缺一不可:
datname=限定库、ILIKE 'SELECT%'只杀读、pid <> pg_backend_pid()不误杀自身。线上慢查询雪崩时,这一条比逐个pg_terminate_backend(pid)快得多。注意:若是前端页面(如某 dashboard)自动重发的查询,杀完会立刻又冒出来,得先让人关掉页面。
6.3 抓锁(谁卡住了谁)
sql
-- 阻塞关系:谁在等锁、被谁挡着
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_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.wait_event_type = 'Lock';
pg_blocking_pids(pid)是排查锁等待的神器------直接告诉你「这个被卡的会话,是被哪些会话挡着的」。
6.4 看大小(库、表、索引)
sql
-- 各库大小
SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;
-- 当前库里最大的 10 张表(含索引)
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;
6.5 抓慢 SQL(pg_stat_statements)
PostgreSQL 抓慢 SQL 的标配是 pg_stat_statements 扩展------按累计耗时排序,谁最费时间一目了然:
sql
-- 一次性安装(需在 postgresql.conf 的 shared_preload_libraries 里加它并重启)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- 找出最耗时的 Top 10 SQL
SELECT queryid, calls, total_exec_time, mean_exec_time, query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
⚠️ 版本坑 :
total_exec_time/mean_exec_time是 PG13+ 的列名;PG12 及以前叫total_time/mean_time,直接跑上面这条会报「列不存在」。老版本把两处列名换掉即可。
6.6 EXPLAIN:看执行计划(慢 SQL 的下一步)
用 pg_stat_statements 揪出慢 SQL 后,下一步就是 EXPLAIN 看它到底慢在哪------PostgreSQL 的 EXPLAIN 比 MySQL 的信息量大得多:
sql
-- 只看计划(不执行,看的是优化器的"估算")
EXPLAIN SELECT * FROM app.account WHERE username = 'tom';
-- 真正执行并给出「实际耗时 + 实际行数」,排查必用
EXPLAIN ANALYZE SELECT * FROM app.account WHERE username = 'tom';
-- 加上缓存命中情况(看走没走 shared_buffers / page cache),最完整
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM app.account WHERE username = 'tom';
怎么读:
Seq Scan(全表扫描) 出现在大表上 = 危险信号,通常意味着缺索引 ;理想是走Index Scan/Index Only Scan。cost=0.00..25.88:优化器估算的启动成本...总成本(相对值,越大越贵)。rows=估算行数 vsactual ... rows=实际行数 :两者差距悬殊,说明统计信息过期 ,该ANALYZE更新统计了。actual time=/loops=:真实耗时和循环次数,EXPLAIN ANALYZE才有。
确认是「大表缺索引」导致的
Seq Scan后,补索引别直接CREATE INDEX------它会加锁、写阻塞整张表。线上大表用CREATE INDEX CONCURRENTLY ON app.account(username);:不锁表、后台慢慢建(代价是更耗时,且失败会残留一个INVALID索引,需DROP掉重建)。
⚠️ 一个能捅娄子的坑 :EXPLAIN ANALYZE是真的会执行语句 的!对SELECT无所谓,但对INSERT/UPDATE/DELETE------数据真的会被改。要分析写语句,务必包在事务里回滚:
sqlBEGIN; EXPLAIN ANALYZE UPDATE app.account SET status = 1 WHERE id = 100; ROLLBACK; -- 只看计划,不落地
6.7 VACUUM 与 ANALYZE(PostgreSQL 独有的必修课)
PostgreSQL 的 MVCC 机制会留下「死元组」,需要 VACUUM 回收------这是 MySQL 用户没有的概念,但不懂它迟早被表膨胀坑到:
sql
VACUUM ANALYZE app.account; -- 回收死元组 + 更新统计信息(日常)
VACUUM FULL app.account; -- 彻底重整、真正释放磁盘,但会锁表,谨慎用
ANALYZE app.account; -- 只更新统计信息,帮优化器选对执行计划
怎么判断一张表「该不该 VACUUM / ANALYZE」------看死元组多不多、上次自动维护是什么时候:
sql
-- 死元组数、上次 autovacuum / autoanalyze 时间,按死元组降序
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_analyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
n_dead_tup(死元组)居高不下、last_autovacuum半天没更新,就是 autovacuum 没跟上、表在膨胀的信号------大表尤其要盯。上一节EXPLAIN里「估算行数 vs 实际行数差很大」也常源于此(统计信息过期),补一次ANALYZE即可。
平时有 autovacuum 自动兜底,一般不用手动。但大批量删除 / 更新后表膨胀明显时,可手动VACUUM;想真正把磁盘还给系统,才用会锁表的VACUUM FULL。
6.8 运行时调参与兜底(改配置不重启 + 防慢查询拖垮机器)
线上出问题时,很多参数不用重启就能改 ------ALTER SYSTEM 写进配置、再 pg_reload_conf() 让它生效:
sql
-- 通用套路:改参数 → 重载,立即生效(对「可 reload」的参数有效)
ALTER SYSTEM SET log_min_duration_statement = '1000'; -- 记录 >1s 的慢语句
SELECT pg_reload_conf(); -- 重读配置,无需重启
SHOW log_min_duration_statement; -- 确认生效值
⚠️
ALTER SYSTEM改的参数分两类:标了context = postmaster的(如shared_preload_libraries)仍要重启 才生效;大多数运行时参数(日志、超时、并行度)pg_reload_conf()就够。这正好补上第六节开头「改postgresql.conf需重启」的另一条路------能 reload 的别重启。
几条线上救命的兜底参数:
sql
-- 1. 语句超时:不让任何查询无限跑(一条几分钟的全表扫就足以拖死机器)
-- 只挂在某个库上,别设全局,免得误杀别的库的正常长事务
ALTER DATABASE mydb SET statement_timeout = '60s';
-- 2. 关掉并行全表扫:2~4 核小机器上,一条慢查询拉起 N 个 parallel worker 一起刷盘 = 自杀
-- 关了之后慢查询最多占一个核、盘上只有一路顺序读
ALTER SYSTEM SET max_parallel_workers_per_gather = 0;
SELECT pg_reload_conf();
-- 3. 慢查询日志(配 6.1 的截断说明一起用):运行时开,事后 grep 日志拿参数化 SQL 全文
ALTER SYSTEM SET log_min_duration_statement = '2000';
SELECT pg_reload_conf();
这是「治标三件套」:
statement_timeout给查询封顶、max_parallel_workers_per_gather = 0防单条查询把多核连盘一起打满、慢日志留证据。治本仍要回到加索引 / 删旧数据 / 换列存------OLAP 分析查询别硬塞进 OLTP 库。
七、MySQL → PostgreSQL 高频踩坑
从 MySQL 转过来,这几个坑几乎人人踩一遍:
| 坑 | 真相 |
|---|---|
| 双引号当字符串用 | PG 里单引号才是字符串 ,"xxx" 是标识符 (列名 / 表名)。WHERE name = "abc" 会报「列 abc 不存在」 |
| 标识符大小写 | 未加引号的标识符 PG 一律转小写 ;"MyTable" 加引号才区分大小写,且以后都得带引号 |
SHOW CREATE TABLE 没有 |
用 \d 表名 看结构,或 pg_dump -t 表名 --schema-only 拿建表语句 |
| 自增 | 不是 AUTO_INCREMENT,用 SERIAL/BIGSERIAL 或 GENERATED ALWAYS AS IDENTITY |
| 字符串拼接 | 用 ` |
| 反引号 | PG 不认 MySQL 的 col,标识符用双引号 |
| 端口 / 库切换 | 默认端口 5432 (不是 3306);切库用 \c 而不是 USE |
LIMIT 分页 |
LIMIT n OFFSET m 一致,这个不用改 |
EXPLAIN ANALYZE 会真执行 |
MySQL 的 EXPLAIN 不执行;PG 的 EXPLAIN ANALYZE 真跑,写语句要 BEGIN..ROLLBACK 包起来 |
八、总结:一张随身速查卡
最后浓缩成一张「进 psql 先敲这几个」的卡片:
text
连接排障: \conninfo \l \c db \dt \d 表 \du \dn
显示优化: \x(竖排) \timing(耗时) \dtS(含系统对象)
跑一条 SQL:psql -d db -c "SQL" (Docker 里套 docker exec)
看连接: SELECT * FROM pg_stat_activity WHERE state<>'idle';
杀查询: SELECT pg_cancel_backend(pid); / pg_terminate_backend(pid)
批量杀: ...pg_terminate_backend(pid)... WHERE now()-query_start>'30s' AND query ILIKE 'SELECT%' AND pid<>pg_backend_pid()
抓锁: pg_blocking_pids(pid)
看大小: pg_size_pretty(pg_database_size / pg_total_relation_size)
抓慢SQL: pg_stat_statements ORDER BY total_exec_time DESC
看计划: EXPLAIN (ANALYZE, BUFFERS) SQL (写语句包 BEGIN..ROLLBACK)
建索引: CREATE INDEX CONCURRENTLY ... (大表不锁表)
调参兜底: ALTER SYSTEM SET xxx + SELECT pg_reload_conf();(免重启)
statement_timeout / max_parallel_workers_per_gather=0
查膨胀: SELECT n_dead_tup,last_autovacuum FROM pg_stat_user_tables
维护: VACUUM ANALYZE / VACUUM FULL(锁表慎用)
备份: pg_dump -Fc / pg_restore / \copy CSV
一句话记住 PostgreSQL 和 MySQL 最大的手感差异:
MySQL 的
SHOW / USE / DESC / KILL,在 PostgreSQL 里换成了「反斜杠元命令 + pg_ 系统视图 + 函数」。 暗号对上了,剩下的就是标准 SQL,转过来一点不难。
你从 MySQL 转 PostgreSQL,还踩过哪些坑?欢迎评论区补充。
延伸阅读
- PostgreSQL 官方:psql 命令参考 ------ 全部反斜杠元命令
- PostgreSQL 官方:pg_dump / pg_restore ------ 备份恢复
- PostgreSQL 官方:监控视图 pg_stat_activity ------ 会话 / 连接排查
- PostgreSQL 官方:pg_stat_statements ------ 慢 SQL 统计
- PostgreSQL 官方:EXPLAIN 与执行计划 ------ 读懂 Seq Scan / Index Scan / cost
- PostgreSQL 官方:VACUUM ------ 死元组回收与表膨胀
🏷️ 标签 :PostgreSQL psql 命令速查 数据库运维 SQL MySQL 对比