唯一有效写法是revoke drop on db_name.* from 'user'@'host';mysql不支持模糊撤销单库drop权限,必须显式指定数据库名和通配符,且需严格匹配原始授权的大小写、反引号及host。

REVOKE DROP ON db_name.* FROM 'user'@'host' 是唯一有效写法
MySQL 不支持“撤掉某个库里的 DROP 权限但保留其他权限”这种模糊操作。必须显式写出数据库名和通配符,格式固定为 REVOKE DROP ON db_name.* FROM 'user'@'host'。哪怕用户当初是通过 GRANT ALL PRIVILEGES ON db_name.* 获得的权限,撤销时也不能写 REVOKE ALL PRIVILEGES——那会把 SELECT、INSERT 等全撤掉,而你只想动 DROP。
常见错误包括:
-
REVOKE DROP ON *.* FROM 'u'@'%':这是撤全局 DROP,不是单库 -
REVOKE DROP ON db_name FROM 'u'@'%':缺.*,语法错误 -
REVOKE DROP ON `my-db`.* FROM 'u'@'%':数据库名含横线却没加反引号,报错
先查原始授权,再照抄构造 REVOKE
执行 SHOW GRANTS FOR 'user'@'host',逐行找带 DROP 且 ON db_name.* 的那条。注意大小写和反引号是否一致——MyDB.* 和 mydb.* 在 lower_case_table_names = 0 时是两回事;当初用 `my-db`.* 授的权,REVOKE 也得带反引号。
如果输出里没有明确出现 DROP ON db_name.*,说明用户可能通过角色继承权限,或当初只授了表级 DROP(比如 ON db_name.t1),那就要改用表级语法撤销,而不是库级。
单撤 DROP 权限防不住删表,还得组合回收
用户只要还有 ALTER 和 CREATE 权限,就能用 ALTER TABLE t RENAME TO t_bak + CREATE TABLE t 绕过 DROP 限制。真正阻断删表行为,至少要一起撤:
REVOKE DROP, ALTER, CREATE ON db_name.* FROM 'user'@'host'- 若目标是防数据丢失,
DELETE和TRUNCATE也得撤:REVOKE DELETE, TRUNCATE ON db_name.* FROM 'user'@'host'(MySQL 8.0+ 才需单独TRUNCATE) - 检查是否残留
SUPER或SYSTEM_VARIABLES_ADMIN:SELECT * FROM mysql.user WHERE User='user' AND Host='host',这类权限能让用户关sql_log_bin直接 DDL,REVOKE DROP对其完全无效
撤销后权限不立即生效,验证必须用新连接
执行 REVOKE 成功只更新服务端权限表,已建立的连接(包括应用连接池里的长连接)仍用旧权限快照。所以:
-
SHOW GRANTS FOR 'user'@'host'输出里没了DROP,不代表当前连接不能删表 - 唯一可靠验证方式:开一个新连接测试,例如
mysql -u user -h db-host -p -e "DROP TABLE db_name.t1" - 生产环境必须通知应用滚动重启连接池,或等连接超时自动驱逐——这个延迟窗口才是实际风险点
- 多个 host 记录(如
'user'@'localhost'和'user'@'%')要分别处理,漏一条就等于没撤











