grant语句必须显式指定db_name.table_name格式才能授予表级权限,仅写表名会报error 1144;撤销时也须严格匹配数据库名、表名和主机名。

GRANT 语句必须带数据库名前缀才能生效
MySQL 表级或列级权限不会自动推导数据库上下文,GRANT SELECT ON orders TO 'u'@'%' 会直接报错 ERROR 1144 (42000): Wildcard denied for table。必须显式写出 db_name.table_name 格式。
常见误配场景:
- 开发想给某张日志表只读权限,却写成
GRANT SELECT ON logs TO 'app'@'192.168.1.%'→ 失败 - 正确写法是
GRANT SELECT ON myapp.logs TO 'app'@'192.168.1.%' - 若要跨库访问(比如从 report 库查 core.user 表),需分别授权:
GRANT SELECT ON core.user TO 'reporter'@'%'
SHOW GRANTS 看不到表权限?查 mysql.tables_priv 才算数
SHOW GRANTS FOR 'user'@'host' 默认只显示全局和数据库级权限,哪怕你刚执行了 GRANT UPDATE ON app.config TO 'user'@'localhost',结果里也不会出现这行——这不是 bug,是 MySQL 的设计行为。
验证表权限是否真正写入:
- 运行
SELECT * FROM mysql.tables_priv WHERE User='user' AND Host='localhost' AND Db='app' AND Table_name='config'\G - 注意
Table_priv字段是逗号分隔字符串,如Update或Select,Insert - 如果查不到记录,说明
GRANT没成功:可能是用户不存在、语法错误、当前会话没GRANT OPTION
用角色封装局部权限,避免重复授予权限出错
给 10 个运维人员每人单独 GRANT SELECT, INSERT ON billing.invoices TO 'ops1'@'%'…ops10'@'%',漏一个就埋雷。MySQL 8.0+ 的 ROLE 是更稳的解法。
实操要点:
- 先建角色:
CREATE ROLE 'invoice_reader', 'invoice_writer'; - 授局部权限到角色:
GRANT SELECT ON billing.invoices TO 'invoice_reader';、GRANT INSERT, UPDATE ON billing.invoices TO 'invoice_writer'; - 再把角色分配给用户:
GRANT 'invoice_reader' TO 'ops1'@'%', 'ops2'@'%'; - 别忘了激活:
SET DEFAULT ROLE 'invoice_reader' FOR 'ops1'@'%';(否则登录后权限为空)
回收时只需 REVOKE 'invoice_reader' FROM 'ops1'@'%',不用翻每条 GRANT 记录。
撤销表权限必须严格匹配原 GRANT 语句
REVOKE 不是“模糊撤销”,它要求主机名、数据库名、表名、用户三者完全一致。比如你用 GRANT SELECT ON billing.invoices TO 'app'@'192.168.1.%' 授权,那么撤销必须写:
REVOKE SELECT ON billing.invoices FROM 'app'@'192.168.1.%';
常见坑点:
- 写成
REVOKE SELECT ON invoices FROM 'app'@'192.168.1.%'→ 无效,不报错但也不生效 - 写成
REVOKE SELECT ON billing.invoices FROM 'app'@'%'→ 匹配失败,因主机段不一致 - 误以为
REVOKE ALL ON billing.* FROM ...会连带撤掉表级权限 → 不会,表级权限独立存储在mysql.tables_priv中
权限层级越细,越容易在撤销时漏掉某个具体表的授权,建议定期用 SELECT 查 mysql.tables_priv 做核对。











