在 PostgreSQL 中列出和切换数据库:完整技术指南
PostgreSQL 在单个服务器实例中管理多个隔离的数据库,每个数据库都有自己的 schema、角色和权限。要列出所有数据库,可在 psql 中运行 l,或从任意会话查询 SELECT datname FROM pg_catalog.pg_database;。要切换数据库,必须打开新连接——PostgreSQL 强制执行严格的会话与数据库绑定,没有等效的会话内 USE 命令。
本指南涵盖枚举和连接 PostgreSQL 数据库的所有可用方法,从原始 psql 命令和系统目录查询,到连接字符串、pg_hba.conf 注意事项,以及生产环境中使用的多数据库工作流模式。
为什么 PostgreSQL 数据库切换的工作方式不同
大多数来自 MySQL 的开发者期望有 USE database_name; 命令。PostgreSQL 故意省略了这一功能。每个 PostgreSQL 会话在连接时绑定到恰好一个数据库,且该绑定在会话的整个生命周期内不可更改。这是一个基于 PostgreSQL 进程模型的架构决策:后端进程(postgres)在启动时将数据库的系统目录加载到共享内存中,而在会话中途切换目录无论如何都需要完整的进程重启。
提前理解这一限制可以避免数小时的调试,并有助于规划多数据库工具、连接池和应用程序配置的架构。
列出 PostgreSQL 中的所有数据库
方法 1:psql 中的 l 元命令
枚举数据库最快的方式是在交互式 psql 会话中使用 l 元命令(别名:list)。
psql -U postgres连接后:
l这将生成类似如下的格式化表格:
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
myapp_db | appuser | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
analytics | analyst | UTF8 | en_US.UTF-8 | en_US.UTF-8 | analyst=CTc/analyst
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |这些列揭示的信息不仅仅是名称:Encoding 在服务器间迁移数据时很重要,Collate 影响排序顺序和索引行为,Access privileges 使用 PostgreSQL 的 ACL 表示法(C = CONNECT,T = TEMPORARY,c = CREATE)。
要获取包括表空间和连接限制在内的扩展详情,请使用:
l+方法 2:查询 pg_database 系统目录
对于脚本编写、监控或应用程序级内省,可直接查询 pg_catalog.pg_database 视图。由于系统目录是全局可见的,这在集群中的任何数据库中均可使用。
SELECT
datname AS database_name,
pg_catalog.pg_get_userbyid(datdba) AS owner,
pg_encoding_to_char(encoding) AS encoding,
datcollate AS collation,
datctype AS ctype,
datconnlimit AS connection_limit,
pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(datname)) AS size
FROM pg_catalog.pg_database
WHERE datistemplate = false
ORDER BY datname;过滤 datistemplate = false 可从结果中排除 template0 和 template1——这些是系统模板,而非运营数据库。datconnlimit 列在共享环境中至关重要:值为 -1 表示无限制,而任何正整数都会限制该数据库的并发连接数。
生产建议:在监控查询中添加 pg_database_size()。数据库悄然超出表空间容量是写入失败的常见原因,事后难以诊断。
方法 3:不进入 psql 即可列出数据库
对于 shell 脚本和自动化流水线,可以不进入交互式会话即可获取数据库列表:
psql -U postgres -c "l"或获取简洁、可供脚本解析的输出:
psql -U postgres -t -c "SELECT datname FROM pg_database WHERE datistemplate = false;"-t 标志会抑制列标题和行计数,仅返回原始值——非常适合通过管道传入 grep、awk 或 Bash 数组。
将列表导出到文件:
psql -U postgres -t -A -c "SELECT datname FROM pg_database WHERE datistemplate = false;" > db_list.txt
-A 禁用列对齐,每行输出一个数据库名称。
在 PostgreSQL 中切换数据库
由于无法在活动会话中切换数据库,正确的做法是终止当前连接并建立一个指向目标数据库的新连接。有几种高效的方式可以实现这一点。
方法 1:退出并从 shell 重新连接
在 psql 中,退出当前会话:
q
然后连接到目标数据库:
psql -U postgres -d target_database
方法 2:在 psql 中使用 c(连接)
这是交互式工作中最实用的方法。c 元命令会关闭当前连接并打开一个到指定数据库的新连接——所有操作都在同一终端会话中完成。
c target_database
也可以同时切换用户和主机:
c target_database admin_user localhost 5432
语法:c [database [username [host [port]]]]运行 c 时,psql 将显示确认信息:
You are now connected to database "target_database" as user "postgres".重要边界情况:如果目标数据库不存在或当前用户缺少 CONNECT 权限,c 将失败并返回到之前的连接。这比听起来更安全——您不会失去连接,但在脚本中必须通过检查退出状态来处理这种情况。
方法 3:以不同用户身份连接
以特定角色连接到数据库:
psql -d myapp_db -U appuser -h localhost -p 5432或在现有会话中使用 c 简写:
c myapp_db appuser这在测试行级安全(RLS)策略或验证应用程序用户无法访问其 schema 之外的表时特别有用。
方法 4:使用连接字符串(URI 格式)
PostgreSQL 支持 libpq 连接 URI 格式,将所有连接参数封装到单个字符串中。这是应用程序配置、CI/CD 流水线和基础设施即代码工具的首选方法。
psql "postgresql://appuser:password@localhost:5432/myapp_db"或使用 postgres:// 方案(两者均有效):
psql "postgres://appuser:password@db.example.com:5432/analytics?sslmode=require"?sslmode=require 参数强制对连接进行 TLS 加密——这是任何暴露在 localhost 之外的数据库的必要要求。如果您在 VPS 或独立服务器上托管 PostgreSQL,请始终将连接字符串与 sslmode=require 或 sslmode=verify-full 以及有效的 SSL 证书配合使用。
值得注意的连接 URI 参数:
| 参数 | 用途 | 示例值 |
|---|---|---|
sslmode | TLS 强制级别 | require、verify-full |
connect_timeout | 连接失败前的等待秒数 | 10 |
application_name | 在 pg_stat_activity 中标识客户端 | myapp_worker |
options | 传递服务器端 GUC 参数 | -c search_path=myschema |
方法 5:将 psql 与环境变量配合使用
对于频繁连接同一集群的情况,可设置环境变量以避免重复输入凭据:
export PGHOST=localhost
export PGPORT=5432
export PGUSER=postgres
export PGPASSWORD=secret # Use .pgpass file in production instead
export PGDATABASE=myapp_db
psql在生产环境中,使用 .pgpass 文件代替 PGPASSWORD,以避免在 shell 历史记录或进程列表中暴露凭据:
# ~/.pgpass format: hostname:port:database:username:password
localhost:5432:myapp_db:appuser:strongpassword正确设置权限,否则 PostgreSQL 将忽略该文件:
chmod 600 ~/.pgpass对比:数据库切换方法
| 方法 | 使用场景 | 需要新进程 | 支持切换用户 | 可脚本化 |
|---|---|---|---|---|
q + psql -d | Shell | 是 | 是 | 是 |
c dbname | psql 交互式 | 否(由 psql 处理) | 是 | 有限 |
| 连接 URI | Shell / 应用程序 | 是 | 是 | 是 |
| 环境变量 | Shell | 是 | 是 | 是 |
| pgAdmin GUI | GUI 客户端 | 否 | 是 | 否 |
| 连接池(PgBouncer) | 应用程序 | 否 | 取决于模式 | 是 |
高效管理多个数据库连接
使用 pgAdmin 进行基于 GUI 的导航
pgAdmin 在左侧对象树中列出每个已注册服务器下的所有数据库。点击某个数据库并打开查询工具,所有查询将自动限定在该数据库范围内。这对于探索性工作很有用,但不适合自动化。
注意事项:pgAdmin 为每个数据库维护独立的连接槽。如果您的 PostgreSQL 服务器的 max_connections 设置较低(默认为 100),在 pgAdmin 中打开多个数据库可能会在应用程序启动之前就耗尽连接池。
使用 PgBouncer 进行连接池管理
在频繁切换数据库的生产环境中,像 PgBouncer 这样的连接池工具可以显著降低开销。PgBouncer 以三种模式运行:
- 会话模式:每个客户端会话对应一个服务器连接。功能上等同于直接连接。
- 事务模式:服务器连接仅在事务期间保持。对 OLTP 工作负载最为高效。
- 语句模式:每条语句执行后归还连接。与多语句事务不兼容。
在单个 PostgreSQL 实例上运行多个应用程序数据库时——这在 VPS 托管或带 cPanel 的 VPS 上是常见模式——事务模式下的 PgBouncer 可以将活跃后端进程数量减少一个数量级。
使用 dblink 和 postgres_fdw 进行跨数据库查询
由于会话是数据库范围的,跨数据库查询数据需要使用扩展。PostgreSQL 提供两种选项:
dblink——较旧的过程式方法:
SELECT * FROM dblink(
'dbname=analytics user=analyst host=localhost',
'SELECT user_id, event_count FROM events WHERE date = current_date'
) AS remote(user_id INT, event_count BIGINT);postgres_fdw——现代、符合标准的外部数据包装器:
CREATE EXTENSION postgres_fdw;
CREATE SERVER analytics_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', dbname 'analytics', port '5432');
CREATE USER MAPPING FOR appuser
SERVER analytics_server
OPTIONS (user 'analyst', password 'secret');
CREATE FOREIGN TABLE remote_events (
user_id INT,
event_count BIGINT
)
SERVER analytics_server
OPTIONS (schema_name 'public', table_name 'events');设置完成后,remote_events 的行为类似于本地表。postgres_fdw 支持谓词下推,这意味着 WHERE 子句在远程服务器上执行,而非在本地执行——这对于大型数据集来说是关键的性能区别。
系统数据库:不可触碰的内容
每个全新的 PostgreSQL 集群都包含四个数据库:
| 数据库 | 用途 | 可以连接? | 可以修改? |
|---|---|---|---|
postgres | 默认管理数据库 | 是 | 谨慎操作 |
template1 | CREATE DATABASE 的模板 | 是 | 是,更改会传播 |
template0 | 干净的基线模板 | 极少 | 否 |
pg_catalog | 不是数据库,而是 schema | 不适用 | 绝不 |
每当您运行 CREATE DATABASE 而不指定模板时,都会克隆 template1。如果您在 template1 中安装扩展或创建 schema,每个新数据库都会继承它们。这对于标准化环境很有用,但若意外操作则很危险。
template0 作为原始备用模板存在。它是在使用不同编码或区域设置恢复 pg_dump 归档时唯一可用的模板,因为它没有可能产生冲突的用户创建对象。
权限、pg_hba.conf 与连接失败
切换数据库时常见的混淆来源是 PostgreSQL 角色级权限与 pg_hba.conf 身份验证规则之间的区别。两者必须独立允许连接。
角色级检查:角色必须对目标数据库拥有 CONNECT 权限:
GRANT CONNECT ON DATABASE target_database TO appuser;pg_hba.conf 检查:基于主机的身份验证文件(Debian/Ubuntu 上为 /etc/postgresql/15/main/pg_hba.conf)必须有与用户、数据库和源地址匹配的规则。典型条目如下:
# TYPE DATABASE USER ADDRESS METHOD
host myapp_db appuser 10.0.0.0/8 scram-sha-256编辑 pg_hba.conf 后,无需重启服务器即可重新加载配置:
sudo systemctl reload postgresql或在 psql 中执行:
SELECT pg_reload_conf();常见失败模式:用户在 SQL 级别拥有 CONNECT 权限,但 pg_hba.conf 中没有匹配规则。错误消息(FATAL: no pg_hba.conf entry for host)很明确,但开发者往往完全忽略该文件,因为他们期望数据库权限完全通过 SQL 管理。
实用决策矩阵
使用此清单为您的场景选择正确的连接方式:
- 在本地开发机器上进行交互式探索:在
psql中使用c dbname。快速,无需新进程。 - 遍历多个数据库的 shell 脚本:在循环中使用
psql -U postgres -d $dbname -c "...",配合-t -A获取简洁输出。 - 连接到单个数据库的应用程序:使用带
sslmode=require的连接 URI 和连接池(PgBouncer 或内置驱动程序池)。 - 需要来自两个数据库数据的应用程序:在主数据库上实现
postgres_fdw,而不是在应用程序代码中管理两个独立的连接池。 - 验证 RLS 或权限隔离:使用
c dbname role_name模拟目标角色,无需离开psql。 - 自动化配置 / 基础设施即代码:使用环境变量或带服务账户的
.pgpass;切勿在脚本中硬编码凭据。 - 高并发生产工作负载:在应用程序和 PostgreSQL 之间以事务模式部署 PgBouncer。在独立服务器上,根据硬件内存容量调整
postgresql.conf中的max_connections(每个后端大约使用 5–10 MB RAM)。 - 每租户独立数据库的多租户 SaaS:考虑在单个数据库内使用基于 schema 的多租户,而非每租户独立数据库,以避免连接池碎片化并简化备份策略。
对于将 PostgreSQL 与 Web 应用程序配合运行的团队,将数据库服务器与正确配置的共享托管或 VPS 环境以及应用程序层的已注册域名配合使用,即可构成标准生产技术栈。
常见问题
我可以在不关闭 psql 会话的情况下切换数据库吗?
可以。在 psql 中使用 c target_database 元命令。它会关闭当前后端连接并打开一个到指定数据库的新连接,所有操作都在同一终端会话中完成。您还可以在同一命令中指定不同的用户、主机和端口。
为什么 PostgreSQL 没有像 MySQL 那样的 USE 命令?
PostgreSQL 的架构在启动时将后端进程绑定到单个数据库。该数据库的系统目录被加载到该进程的共享内存中,而在会话中途切换目录在架构上等同于启动一个新进程。psql 中的 c 命令是实际等效方案——它只是让进程重启对用户透明。
如何同时查询两个不同 PostgreSQL 数据库中的数据?
使用 postgres_fdw 扩展创建外部服务器和映射到远程数据库的外部表。设置完成后,您可以在单个查询中 JOIN 本地表和远程表。对于一次性查询,dblink 更简单,但性能较差且难以维护。
如果我连接到 template1 并修改它会发生什么?
您在 template1 中创建的任何对象——表、扩展、schema——都将被克隆到使用 CREATE DATABASE 创建的每个新数据库中(除非明确指定了 TEMPLATE template0)。这有时是有意为之(例如,预安装 uuid-ossp 或 pgcrypto),但意外修改可能会损坏所有后续创建的数据库。
如何查找当前 psql 会话连接的是哪个数据库?
在 psql 中运行以下命令:
SELECT current_database();或查看 psql 提示符本身——默认情况下它显示 dbname=#(超级用户)或 dbname=>(普通用户),其中 dbname 是当前活动数据库。
