MySQL权限必须绑定到'username'@'host'账户,授予权限时需明确指定库名加.(如myapp_db.),禁止省略;执行后须FLUSH PRIVILEGES生效,且要区分localhost与%的连接匹配逻辑。

如何用 GRANT 精确授予指定数据库权限
直接给用户分配「只对某个库有操作权」,是 MySQL 权限管理中最常见也最易出错的场景。核心在于:权限不是绑定到“用户”本身,而是绑定到 'username'@'host' 这个完整账户标识上,且必须显式指定数据库范围(如 myapp_db.*),不能只写库名或留空。
-
GRANT SELECT, INSERT ON myapp_db.* TO 'appuser'@'192.168.1.%';—— 正确:明确限定库、通配表、限制来源 IP 段 -
GRANT ALL ON myapp_db TO 'appuser'@'%';—— 错误:缺少.*,MySQL 会报错ERROR 1064 (42000) -
GRANT SELECT ON *.* TO 'appuser'@'%';—— 危险:这是全局只读,可能越权访问其他业务库
执行后务必加 FLUSH PRIVILEGES;,否则权限不会立即生效(尤其在非 root 用户修改后或配置了 skip-grant-tables 时)。
'%' vs 'localhost':为什么授权后还是连不上?
MySQL 把 'user'@'localhost' 和 'user'@'%' 当作两个完全独立的账户。如果你用客户端从本机连接却用了 mysql -h 127.0.0.1 -u appuser,实际走的是 TCP 连接,匹配的是 'appuser'@'%',而非 'appuser'@'localhost'(后者仅匹配 Unix socket 或 -h localhost 且未启用 skip-name-resolve 时)。
- 本地开发常用:先授
'appuser'@'localhost',再授'appuser'@'127.0.0.1',覆盖两种本地连接方式 - 生产环境建议用具体 IP 段(如
'appuser'@'10.10.20.%')而非%,避免暴露给公网 -
阿里云/腾讯云 ECS 需额外检查安全组是否放行
3306端口,且 MySQL 配置中bind-address不能为127.0.0.1(否则拒绝远程)
只读用户怎么建才真正安全?
只给 SELECT 不等于“只读”——如果用户还能 DROP TABLE 或 CREATE VIEW,仍可能间接破坏数据结构或绕过权限逻辑。最小化原则下,应显式排除高危操作。
CREATE USER 'reporter'@'%' IDENTIFIED BY 'StrongPass!2025'; GRANT SELECT ON finance_db.* TO 'reporter'@'%'; -- 不要 GRANT SHOW VIEW、LOCK TABLES、EXECUTE,除非明确需要 REVOKE FILE, PROCESS, SUPER, REPLICATION CLIENT ON *.* FROM 'reporter'@'%'; FLUSH PRIVILEGES;
注意:REVOKE 只能撤销之前 GRANT 过的权限;若用户已有全局权限,必须先 REVOKE ALL PRIVILEGES ON *.* 再逐库授权,否则旧权限残留。
权限查不全?SHOW GRANTS 的真实含义
SHOW GRANTS FOR 'appuser'@'%'; 显示的是该账户被显式授予的权限,**不包含隐式继承或默认权限**(比如所有账户默认都有 USAGE,即连接权)。它也不会合并多个 GRANT 语句的结果——如果分别授了 SELECT 和 INSERT,这里会显示两条独立记录。
- 想确认某用户对某张表是否有
UPDATE权:直接用该用户登录,执行UPDATE test_table SET x=1 WHERE 1=0;看是否报ERROR 1142 (42000): UPDATE command denied -
SELECT * FROM mysql.db WHERE User='appuser' AND Db='myapp_db';可查数据库层级权限原始记录(需SELECT权限访问系统表) - 权限缓存可能延迟:修改
mysql.user表后,必须FLUSH PRIVILEGES;,否则新连接仍按旧缓存判断
权限粒度越细,排查路径越长。别迷信 SHOW GRANTS 输出,真要用时,始终以「该用户实际能否执行」为准。










