不能直接撤销单表DROP权限;必须严格匹配GRANT时的数据库名和表名,如REVOKE DROP ON db_name.tbl FROM 'user'@'host',否则静默失败或报ERROR 1141。

REVOKE DROP ON db_name.tbl 是唯一合法写法
MySQL 不支持“只撤某张表的 DROP 权限”这种模糊操作,必须显式写出数据库名和表名,格式为 REVOKE DROP ON db_name.tbl FROM 'user'@'host'。哪怕你当初是用 GRANT DROP ON db_name.* 授的权,只要想精确到单表,就必须改用表级语法——因为权限系统不识别“从库级权限里抠出一张表”,它只认完全匹配的 ON 范围。
常见错误现象:ERROR 1141 (42000): There is no such grant defined,本质是 ON 后写的范围和当初 GRANT 时不一致。比如授的是 mydb.users,却写 mydb.* 去撤;或者大小写不匹配(lower_case_table_names = 0 时,MyDB.Users ≠ mydb.users)。
- 先查原始授权:
SHOW GRANTS FOR 'user'@'host',逐行确认ON子句内容 - 表名带特殊字符(如横线、点)需用反引号包裹:
REVOKE DROP ON `my-db`.`user_log` FROM 'u'@'%' - 不能省略
ON子句,否则报错;也不能写成ON *.tbl或ON db_name.*试图覆盖单表
撤销后用户仍能删表?检查 ALTER + CREATE 组合路径
单撤 DROP 权限防不住删表,因为用户可能走 ALTER TABLE ... RENAME + CREATE TABLE 的绕过路径。只要他还持有 ALTER 和 CREATE 权限,就能把原表重命名再重建同名空表,效果等同于删除。
更隐蔽的是 SUPER 或 SYSTEM_VARIABLES_ADMIN 权限:这类高危权限允许用户临时关闭 sql_log_bin,绕过 binlog 约束直接执行 DDL,且 REVOKE DROP 对其完全无效。
- 查是否残留高危权限:
SELECT * FROM mysql.role_edges WHERE TO_USER = 'user' AND TO_HOST = 'host' - 查代理权限:
SELECT * FROM mysql.proxies_priv WHERE User = 'user' AND Host = 'host' - 真正阻断删表行为,得组合回收:
REVOKE DROP, ALTER, CREATE ON db_name.tbl FROM 'user'@'host'
TRUNCATE 和 DELETE 不受 DROP 权限控制
TRUNCATE TABLE 和无条件 DELETE FROM tbl 都不需要 DROP 权限。前者在 MySQL 8.0+ 需要单独的 TRUNCATE 权限,旧版则依赖 DELETE + DROP;后者只要 DELETE 权限就可清空整表,且不走 binlog(除非开启 binlog_format = ROW)。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
所以如果目标是防“数据丢失”,光撤 DROP 毫无意义。必须同步处理:
-
REVOKE TRUNCATE ON db_name.tbl FROM 'user'@'host'(MySQL 8.0+) -
REVOKE DELETE ON db_name.tbl FROM 'user'@'host'(所有版本) - 若用户有
ALL PRIVILEGES,先REVOKE ALL PRIVILEGES ON db_name.tbl FROM 'user'@'host',再按需重授安全权限
旧连接不生效,必须验证 + 重连
REVOKE 执行成功只是修改了权限元数据,已建立的连接仍持有旧权限缓存。用户不重连前,DROP TABLE 依然能成功——这不是 bug,是 MySQL 的设计行为。
FLUSH PRIVILEGES 在 MySQL 8.0+ 中非必需(权限变更自动刷新内存),但它对旧连接也无效。真正有效的验证方式只有两步:
- 立刻执行:
SHOW GRANTS FOR 'user'@'host',确认输出里已无对应DROP行 - 通知应用重启连接池,或手动
KILL CONNECTION强制断开旧会话 - 别信 “Query OK” 提示,它只表示语句解析通过,不代表权限真被移除
实际操作中最容易被忽略的,是用户可能在多个 host 上存在重复账号(如 'u'@'localhost' 和 'u'@'%'),漏掉任意一个,权限就等于没撤干净。










