psql是 PostgreSQL 官方命令行客户端,也是最能体现 PG 风格的入口。它的**元命令(meta-commands)**系统比多数数据库的命令行工具都好用——\d家族看结构、\x看宽表、\gset存变量、\copy传数据。本文聚焦这个工具本身。SQL 语法见 SQL 命令使用教程,PG 数据库本身见 PostgreSQL 使用全面指南。
一、启动与连接
1.1 基本方式
psql # 用当前系统用户连本地默认库
psql -U app -d sales_db # 指定用户和库
psql -h db.example.com -p 5432 -U app -d sales_db1.2 参数速查
| 参数 | 含义 | 示例 |
|---|---|---|
-U | 用户名 | -U app |
-d | 数据库名 | -d sales_db |
-h | 主机(不写走 Unix socket) | -h db.example.com |
-p | 端口(默认 5432) | -p 6432 |
-W | 强制提示输入密码 | -W |
-c | 执行一条命令后退出 | -c "SELECT 1" |
-f | 执行 SQL 文件 | -f setup.sql |
-l | 列出所有数据库 | -l |
-v | 设置变量 | -v tbl=users |
-A | 无对齐输出(便于脚本解析) | -A |
-t | 只输出行,不带表头和边框 | -t |
--csv | 直接输出 CSV | --csv |
1.3 URI 连接串
psql "postgresql://app:secret@db.example.com:5432/sales_db?sslmode=require"URI 形式在脚本和 ORM 配置里很常见,附加参数(sslmode、connect_timeout)直接写在 query string 里。
1.4 免密登录:.pgpass
在命令行写密码既不方便也不安全。PG 的标准做法是 ~/.pgpass:
# 格式:hostname:port:database:username:password
cat > ~/.pgpass <<'EOF'
db.example.com:5432:sales_db:app:your_password
*:*:*:app:fallback_password
EOF
chmod 600 ~/.pgpass # 权限必须是 600,否则 psql 会忽略它之后 psql -h db.example.com -U app -d sales_db 就直接连上,无需输密码。* 是通配。
注意
chmod 600不是可选项。权限过宽时psql会静默忽略这个文件,你会以为是密码错了。
二、元命令:psql 的精华
元命令以 \ 开头,是 psql 客户端自己的指令,不是 SQL——不能写进 .sql 文件用 -f 执行,也不能通过驱动调用。
2.1 \d 家族(看结构)
| 命令 | 作用 |
|---|---|
\d | 列出所有表、视图、序列 |
\d table | 看某张表的详细结构(列、索引、约束、外键) |
\d+ table | 同上,附存储大小和描述 |
\dt | 只看表 |
\dv | 只看视图 |
\di | 只看索引 |
\ds | 只看序列 |
\df | 只看函数 |
\dn | 列出所有 schema |
\dS | 包含系统表 |
\dp table | 查看权限(等价 \z) |
\d products Table "public.products"
Column | Type | Collation | Nullable | Default
--------+---------------+-----------+----------+--------------------------------------
id | integer | | not null | nextval('products_id_seq'::regclass)
name | text | | |
price | numeric(10,2) | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (id)\d table 的输出把列定义、索引、外键一次给全,比 information_schema 查询快得多。
2.2 数据库与角色
\l -- 列出所有数据库(等价 \list)
\c dbname -- 切换数据库(等价 \connect)
\c dbname otheruser -- 切换数据库同时切用户
\du -- 列出所有角色(用户)
\du+ -- 附角色描述
\conninfo -- 显示当前连接信息\conninfoYou are connected to database "postgres" as user "postgres" via socket in
"/var/run/postgresql" at port "5432".2.3 帮助系统
\? -- 所有元命令的帮助
\h -- SQL 命令列表
\h CREATE TABLE -- 查看某条 SQL 的语法
\h CREATE INDEX\? 和 \h 是离线速查,比切浏览器查文档快。忘了某个选项就敲 \?。
三、输出与显示控制
3.1 \x:宽表救星
\x auto
SELECT * FROM big_wide_table;\x(expanded display)把结果从"一行多列"转成"一列多行":
-[ RECORD 1 ]---
id | 1
name | 张三
price | 9.99\x auto 是推荐设置——列多到放不下时自动切换扩展模式,列少时保持表格。\x 单独用是强制开启,再敲一次关掉。
3.2 \pset:格式化细节
\pset null '[NULL]' -- 空值显示为 [NULL](默认是空白,容易和空字符串混淆)
\pset border 2 -- 边框样式(0/1/2)
\pset format csv -- 输出格式(aligned/unaligned/csv/html/latex/json)
\pset pager off -- 关闭分页器(结果直接刷屏,配合 \o 写文件)
\pset expanded auto -- 等价 \x auto\pset null '[NULL]' 建议写进 .psqlrc——NULL 和空串在输出里长得一样,是排查数据问题的常见干扰。
3.3 \timing
\timing on
SELECT count(*) FROM big_table; count
--------
1000000
(1 row)
Time: 123.456 ms开了之后每条语句后面都会显示耗时,调试慢查询时第一件该做的事。
3.4 \watch:周期执行
SELECT count(*) FROM pg_stat_activity WHERE state = 'active';
\watch 2每 2 秒重跑一次上一条查询(Ctrl+C 退出)。监控连接数、队列长度这类实时指标很好用,比反复按上箭头敲回车优雅。
3.5 输出重定向 \o
\o /tmp/report.txt
SELECT * FROM sales_summary;
SELECT * FROM top_customers;
\o -- 不带参数表示恢复输出到终端\o 之后所有查询结果都写入文件,直到用不带参数的 \o 关闭。
四、查询缓冲与编辑
psql 维护一个"查询缓冲"(query buffer),你敲的内容先进缓冲,遇到 ; 才发送执行。
| 命令 | 作用 |
|---|---|
\p | 打印当前缓冲内容(检查有没有敲错) |
\e | 用 $EDITOR 打开编辑器编辑当前缓冲 |
\r | 清空缓冲 |
\g | 执行缓冲内容 |
\gx | 执行缓冲,并以扩展格式输出 |
\gset | 执行并把结果存成变量(见 §5.1) |
\g filename | 执行并写入文件 |
多行查询直接敲就行——psql 会一直等 ;:
SELECT
first_name,
last_name
FROM employees
WHERE hire_date > '2020-01-01'
; -- 分号结束并执行常见误解:不需要"单独一行分号来结束多行模式"——分号只是语句终止符,跟在查询末尾即可(上面写成单独一行只是为了排版)。另外
\set PROMPT1 ...是改提示符外观,和"多行模式"没有任何关系。
五、变量与脚本化
5.1 变量
\set tbl employees
SELECT count(*) FROM :tbl;
\set minimum 100
SELECT * FROM products WHERE price > :minimum;把查询结果存进变量(\gset)——做后续判断时很有用:
SELECT count(*) AS cnt FROM products \gset
\echo '产品数量: ' :cnt产品数量: 25.2 脚本化执行
psql -d sales_db -f setup.sql -v ON_ERROR_STOP=1-f file:执行文件-v ON_ERROR_STOP=1:遇错立即停止。默认 psql 会忽略错误继续执行——批量脚本里这往往是灾难,务必开启-v tbl=users:从命令行传变量进去
脚本里判断执行结果:
if psql -d sales_db -v ON_ERROR_STOP=1 -f migrate.sql; then
echo "迁移成功"
else
echo "迁移失败,退出码 $?"
exit 1
fipsql 的退出码:0 成功,1 致命错误(连不上等),2 会话正常结束但 ON_ERROR_STOP 触发过,3 脚本出错。
5.3 输出格式适合脚本
# 无对齐 + 无表头,便于 awk/cut 处理
psql -A -t -d sales_db -c "SELECT id, name FROM products"
# 直接输出 CSV
psql --csv -d sales_db -c "SELECT id, name FROM products" > products.csv六、导入导出
6.1 \copy 与 COPY 的区别(重要)
这是最容易混淆的一对:
COPY | \copy | |
|---|---|---|
| 类型 | SQL 命令 | psql 元命令 |
| 文件位置 | 服务器上 | 客户端(执行 psql 的机器) |
| 权限 | 需要超级用户或 pg_read_server_files | 用当前数据库用户权限即可 |
| 服务端可用 | ✅(任何 SQL 客户端) | ❌(只在 psql 里) |
-- \copy:文件在本地机器
\copy (SELECT * FROM users) TO '/tmp/users.csv' CSV HEADER
\copy users FROM '/tmp/users.csv' CSV HEADER-- COPY:文件在数据库服务器上
COPY employees FROM '/var/lib/postgresql/employees.csv' DELIMITER ',' CSV HEADER;日常用 \copy——你不一定能在数据库服务器上放文件,也不一定有超级用户权限。
6.2 pg_dump 备份
# 纯文本 SQL(可读,但恢复慢)
pg_dump -U app sales_db > sales_db.sql
# 自定义格式(推荐:压缩 + 支持并行恢复 + 选择性恢复)
pg_dump -U app -Fc sales_db > sales_db.dump
# 只导出结构 / 只导出数据
pg_dump -U app --schema-only sales_db > schema.sql
pg_dump -U app --data-only sales_db > data.sql
# 只导出某张表
pg_dump -U app -t products sales_db > products.sql
# 并行导出(大库提速,-Fd 目录格式才支持 -j)
pg_dump -U app -Fd -j 4 sales_db -f /backup/sales_db_dir6.3 恢复
# 纯文本:用 psql 执行
psql -U app -d sales_db -f sales_db.sql
# 自定义/目录格式:用 pg_restore
pg_restore -U app -d sales_db sales_db.dump
pg_restore -U app -d sales_db -j 4 /backup/sales_db_dir # 并行恢复
# 只恢复某张表
pg_restore -U app -d sales_db -t products sales_db.dump-Fc 自定义格式比纯 SQL 文本更值得用:体积更小、支持并行恢复、能选择性恢复单表,纯文本格式这三点都做不到。
七、.psqlrc 定制
把常用设置写进 ~/.psqlrc,每次启动自动生效:
-- 空值显示
\pset null '[NULL]'
-- 宽表自动扩展
\x auto
-- 显示执行时间
\timing on
-- 历史文件按数据库分开
\set HISTFILE ~/.psql_history-:DBNAME
\set HISTSIZE 5000
-- 出错即停(交互式下建议关掉,脚本里建议开)
\set ON_ERROR_STOP off
-- 常用查询的快捷方式(用 \set + :var)
\set active 'SELECT pid, state, query FROM pg_stat_activity WHERE state = $q$active$q$;'
$q$...$q$是 PG 的 dollar-quoting,可以避免字符串里的引号冲突。写长 SQL 进.psqlrc时很实用。
八、性能诊断
8.1 执行计划
EXPLAIN SELECT * FROM products WHERE price > 10;
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM products WHERE price > 10;(ANALYZE, BUFFERS) 是调优标配:ANALYZE 实际执行并给出各节点真实耗时和行数,BUFFERS 给出缓存命中情况(shared hit vs shared read——read 多说明要从磁盘读)。
看执行计划时重点找:
Seq Scan+ 大表:全表扫描,考虑加索引rows=预估与实际差很多:统计信息过期,跑ANALYZE tableNested Loop出现在大表上:可能是 JOIN 方式选错了shared read很高:缓存不足或索引没命中
8.2 当前活动
-- 正在运行的查询
SELECT pid, usename, state, wait_event_type, wait_event,
now() - query_start AS duration, left(query, 60) AS q
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;
-- 连接数统计
SELECT usename, state, count(*) FROM pg_stat_activity GROUP BY usename, state;杀掉一个卡住的查询:
SELECT pg_cancel_backend(12345); -- 温和取消(等价 Ctrl+C)
SELECT pg_terminate_backend(12345); -- 强制断开连接8.3 表与索引统计
-- 表的增删改查次数、顺序扫描次数
SELECT relname, seq_scan, idx_scan, n_live_tup, n_dead_tup
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;
-- 索引使用情况(idx_scan 为 0 的索引可能是多余的)
SELECT relname, indexrelname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes
ORDER BY idx_scan;seq_scan 高而 idx_scan 低,说明索引没被用上;n_dead_tup 高说明需要 VACUUM。
8.4 表空间占用
-- 单表大小
SELECT pg_size_pretty(pg_total_relation_size('products'));
-- 各表大小排行
SELECT relname,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;
-- 当前数据库大小
SELECT pg_size_pretty(pg_database_size(current_database()));九、维护操作
VACUUM products; -- 回收死元组空间(不锁表,可并发)
VACUUM ANALYZE products; -- 回收 + 更新统计信息
VACUUM FULL products; -- 彻底整理并归还空间给操作系统(⚠️ 会锁表!)
ANALYZE products; -- 只更新统计信息
REINDEX TABLE products; -- 重建索引(索引膨胀时用)
REINDEX TABLE CONCURRENTLY products; -- 并发重建,不阻塞写(更慢)
VACUUM FULL会持有排他锁,期间该表读写全部阻塞。大表上执行可能锁几分钟甚至更久。日常维护用普通VACUUM(或依赖 autovacuum)即可;确实需要回收磁盘空间时,考虑pg_repack扩展做在线整理。
十、故障排查
10.1 认证失败
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed:
FATAL: password authentication failed for user "app"排查顺序:
- 密码是否正确(注意
.pgpass是否被权限问题忽略了) pg_hba.conf是否允许该 host/database/user 组合,以及要求的认证方式(peer/md5/scram-sha-256)- 改完
pg_hba.conf要SELECT pg_reload_conf();或重启才生效
# 看当前的 hba 规则
psql -c "SHOW hba_file;"10.2 常见错误
| 报错 | 含义 | 处理 |
|---|---|---|
password authentication failed | 密码/认证配置问题 | 见上 |
no pg_hba.conf entry for host | 该主机未被允许 | 在 pg_hba.conf 加规则 |
FATAL: database "x" does not exist | 库名错 | \l 确认 |
permission denied for table x | 无权限 | GRANT 授权 |
relation "x" does not exist | 表不存在,或 search_path 没包含该 schema | \dt schema.* 确认 |
deadlock detected | 死锁,PG 已回滚一方 | 重试;检查加锁顺序 |
could not serialize access | 可串行化隔离下冲突 | 重试事务 |
too many connections | 连接数满 | 查 pg_stat_activity,考虑连接池 |
relation does not exist 里最常见的隐性原因是 search_path——表建在别的 schema 下,而 search_path 只有 "$user", public:
SHOW search_path;
SET search_path TO myschema, public;10.3 权限问题
-- 看某张表的权限
\dp products
-- 授权
GRANT SELECT, INSERT, UPDATE ON products TO app;
GRANT USAGE ON SCHEMA public TO app; -- 访问 schema 本身也要授权
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app; -- 自增序列要单独授PG 的权限模型里,
USAGE ON SCHEMA和序列权限是常被忘记的两项。只授表权限、不授 schema 和序列权限时,会出现"能 SELECT 但 INSERT 报 nextval 权限不足"的怪现象。
十一、实用技巧
Tab 补全:连表名、列名、schema 都能补全,比手敲快且不易错
上下箭头:浏览历史命令(历史存在
~/.psql_history)\!执行 shell 命令:\! ls -l /backups \! df -h注意:
\!后面跟的是系统命令,不是"历史行号"。想重跑历史里的某条语句,用上下箭头翻出来,或\e编辑。\e编辑长查询:\e会打开$EDITOR(默认 vi)编辑当前缓冲,保存退出即执行。写复杂 SQL 时比在终端里逐行敲舒服得多复制上一条语句改:输入
\e后可以基于缓冲修改,不用重敲
附录:常用元命令速查表
-- —— 连接与数据库 ——
\l / \list 列出数据库
\c dbname [user] 切换数据库(可换用户)
\conninfo 当前连接信息
\du [pattern] 列出角色
\dn 列出 schema
-- —— 看结构 ——
\d [table] 列出对象 / 看表结构
\d+ table 附大小和描述
\dt / \dv / \di / \ds / \df 表/视图/索引/序列/函数
\dp table 权限
\di+ 索引详情
-- —— 显示控制 ——
\x [auto] 扩展显示
\pset null '[NULL]' 空值显示
\pset border 2 边框
\timing [on|off] 显示耗时
\watch N 每 N 秒重跑
\o file / \o 输出到文件 / 恢复终端
-- —— 缓冲与编辑 ——
\p 打印缓冲 \e 编辑 \r 清空 \g 执行 \gx 扩展执行 \gset 存变量
-- —— 帮助 ——
\? 元命令帮助
\h [SQL] SQL 语法帮助
-- —— 其他 ——
\i file.sql 执行文件
\copy ... 客户端导入导出
\echo text 打印文本
\q 退出# —— 命令行 ——
psql -U app -d db 交互连接
psql -c "SELECT 1" 执行一条
psql -f file.sql -v ON_ERROR_STOP=1 执行文件(遇错即停)
psql -A -t -c "SQL" 无对齐输出,便于脚本
psql --csv -c "SQL" > out.csv 输出 CSV
# —— 备份恢复 ——
pg_dump -Fc db > db.dump 自定义格式(推荐)
pg_dump -Fd -j 4 db -f /backup/dir 并行导出
pg_restore -d db db.dump 恢复学习资源
- psql 官方文档 —— 元命令的权威列表
- PostgreSQL 官方教程
- PostgreSQL 速查表(官方 wiki) —— 实战片段
- pgAdmin —— 图形化管理工具
psql最值得记的就三组:看结构用\d家族,看宽表用\x auto,传数据用\copy。另外把ON_ERROR_STOP=1写进脚本——默认忽略错误继续跑,是批量运维里最危险的行为。
参考&致谢
系列教程
数据库系列
- SQL 命令使用教程:从入门到精通 —— SQL 标准命令,含 MySQL/PostgreSQL/SQLite 方言对照
- SQLite 使用全面教程:轻量级数据库的终极指南 —— 嵌入式数据库,类型系统、外键陷阱、WAL、FTS5
- MySQL 命令行使用全面教程:从入门到精通 ——
mysql客户端工具,批处理、备份、性能诊断 - MySQL 使用全面指南:从入门到高级实践 —— MySQL 安装配置、字符集、索引、事务锁、复制
- PostgreSQL 使用全面指南:从入门到企业级应用 —— PG 认证配置、JSONB、分区、备份与高可用
- PostgreSQL 命令行使用教程:掌握 psql 工具 ——
psql元命令、脚本化、\copy与pg_dump - PostgreSQL 实现原理深度剖析:高性能数据库引擎的核心机制 —— MVCC、WAL、B-tree 内核机制与可验证观察方法

