这里有一个非常容易出错的关键点:每个数据库都必须单独执行一条 GRANT 授权语句。MySQL 不支持把多个数据库合并到一次授权中,例如 GRANT SELECT ONdb1.,db2. TO 'user'@'%' 这种写法会直接触发语法错误 ERROR 1064。正确做法只能是逐库逐条授权;另外,如果数据库名称包含特殊字符,或者需要严格区分大小写,就必须使用反引号包裹。

必须为每个数据库分别执行一条 GRANT 语句,不能合并授权,也不能用一条语句覆盖“多个指定库”——这是 MySQL 权限模型本身的硬性限制,不是操作方式上的选择问题。
GRANT 给多个数据库授权时,每个库都要单独写,不能合并到一条语句中
MySQL 不支持类似 GRANT SELECT ON `db1`.*, `db2`.*, `db3`.* TO 'user'@'%' 这样的写法。执行后会直接报语法错误:ERROR 1064 (42000)。它只接受单个数据库粒度的 ON database.* 授权结构。
- 正确方式是逐条执行,例如:
GRANT SELECT ON `db1`.* TO 'app'@'%';、GRANT SELECT ON `db2`.* TO 'app'@'%';、GRANT SELECT ON `db3`.* TO 'app'@'%'; - 即使授予的权限完全相同,也必须重复写多次——这不是多余操作,而是因为 MySQL 权限系统底层依赖
mysql.db表中“用户 + 主机 + 数据库名”这个三元组进行唯一匹配 - 不要以为用脚本生成 SQL 就能一次性省事:脚本生成没有问题,但每一条
GRANT都必须独立执行并确认返回 OK,不能简单批量粘贴到客户端一次跑完(部分客户端可能会静默跳过失败的授权语句)
数据库名包含特殊字符或大小写时,反引号一定不能省略
如果库名是 my-app-db、UserDB 或 order_2024_q3,不加反引号就可能触发 ERROR 1144 (42000): Wildcard denied for database。
- 标准写法必须是:
GRANT SELECT ON `my-app-db`.* TO 'app'@'%';,而不是GRANT SELECT ON my-app-db.* TO ... `UserDB`和`userdb`是两个不同的数据库名,是否大小写敏感取决于文件系统以及lower_case_table_names配置,但权限匹配时会严格按照你写入的字符进行比对- 通配符只在数据库名位置有效,并且仅支持
%和_,例如GRANT SELECT ON `app_%`.* TO 'dev'@'%'是合法写法;但GRANT SELECT ON `app%`.*(少了下划线)则不会按预期生效
验证 MySQL 授权是否真正生效,不要只看 SHOW GRANTS FOR 'user'@'host'
SHOW GRANTS FOR 'app'@'%' 只能显示你“曾经执行过哪些 GRANT 语句”,并不等于这些授权就一定已经正确加载。要确认实际生效的 MySQL 用户权限,应该使用目标账号登录后查看 CURRENT_USER()。
- 先用目标账号登录:
mysql -u app -p -h your-host - 然后执行:
SHOW GRANTS FOR CURRENT_USER();—— 注意这里必须是CURRENT_USER(),不是USER(),因为后者显示的是连接时使用的账号名,不一定是权限匹配到的真实账户 - 输出结果中必须清楚包含你预期的每一条授权,例如:
GRANT SELECT ON `db1`.* TO 'app'@'%'、GRANT SELECT ON `db2`.* TO 'app'@'%'……如果缺少其中某一条,就说明对应的GRANT没有成功生效(常见原因是 Host 不匹配,比如你授权的是'app'@'localhost',但实际连接来源却是'app'@'10.0.1.5')
跨库 JOIN 查询失败?先检查两个数据库的权限是否命中了同一个账号
即使 SHOW GRANTS FOR CURRENT_USER() 看上去两个库的授权都存在,跨库 JOIN 依然可能被拒绝。最常被忽视的问题就是:用户实际连接时的 Host,与执行授权时写入的 Host 并不一致。
- 先查看当前连接来源:
SELECT USER(), CURRENT_USER();—— 前者表示“你是怎么连接进来的”,后者表示“MySQL 最终把你识别成了哪个账户” - 如果输出是
'app'@'192.168.10.22'和'app'@'%',说明当前命中的是'app'@'%'这条授权记录,没有问题;但如果输出是'app'@'192.168.10.22'和'app'@'localhost',那就表示 MySQL 匹配到了另一条更精确的账户记录(例如'app'@'localhost'),而这条记录可能没有第二个数据库的访问权限 - 像
information_schema这样的系统库并不走mysql.db表,跨库 JOIN 如果涉及它,还需要额外显式授权:GRANT SELECT ON `information_schema`.* TO 'app'@'%';
真正复杂的地方,从来不是 MySQL 要写多少条 GRANT,而是每一条授权背后对应的“用户@主机 + 数据库名”这个匹配三元组,必须完全一致、一个都不能错。少写一个反引号、IP 地址段错一位,或者漏掉一次 FLUSH PRIVILEGES(虽然 5.7.6+ 之后大多数场景会自动刷新权限,但某些权限变更仍建议手动执行),都可能导致某条数据库权限直接失效,而且后续排查通常会非常耗时。
