grant权限必须严格按层级匹配:on .为全局权限,on mydb.*仅限该库所有表,反引号包裹含特殊字符库名,host需精确匹配且须flush privileges生效。

GRANT 不是“一键赋权”,而是按层级叠加生效;错一层,权限就失控——比如 ON *.* 和 ON mydb.* 看似只差几个字符,实际权限范围天壤之别。
权限作用域写错:ON 后面的 *.* 和 mydb.* 怎么选?
MySQL 权限严格按对象层级生效,ON 后面的语法直接决定你能管多大范围:
-
ON *.*是全局权限:能查information_schema、mysql等系统库,生产环境基本禁用 -
ON mydb.*是数据库级:只对mydb下所有表有效,跨库查询会报ERROR 1142 (42000): SELECT command denied - 数据库名含短横线(如
my-app)必须用反引号:ON `my-app`.*,否则语法错误 - 表级授权(如
ON mydb.users)不能绕过库级限制——如果用户没mydb库的 USAGE 权限,连USE mydb都失败
用户 host 匹配失败:“Access denied” 却刚执行过 GRANT
MySQL 认证靠完整标识符 'user'@'host',三个地方极易不一致:
- IP 段写法不等价:
'app'@'10.0.1.%'≠'app'@'10.0.1.5',哪怕后者在前者网段内 -
'app'@'%'不匹配localhost:MySQL 对本地连接优先走 socket 认证,'app'@'localhost'必须单独授 - Linux 下用户名大小写敏感:
'App'@'%'和'app'@'%'是两个用户,SHOW GRANTS FOR 'app'@'%'查不到前者 - MySQL 8.0+ 默认用
caching_sha2_password插件,老客户端(如 PHP 7.4 + mysqlnd)可能握手失败,报Access denied——不是权限问题,是认证协议不兼容
权限没生效:为什么 GRANT 成功了但应用连不上?
GRANT 只写入权限表,不自动刷新运行时缓存,尤其以下情况必须手动刷:
- MySQL 5.7 及更早版本:必须跟
FLUSH PRIVILEGES,否则新连接也拿不到权限 - MySQL 8.0+ 多数情况自动刷新,但跨主机授权、或修改了
mysql.user表以外的权限表(如mysql.db)时,仍建议执行FLUSH PRIVILEGES - 已存在的连接不会更新权限:用户得断开重连,
SHOW GRANTS FOR CURRENT_USER在旧会话里永远显示旧权限 - 检查 bind_address:若配置为
127.0.0.1,远程 IP 根本连不进 MySQL,GRANT再全也没用
权限追加与覆盖:REVOKE 漏写 ON 子句会留后门
GRANT 是追加,不是覆盖;REVOKE 也是精确移除,不是清空:
- 先执行
GRANT INSERT, DELETE ON mydb.* TO 'u'@'%',再执行REVOKE DELETE ON mydb.t1——库级DELETE依然有效,u还能删其他表 - 要彻底去掉库级权限,必须写全:
REVOKE DELETE ON mydb.* FROM 'u'@'%' - 没有
DENY语句,低层级授权不能抵消高层级权限;比如给了SELECT ON *.*,再给REVOKE SELECT ON mydb.*无效 - MySQL 8.0+ 推荐用角色(
CREATE ROLE+GRANT ... TO role_name),避免直接对用户反复 GRANT/REVOKE
最常被忽略的是 host 匹配细节和权限层级叠加逻辑——一个 @'%' 写错位置,或漏掉 FLUSH PRIVILEGES,后面所有调试都白费。权限不是“设完就跑”,得像查 bug 一样逐层验证。











