执行select user(), current_user()确认实际认证身份,再用show grants for current_user()查真实权限;注意host匹配、大小写敏感、权限层级与deny优先级、角色激活及连接复用影响。

查当前用户和权限范围
连接 mysql 后第一件事不是猜错哪了,而是确认你用的是谁、连的是哪个实例、有没有被 host 限制。执行:
SELECT USER(), CURRENT_USER();
前者返回客户端声明的用户名+host,后者才是 MySQL 实际认证通过的身份(关键!)。如果两者不一致,说明存在 proxy 用户或 host 匹配规则问题。
接着查权限:
SHOW GRANTS FOR CURRENT_USER();
注意别写成 SHOW GRANTS FOR 'user'@'host'——你可能根本没权限查别人,而且 CURRENT_USER() 才反映真实生效权限。
常见陷阱:
- 用户创建时用了
'user'@'192.168.%',但你是从10.0.0.5连的,结果匹配到'user'@'%'——而这个账号可能压根没授任何权限 - MySQL 8.0+ 默认启用
sql_mode=STRICT_TRANS_tableS,某些低权限操作(比如 INSERT 忽略字段)会直接报错,看起来像权限问题,其实是模式限制
INSERT/UPDATE/delete 报错 Error 1142
典型错误信息是 ERROR 1142 (42000): INSERT command denied to user 'xxx'@'yyy' for table 'zzz'。这表示权限检查失败,但未必是没给 INSERT 权限——也可能是表不存在、数据库名拼错、或者用了反引号但大小写不匹配(尤其在 linux 系统上,lower_case_table_names=0 时 `MyTable` ≠ `mytable`)。
实操建议:
- 先用
USE database_name;切库,再SHOW TABLES;确认表名完全一致(含大小写) - 检查是否对整个库授权:比如只给了
GRANT SELECT ON db1.* TO ...,但操作的是db2.table,就会报 1142 - 如果用的是视图或临时表,权限检查逻辑不同——视图依赖定义者权限,临时表无需额外授权,但只对当前 session 有效
CREATE DATABASE 或 DROP TABLE 被拒
这类操作需要更高级别的权限,不是普通 DML 权限能覆盖的。CREATE DATABASE 需要 CREATE 权限作用于 *.* 或 database_name.*;DROP TABLE 在 MySQL 8.0+ 中即使有 DROP 权限,若表上有外键约束且你没 REFERENCES 权限,也会失败。
容易忽略的点:
-
GRANT ALL PRIVILEGES ON db.* TO ...不包含GRANT OPTION,也不能代理授权;如需转授,必须显式加上WITH GRANT OPTION - MySQL 8.0 引入角色(role),如果用户被赋予的是角色而非直接权限,要确认角色已激活:
SET ROLE role_name;或检查activate_all_roles_on_login=ON配置 - 系统库(
mysql、information_schema)默认禁止写入,哪怕你有ALL PRIVILEGES ON *.*,对mysql.user表的 UPDATE 仍会被拒绝
刷新权限后仍不生效
执行 GRANT 或 UPDATE mysql.user 后,必须让 MySQL 重载权限表。最可靠的方式是:
FLUSH PRIVILEGES;
但注意:如果你用的是 MySQL 8.0+ 并通过 CREATE USER/GRANT 操作,其实不需要手动刷——这些语句会自动持久化并生效;只有直接修改系统表(如 UPDATE mysql.user)才必须 FLUSH。
另一个常被忽视的环节是连接复用。如果你用的是连接池(如 Python 的 PyMySQL + connection pool)、或开启了 wait_timeout 后长连接未断开,旧连接仍持有旧权限缓存。验证方法:断开重连后立刻执行 SELECT CURRENT_USER(); SHOW GRANTS;,比在原连接里反复试更可信。
复杂点在于权限叠加逻辑——全局权限 > 库级 > 表级 > 列级,但任意一级显式 DENY(MySQL 8.0+ 的 roles 支持 deny)会直接否决所有 grant。排查时别只盯 grant,也要查有没有隐式 deny 或角色冲突。