mysql列级update权限撤销必须显式指定列名,不能模糊操作;需查mysql.columns_priv表确认权限存在,撤销后旧连接仍保留权限需重连或刷新角色,且须排查表级、角色、库级等其他权限路径。

列级UPDATE权限必须显式写出列名
MySQL不支持模糊撤销,比如REVOKE UPDATE ON db.tbl FROM 'u'@'h'不会影响列级权限。当初用GRANT UPDATE(col_a, col_b) ON db.tbl TO 'u'@'h'授的权,现在要撤col_b,就必须把列名完整写进REVOKE语句里,少一个都不行。
- 列名顺序无关,但集合必须完全一致:
REVOKE UPDATE(col_b, col_a) ON db.tbl FROM 'u'@'h'合法;REVOKE UPDATE(col_b) ON db.tbl FROM 'u'@'h'也合法(只撤一个) - 不能省略括号或写成
UPDATE(col_b, *)——语法错误 - 如果当初授权时用了反引号包裹列名(如
`col with space`),撤销时也得带反引号
先查清楚用户到底有没有这条列级权限
直接执行REVOKE前,先确认权限确实存在且没被其他方式覆盖。列级权限存放在mysql.columns_priv表里,不是SHOW GRANTS能完全反映的:
- 运行
SELECT * FROM mysql.columns_priv WHERE User='u' AND Host='h' AND Db='db' AND Table_name='tbl'; - 检查
Column_name字段是否包含目标列,Update_priv是否为Y - 注意:
SHOW GRANTS FOR 'u'@'h'只显示显式授予的语句,不体现通过角色继承或全局权限隐含的列访问能力
撤销后旧连接仍保留原权限
执行REVOKE UPDATE(col_b) ON db.tbl FROM 'u'@'h'成功,只是更新了系统表。已建立的连接(尤其是应用连接池里的长连接)不会自动丢掉col_b的UPDATE能力,直到重连或触发权限重载:
- 最可靠的方式是让客户端断开重连
- MySQL 8.0+ 可在会话内执行
SET ROLE NONE;强制刷新角色权限(若用户启用了角色) -
FLUSH PRIVILEGES在这里不是必需的,但加了也不报错;它只在你手动UPDATE过mysql.columns_priv等系统表后才真正起作用
细粒度权限容易残留,必须分层清理
撤掉col_b的UPDATE,不代表用户彻底失去对该列的写入能力。还要排查其他可能路径:
- 用户是否拥有
UPDATE权限在整张表上?那col_b自然可写,不受列级限制 - 是否通过角色获得了该列权限?需用
REVOKE 'role_name' FROM 'u'@'h'解绑角色 - 是否有更高层级权限覆盖(如
UPDATE ON db.*)?列级权限无法压制库级权限 - 验证最终效果:用该用户账号实际执行
UPDATE db.tbl SET col_b = 1 WHERE id = 1;,看是否报ERROR 1142 (42000): UPDATE command denied
真正难的不是写对那条REVOKE,而是搞清权限从哪来、在哪生效、当前连接还持有什么快照——这些不查系统表、不重连验证、不跑真实SQL,根本没法闭环。











