MySQL 8.0 角色机制是唯一安全支撑只读、只写、管理员三类角色分级授权的方案,必须显式创建角色、精确授予权限、禁用高危权限并显式激活,默认不生效。

只读、只写、管理员三类角色不能靠“直觉命名”或笼统授权,必须严格按权限边界拆分,否则容易出现权限越界或功能缺失。MySQL 8.0 角色机制是唯一能安全支撑这种分级的方案,低于 8.0 的版本不支持角色,只能硬编码用户级授权,维护成本高且易出错。
CREATE ROLE 必须先执行,且不能跳过
MySQL 8.0 要求角色必须显式创建,不能在 GRANT 时隐式生成。未创建就直接 GRANT 'ro_role' TO 'user'@'%' 会报错 ERROR 3530 (HY000): Role does not exist。
- 创建角色语法必须带引号:
CREATE ROLE 'ro_role', 'rw_role', 'admin_role'; -
IF NOT EXISTS是强推荐项,避免脚本重复执行失败 - 角色名区分大小写,且不能与已有用户名冲突(即使 host 不同)
- 角色本身不带密码、不登录,只是权限容器,后续才绑定到用户
只读角色只给 SELECT,但必须限制作用域
只读不是“不给写权限”就完事,关键是防止误触系统库、跨库查询或临时表写入。全局 SELECT ON *.* 看似省事,实际等于把 mysql、information_schema 全部暴露给只读账号,存在敏感信息泄露风险。
- 精确到库:
GRANT SELECT ON `sales_db`.* TO 'ro_role'; - 精确到表(更安全):
GRANT SELECT ON `sales_db`.`orders` TO 'ro_role'; - 禁止隐式写行为:确保不授予
LOCK TABLES、CREATE TEMPORARY TABLES,否则用户可通过建临时表+INSERT绕过只读限制 - 验证是否干净:
SHOW GRANTS FOR 'ro_role';输出里只应有SELECT,无INSERT、UPDATE、DELETE、SUPER、REPLICATION CLIENT等任何其他权限
只写角色要禁用 SELECT,但需保留必要元数据访问
只写角色常见于日志采集、ETL 写入等场景,目标是“能插不能查”。但完全剥夺 SELECT 会导致部分操作失败——比如 INSERT ... ON DUPLICATE KEY UPDATE 需要先查主键是否存在,REPLACE INTO 也依赖隐式 SELECT。
- 基础写权限:
GRANT INSERT, UPDATE, DELETE ON `log_db`.* TO 'rw_role'; - 允许最小元数据访问:
GRANT SELECT (id) ON `log_db`.`events` TO 'rw_role';(仅查主键字段,用于冲突判断) - 显式回收危险权限:
REVOKE SELECT ON *.* FROM 'rw_role';(撤回之前可能继承的全局 SELECT) - 禁止结构变更:
REVOKE CREATE, DROP, ALTER, INDEX ON *.* FROM 'rw_role';,否则可能意外删表或加索引影响性能
管理员角色别直接给 ALL PRIVILEGES
ALL PRIVILEGES 包含 GRANT OPTION 和 SUPER,一旦赋予,该角色用户就能给自己加 SUPER、关 read_only、甚至删 root 账号。生产环境必须拆解授权。
- 核心管理权限:
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, CREATE VIEW, SHOW VIEW ON *.* TO 'admin_role'; - 显式排除高危项:
REVOKE SUPER, REPLICATION CLIENT, REPLICATION SLAVE, FILE, SHUTDOWN ON *.* FROM 'admin_role'; - 如需赋权能力,单独给
GRANT OPTION:GRANT GRANT OPTION ON `app_db`.* TO 'admin_role';(限定在业务库,不给全局) - 角色激活后默认不生效,用户登录后需执行
SET DEFAULT ROLE ALL TO 'admin_user'@'%';才能使用
角色设计最易被忽略的点是权限继承链和默认激活状态:角色创建后不会自动绑定到用户,GRANT 'role' TO 'user' 只是建立关联;用户登录后角色默认不启用,必须显式 SET DEFAULT ROLE 或每次 SET ROLE,否则权限不生效。这点在自动化脚本和连接池配置中尤其容易漏掉。


















