1. 项目概述
作为一名数据库管理员或开发者,经常需要查看PostgreSQL数据库中的用户列表。这看似简单的需求,实际上涉及PostgreSQL的权限系统、角色管理、元数据查询等多个核心概念。今天我将分享几种实用的方法,从基础查询到高级技巧,帮你全面掌握PostgreSQL用户管理的精髓。
PostgreSQL采用"角色(role)"的概念来管理数据库访问权限,每个登录用户本质上就是一个具有LOGIN权限的角色。理解这一点很重要,因为PostgreSQL的系统目录表中存储的是角色信息而非传统意义上的"用户"。我们将重点解析pg_catalog.pg_roles和pg_user这两个关键系统视图的区别与联系。
2. 基础查询方法
2.1 使用psql的\du命令
对于习惯命令行操作的用户,PostgreSQL自带的psql客户端提供了最快捷的方式:
bash复制psql -U postgres -c "\du"
这个命令会输出格式化的角色列表,包含:
- 角色名称
- 角色属性(超级用户、创建角色、创建数据库等)
- 所属组
- 描述信息
提示:如果想查看更详细的信息,可以使用\du+命令,它会额外显示角色描述和配置参数。
2.2 查询pg_roles系统视图
更灵活的方式是直接查询系统目录表:
sql复制SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolcanlogin
FROM pg_roles;
pg_roles包含所有角色(包括用户和组)的完整信息,关键字段说明:
- rolname:角色名称
- rolsuper:是否为超级用户
- rolcreaterole:能否创建新角色
- rolcreatedb:能否创建数据库
- rolcanlogin:能否作为用户登录
2.3 查询pg_user视图
PostgreSQL还提供了简化的pg_user视图:
sql复制SELECT * FROM pg_user;
这个视图实际上是pg_roles的子集,只包含可以登录的角色(rolcanlogin=true)。它排除了那些仅用于分组而不能直接登录的角色。
3. 高级查询技巧
3.1 筛选特定权限的用户
在实际管理中,我们经常需要查找具有特定权限的用户。例如,查找所有能创建数据库的用户:
sql复制SELECT rolname FROM pg_roles WHERE rolcreatedb = true;
或者查找所有超级用户:
sql复制SELECT rolname FROM pg_roles WHERE rolsuper = true;
3.2 查看用户属性详情
PostgreSQL角色可以设置多种属性,这些属性控制着角色的行为:
sql复制SELECT
rolname,
rolsuper AS is_superuser,
rolcreaterole AS can_create_roles,
rolcreatedb AS can_create_db,
rolcanlogin AS can_login,
rolreplication AS is_replication,
rolconnlimit AS connection_limit,
rolvaliduntil AS password_expires_at
FROM pg_roles;
3.3 查看用户成员关系
PostgreSQL支持角色继承,可以通过以下查询查看角色之间的成员关系:
sql复制SELECT
r.rolname AS role_name,
m.rolname AS member_name,
a.admin_option AS has_admin
FROM pg_auth_members am
JOIN pg_roles r ON am.roleid = r.oid
JOIN pg_roles m ON am.member = m.oid
ORDER BY r.rolname, m.rolname;
4. 元数据查询原理
4.1 PostgreSQL系统目录结构
PostgreSQL将所有数据库对象的元数据存储在系统目录表中。这些表位于pg_catalog模式下,包括:
- pg_authid:存储认证信息(密码等)
- pg_auth_members:角色成员关系
- pg_roles:角色信息的视图(基于pg_authid)
- pg_user:可登录角色的视图
4.2 pg_roles与pg_user的区别
虽然pg_user和pg_roles都提供用户信息,但它们有重要区别:
| 特性 | pg_roles | pg_user |
|---|---|---|
| 数据来源 | pg_authid和系统函数 | pg_roles的过滤视图 |
| 包含对象 | 所有角色(用户和组) | 仅可登录用户 |
| 字段数量 | 约30个字段 | 10个字段 |
| 密码信息 | 不包含 | 不包含 |
| 适用场景 | 需要完整角色信息时 | 仅需基本用户信息时 |
5. 安全与权限考量
5.1 查询权限要求
要查询用户列表,当前角色需要满足以下条件之一:
- 是超级用户
- 拥有pg_read_all_stats权限
- 是目标角色的成员
普通用户只能看到自己有权限查看的角色信息。
5.2 安全最佳实践
- 定期审计用户列表,特别是超级用户
- 限制具有创建角色权限的用户数量
- 为每个应用使用单独的角色,避免共享账号
- 设置合理的密码有效期(rolvaliduntil)
- 记录用户创建和修改操作
6. 常见问题排查
6.1 为什么看不到所有用户?
如果查询结果不完整,可能是由于:
- 当前用户权限不足
- 使用了pg_user视图(它只显示可登录用户)
- 连接到了错误的数据库集群
解决方案:
sql复制-- 确认当前用户
SELECT current_user;
-- 尝试以超级用户身份查询
SELECT rolname FROM pg_roles;
6.2 如何查看用户密码?
出于安全考虑,PostgreSQL不直接存储明文密码,也不允许查询密码哈希值。密码信息存储在pg_authid表中,但只有超级用户能查看加密后的密码。
6.3 用户显示存在但无法登录
可能原因:
- 角色没有LOGIN权限
sql复制ALTER ROLE username WITH LOGIN; - 密码已过期
sql复制ALTER ROLE username VALID UNTIL 'infinity'; - 连接数限制
sql复制ALTER ROLE username CONNECTION LIMIT -1; -- 无限制
7. 实用脚本分享
7.1 导出用户列表到CSV
sql复制COPY (
SELECT
rolname AS username,
rolsuper AS is_superuser,
rolcreaterole AS can_create_roles,
rolcreatedb AS can_create_db,
rolcanlogin AS can_login,
rolconnlimit AS connection_limit,
rolvaliduntil AS password_expires
FROM pg_roles
ORDER BY rolname
) TO '/tmp/postgres_users.csv' WITH CSV HEADER;
7.2 检查用户权限摘要
sql复制SELECT
r.rolname,
CASE WHEN r.rolsuper THEN '超级用户' ELSE '普通用户' END AS role_type,
CASE WHEN r.rolcanlogin THEN '可登录' ELSE '不可登录' END AS login_status,
r.rolconnlimit AS 连接限制,
r.rolvaliduntil AS 密码过期时间,
array_to_string(array(
SELECT b.rolname
FROM pg_catalog.pg_auth_members m
JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)
WHERE m.member = r.oid
), ', ') AS 所属角色组
FROM pg_catalog.pg_roles r
WHERE r.rolname NOT LIKE 'pg_%'
ORDER BY 1;
7.3 监控用户创建和修改
sql复制-- 创建事件触发器记录用户变更
CREATE OR REPLACE FUNCTION log_role_changes()
RETURNS event_trigger AS $$
DECLARE
obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
IF obj.command_tag = 'CREATE ROLE' OR obj.command_tag = 'ALTER ROLE' THEN
INSERT INTO audit.role_changes(event_time, role_name, operation, current_user)
VALUES (now(), obj.object_identity, obj.command_tag, current_user);
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER log_role_changes_trigger
ON ddl_command_end
WHEN TAG IN ('CREATE ROLE', 'ALTER ROLE')
EXECUTE FUNCTION log_role_changes();
8. 性能优化建议
当数据库中有大量角色时,查询用户列表可能会影响性能。以下是一些优化建议:
- 避免频繁查询完整角色列表,可以缓存结果
- 在大型系统中,考虑使用角色组(group roles)来简化管理
- 对pg_roles的查询可以添加条件过滤系统角色:
sql复制SELECT * FROM pg_roles WHERE rolname NOT LIKE 'pg_%'; - 在专用监控库中创建物化视图定期刷新:
sql复制CREATE MATERIALIZED VIEW mv_user_list AS SELECT rolname, rolsuper, rolcanlogin FROM pg_roles WHERE rolname NOT LIKE 'pg_%' WITH DATA; REFRESH MATERIALIZED VIEW mv_user_list;
9. 跨版本兼容性
不同PostgreSQL版本在用户管理方面有些许差异:
- PostgreSQL 8.1之前使用pg_shadow视图而非pg_user
- PostgreSQL 10引入了角色继承的显式管理
- PostgreSQL 14增加了pg_roles视图中的rolbypassrls字段(绕过行级安全)
编写脚本时应考虑版本差异:
sql复制-- 版本兼容的查询示例
SELECT
rolname,
rolsuper,
rolcanlogin,
CASE
WHEN current_setting('server_version_num')::int >= 140000
THEN rolbypassrls
ELSE NULL
END AS bypass_rls
FROM pg_roles;
10. 扩展知识:与其他数据库对比
了解PostgreSQL用户管理与其他数据库的区别有助于跨平台工作:
| 功能 | PostgreSQL | MySQL | Oracle |
|---|---|---|---|
| 用户概念 | 角色(role) | 用户(user) | 用户(user) |
| 组/角色 | 内置支持 | 8.0+支持角色 | 内置角色系统 |
| 继承 | 支持角色继承 | 不支持 | 有限支持 |
| 系统视图 | pg_roles, pg_user | mysql.user | DBA_USERS |
| 密码加密 | scram-sha-256等 | 多种加密方式 | 多种加密方式 |
在实际工作中,我发现PostgreSQL的角色系统比传统用户/组模型更灵活,特别是角色继承和权限委派功能,非常适合复杂的权限管理场景。例如,可以创建一个"分析师"角色,赋予只读权限,然后让所有分析师用户继承这个角色,这样权限变更只需修改一次基础角色即可。