GRANT语句在线执行但有MDL锁风险,权限变更对新连接生效,角色管理更安全可控,需注意host匹配、密码插件兼容性及CURRENT_USER()与USER()差异。

GRANT 语句本身是在线操作,但有隐式锁风险
MySQL 的 GRANT 和 REVOKE 命令在大多数版本(5.7+、8.0)中不阻塞查询,但会短暂获取 mysql 系统库的元数据锁(MDL)。如果此时恰好有长事务正在读取 mysql.user 或执行 FLUSH PRIVILEGES,GRANT 可能卡住,连带阻塞后续新连接认证——这不是权限生效慢,而是锁等待。
- 执行前用
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'mysql';检查是否有未释放的 MDL - 避免在业务高峰执行,尤其不要和
FLUSH PRIVILEGES同时操作(8.0+ 已不需要该命令) - 权限变更后,已有连接不会立即生效;新连接或执行
FLUSH PRIVILEGES(仅 5.7 及更早需)才加载,但注意:该命令会重载整个权限表,触发全局读锁
MySQL 8.0 中角色(ROLE)赋权比直接 GRANT 更可控
直接对用户 GRANT 权限,一旦误操作(比如 GRANT ALL ON *.*),撤回成本高且易漏。而用角色封装权限集,再把角色赋予用户,变更时只需调整角色定义或成员关系,不影响在线连接行为。
- 创建角色:
CREATE ROLE 'app_reader'; - 赋予角色权限:
GRANT SELECT ON app_db.* TO 'app_reader'; - 赋予用户角色:
GRANT 'app_reader' TO 'app_user'@'%'; - 激活角色(会话级):
SET ROLE 'app_reader';,或设为默认:SET DEFAULT ROLE 'app_reader' TO 'app_user'@'%'; - 注意:角色功能在 8.0.2+ 才稳定支持,默认不启用;需确认
activate_all_roles_on_login=ON或显式SET DEFAULT ROLE
权限变更后连接仍报 Access denied?检查 host 匹配和密码插件
常见现象不是权限没生效,而是客户端实际连接用的用户身份与预期不符。MySQL 认证基于 'user'@'host' 全匹配,'user'@'%' 和 'user'@'192.168.1.%' 是两个不同账户;另外 8.0 默认密码插件从 mysql_native_password 改为 caching_sha2_password,老客户端可能无法握手成功,导致误判为权限问题。
- 查当前匹配用户:
SELECT USER(), CURRENT_USER();—— 前者是客户端声明的,后者是服务端认证后的实际账户 - 确认 host 字段是否精确匹配,特别是使用 DNS 名称时,MySQL 默认跳过 DNS 解析(
skip_name_resolve=ON),只认 IP - 若需兼容旧客户端,建用户时显式指定:
CREATE USER 'u'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd';
ALTER USER 修改密码或属性时,注意并发连接中断风险
ALTER USER 修改密码、失效时间、资源限制等属性,在 8.0 中基本是原子操作,但有一个关键例外:修改 max_connections 或 max_user_connections 会立即影响所有该用户的新连接,而已存在的连接不受影响。真正危险的是改密码同时触发认证失败——比如密码强度策略不满足,或新密码被 validate_password 插件拒绝,此时命令报错,但部分属性可能已写入,状态不一致。
- 执行前先验证密码合规性:
SELECT VALIDATE_PASSWORD_STRENGTH('newpass');(需插件启用) - 避免在一条
ALTER USER里混合改密码 + 属性 + 资源限制,拆成多条,便于定位失败点 - 修改
password_expired为YES后,用户首次登录必须改密,这会中断自动化脚本,除非脚本支持交互式改密流程
权限变更看着只是几条 SQL,但背后牵扯认证链、MDL 锁、密码插件兼容性和 host 匹配逻辑。最容易被忽略的是 CURRENT_USER() 和 USER() 的差异,以及 8.0 角色默认不自动激活这个细节。


















