mysql权限需明确指定作用域,on *.*禁止使用;create user和grant后无需flush privileges;localhost与127.0.0.1权限分离;应revoke all后逐条grant并用show grants验证。

GRANT 语句里不写 ON *.* 就别想有全局权限
很多人以为 GRANT SELECT 就完事了,结果账户一建好就能查所有库——根本原因是漏写了作用范围。MySQL 的 GRANT 必须明确指定数据库和表,否则默认不生效(5.7+)或意外授予全局权限(旧版本行为不一致)。
实操建议:
- 始终用
GRANT SELECT ON `db_name`.`table_name` TO 'user'@'host',哪怕只想查一张表 - 要限制到具体库?写成
GRANT INSERT ON `app_log`.* TO 'logger'@'%',.*是安全的,但ON *.*绝对禁止 - 不确定当前有哪些隐式权限?执行
SHOW GRANTS FOR 'user'@'host'看真实结果,别信自己“记得”写了什么
CREATE USER 后必须 FLUSH PRIVILEGES 吗
不需要,而且加了反而可能掩盖问题。MySQL 5.7.6+ 中 CREATE USER 和 GRANT 都会自动重载权限表,手动 FLUSH PRIVILEGES 不仅多余,还可能在并发操作时导致权限状态短暂不一致。
常见错误现象:
- 执行完
GRANT立刻连不上——其实是 host 匹配失败(比如用了'user'@'localhost'却从远程连),不是权限没刷进去 - 改了密码后仍能登录——大概率是存在同名但 host 更宽泛的账号(如
'user'@'%'),优先级高于'user'@'192.168.1.%' - 权限改了但应用没反应——检查连接池是否复用旧连接,有些驱动不会自动重认证
只读账户被 INSERT 报错:权限继承陷阱
MySQL 没有“只读角色”这种东西,SELECT 权限不自动排除 INSERT、UPDATE。更危险的是:如果用户已有 USAGE 权限(创建账号时默认赋予),再 GRANT SELECT,并不会自动撤回其他权限。
正确做法:
- 新建账号后立刻
REVOKE ALL PRIVILEGES ON *.* FROM 'user'@'host',清空基础权限 - 再逐条
GRANT,例如:GRANT SELECT, SHOW VIEW ON `report_db`.* TO 'reporter'@'10.0.2.%' - 特别注意
SHOW VIEW——没有它,即使有SELECT权限也查不了视图,报错ERROR 1142 (42000): SELECT command denied to user
localhost 和 127.0.0.1 在权限里是两回事
MySQL 认为 'user'@'localhost' 和 'user'@'127.0.0.1' 完全是两个账号,底层用 Unix socket 和 TCP 连接区分。很多本地测试用 mysql -u user -p 走 socket,却给 'user'@'127.0.0.1' 授了权,结果连不上。
使用场景判断:
- 容器内连接宿主 MySQL?基本走
127.0.0.1,别只配localhost - PHP-FPM 默认用 socket,配权限时 host 写
localhost才生效 - 想统一处理?干脆用
'user'@'%'+ 防火墙控源 IP,比猜 host 更可靠
最小权限不是靠少写几个单词实现的,是靠每一条 GRANT 都清楚作用域、每一处 host 都匹配真实连接路径、每一次 SHOW GRANTS 都验证过结果。漏掉其中任何一环,权限就可能比你以为的大得多。











