mysql 8.0.1起原生支持角色管理,5.7及更早版本无真角色;应显式创建角色并授权具体库表,避免通配符;分配角色后需设默认角色或会话激活,权限才生效。

MySQL 8.0+ 才真正支持 Role 角色管理
MySQL 5.7 及更早版本没有原生 ROLE 概念,所谓“角色”只能靠手动维护用户+权限组合,本质是伪角色。真角色(CREATE ROLE、GRANT role_name TO user)从 MySQL 8.0.1 开始稳定支持,且默认启用 role_support。如果你用的是 5.7 或 MariaDB,请直接跳过角色方案,改用细粒度 GRANT 配合用户分组管理。
创建读写分离角色的最小安全操作集
不要给角色赋全局权限(如 GRANT ALL ON *.*),也不要直接对库名用通配符(如 `app_%`)——MySQL 不支持模式匹配的数据库级授权。正确做法是显式声明每个业务库:
CREATE ROLE 'app_reader', 'app_writer';GRANT SELECT ON `order_db`.* TO 'app_reader';GRANT SELECT, INSERT, UPDATE, DELETE ON `order_db`.* TO 'app_writer';-
GRANT SELECT ON `user_db`.* TO 'app_reader';(只读库可复用同一角色) -
FLUSH PRIVILEGES;(仅在直接修改 mysql.sys 表后才需;用 GRANT 语句无需此步)
注意:INSERT 和 UPDATE 权限不隐含 SELECT,某些 ORM(如 Django)执行 UPDATE ... RETURNING 类操作时会失败,必须显式补上 SELECT。
把角色分配给用户时的两个关键约束
角色不是自动激活的。用户登录后,默认不持有任何被授予的角色,必须显式 SET ROLE 或配置默认角色,否则权限不生效:
GRANT 'app_reader' TO 'web_app'@'10.20.%.%';-
SET DEFAULT ROLE 'app_reader' TO 'web_app'@'10.20.%.%';(推荐,避免应用层每次连接都调SET ROLE) - 若用户已有其他权限(比如旧版遗留的
GRANT SELECT ON *.*),角色权限会与之叠加——可能意外扩大权限,建议先REVOKE冗余权限 - 角色不能嵌套:MySQL 不支持
GRANT role_a TO role_b,只能授给用户
权限变更后连接不生效?检查 active roles 状态
常见现象:改完角色权限,应用连上去还是报 ERROR 1142 (42000): SELECT command denied。原因往往是连接复用或角色未激活:
- 在当前会话中运行
SELECT CURRENT_ROLE();,确认返回值是否为预期角色(如'app_reader') - 如果返回
NULL,说明该会话没激活角色,手动执行SET ROLE 'app_reader';测试 - 应用使用连接池(如 HikariCP)时,需确保连接初始化 SQL 包含
SET ROLE,或服务端已设好DEFAULT ROLE - MySQL 8.0.16+ 支持
SET PERSIST持久化activate_all_roles_on_login=ON,但生产环境慎用——它会让所有用户自动激活全部被授予角色,破坏最小权限原则
角色权限的生效链条比传统用户权限多一环:用户 → 被授角色 → 角色所拥有的权限 → 当前会话激活状态。漏掉任一环,读写控制就失效。











