mysql 8.0.16+ 全局授权需 system_user 权限且 partial_revokes=off;pg 无真正全库只读,须逐 schema 授权表/视图/序列并设 default privileges;sql server db_datareader 不跨库、不含系统视图权限;三者权限变更后均需重连生效。
mysql 8.0+ 中用 grant select on *.* 会报错
直接执行 grant select on *.* to 'user'@'%' 在 mysql 8.0.16+ 默认会失败,报错 error 3790 (hy000): syntax error or access rule violation —— 这是因为启用了 require_row_format 或更常见的是开启了角色权限模型后,全局对象授权被限制。实际生效的前提是:用户必须有 system_user 权限(或 root),且未启用 partial_revokes 等严格策略。
- 先确认是否允许全局授权:
SELECT @@global.partial_revokes;,返回OFF才能继续 - 检查当前账户权限:
SHOW GRANTS FOR CURRENT_USER;,确保含SYSTEM_USER或是root - 如果启用了
partial_revokes,得先用REVOKE清掉冲突的细粒度拒绝规则
PostgreSQL 中没有真正的“全库只读”,得靠 DEFAULT PRIVILEGES + 循环授权
PG 不支持 GRANT SELECT ON DATABASES *,所谓“全局只读”本质是给每个现有 schema 的所有表、视图、序列加 SELECT,再设默认权限让后续新建对象自动继承。漏掉 pg_catalog 或 information_schema 就会导致 psql 连接后 \dt 报错或看不到表名。
- 对已有对象批量授权:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reader; - 设置默认权限(影响后续新建对象):
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO reader; - 别忘了视图和序列:
GRANT SELECT ON ALL VIEWS IN SCHEMA public TO reader;和GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO reader; - 若业务用多个 schema(如
sales,log),每 schema 都要单独跑一遍,不能省
SQL Server 用 db_datareader 角色不等于全库只读
把用户加到 db_datareader 只对其所在数据库生效,且该角色无法跨库——哪怕你显式 USE other_db; GRANT ...,也不会自动同步。更隐蔽的问题是:它不包含对系统视图(如 sys.tables)的访问权,应用连 SELECT TOP 1 * FROM sys.tables 都会报 SELECT permission denied。
- 单库只读:直接
EXEC sp_addrolemember 'db_datareader', 'reader'; - 多库只读:必须在每个目标库中分别执行角色添加,或改用服务器级权限
VIEW ANY DATABASE+ 库内db_datareader - 查系统元数据需额外授权:
GRANT VIEW DEFINITION TO reader;或更精准地GRANT SELECT ON SCHEMA::sys TO reader; - 注意
db_datareader允许读取加密列(如果没设密钥权限),实际并非“安全只读”
权限变更后连接不生效?重点检查 FLUSH PRIVILEGES 的适用范围
MySQL 需要 FLUSH PRIVILEGES 仅当你是手动修改了 mysql.user 表;用 GRANT 语句授权后完全不需要刷——刷了反而可能因权限缓存错乱导致新权限延迟 1 分钟以上才生效。而 PostgreSQL 和 SQL Server 根本没有这个命令,强制刷新只会报错。
- MySQL:用
GRANT就立刻生效,FLUSH PRIVILEGES是冗余操作 - PostgreSQL:权限变更立即可见,但客户端连接复用旧 session 时需重连
- SQL Server:部分权限(如服务器角色)需重新登录,库级角色变更有时需等几秒缓存更新
- 所有数据库里,如果用户已建立长连接,新权限不会热加载,必须断开重连才能验证
权限不是配完就完事的那层纸,schema 增减、角色嵌套、密码过期策略、甚至客户端驱动版本都可能让 SELECT 突然失败。最常被跳过的一步是:没在测试库走一遍真实查询路径,只验证了 SELECT 1。










