MySQL不支持只撤某张表的DROP权限,必须显式写出数据库名和表名,且ON子句须与原始GRANT完全一致,否则静默失败或报ERROR 1141;唯一合法写法为REVOKE DROP ON db_name.tbl FROM 'user'@'host'。
不能只撤“某张表的 DROP 权限”——MySQL 不支持模糊撤销,必须显式写出数据库名和表名,且 ON 子句必须和当初 GRANT 时完全一致,否则静默失败或报 ERROR 1141。
MySQL 的权限系统不识别“从库级权限里抠出一张表”,它只认完全匹配的 ON 范围。哪怕你当初是用 GRANT DROP ON mydb.* 授的权,只要想精确到单表,就必须改用表级语法:
REVOKE DROP ON mydb.users FROM 'u'@'%' ✅ 正确(假设当初就是这么授的)REVOKE DROP ON mydb.* FROM 'u'@'%' ❌ 错误(这是撤整个库,不是单表)REVOKE DROP ON *.users FROM 'u'@'%' ❌ 语法错误REVOKE DROP ON users FROM 'u'@'%' ❌ 缺少数据库名,报错表名含特殊字符(如横线、点)必须用反引号: REVOKE DROP ON `my-db`.`user_log` FROM 'u'@'%'
执行 REVOKE 后提示 Query OK,但用户还能删表?大概率是 ON 子句没对上。重点检查:
lower_case_table_names = 0 时,MyDB.Users ≠ mydb.users)GRANT 是不是带了反引号?那 REVOKE 也得带'u'@'localhost' 和 'u'@'%' 是两个独立账号,必须分别处理SHOW GRANTS FOR 'u'@'%',逐行确认 ON 后的内容用户仍可能通过其他路径绕过:
ALTER TABLE t RENAME TO t_bak + CREATE TABLE t → 需同步 REVOKE ALTER, CREATE ON mydb.users FROM 'u'@'%'
SUPER 或 SYSTEM_VARIABLES_ADMIN → 可关 sql_log_bin 直接 DDL,REVOKE DROP 对其完全无效TRUNCATE TABLE 不依赖 DROP 权限(MySQL 8.0+ 需单独 TRUNCATE 权限,旧版靠 DELETE+DROP)→ 若目标是防数据丢失,还得 REVOKE TRUNCATE, DELETE ON mydb.users FROM 'u'@'%'
执行 REVOKE 成功只更新服务端权限表,已建立的连接(包括应用连接池里的长连接)仍用原权限快照。验证是否真生效的唯一方式是:
SHOW GRANTS FOR 'u'@'%',确认输出里已无对应 DROP 行mysql -u u -h db-host -p -e "DROP TABLE mydb.users"
真正难的不是写对那条 REVOKE,而是搞清权限在哪一层被授予过、有没有角色继承、当前连接是否还在用旧快照——这些不查系统表、不重连验证,根本没法闭环。