最可靠的方式是只授予select权限,彻底禁写;其他手段如sql_safe_updates仅能拦截无where的误操作,无法防范有意执行的语句。

直接禁止用户执行 DELETE 和 UPDATE,最可靠的方式是收权限,而不是靠参数或触发器兜底。其他手段(比如 sql_safe_updates)只能拦住“没加 WHERE”的误操作,对有意执行的语句完全无效。
只授 SELECT 权限,彻底禁写
这是生产环境里最干净、最可审计的做法。MySQL 的权限模型本身支持精确控制,没必要绕路。
- 新建账号时明确只给
SELECT:GRANT SELECT ON mydb.users TO 'app_ro'@'10.20.%'; - 已有账号要清理冗余权限:先
REVOKE INSERT, UPDATE, DELETE, DROP, ALTER ON mydb.* FROM 'app_ro'@'10.20.%';,再FLUSH PRIVILEGES; - 千万别用
GRANT ALL ON mydb.*,哪怕只是临时测试——权限一旦扩散,很难追踪和回收 - 系统库(
mysql、information_schema)一律不授权,避免通过元数据绕过限制
启用 sql_safe_updates 作为辅助防线
它不能替代权限控制,但能拦住“手抖漏写 WHERE”这种高频事故。注意:它只对 UPDATE 和 DELETE 生效,且要求条件字段必须有索引(主键或唯一键也算)。
- 会话级开启:
SET SESSION sql_safe_updates = 1; - 全局生效(推荐开发/测试环境):在
my.cnf里加sql_safe_updates = 1,然后重启mysqld - 错误示例:
DELETE FROM logs;会报ERROR 1175 (HY000);DELETE FROM logs WHERE id = 123;可以执行(前提是id是主键或有索引) -
TRUNCATE不受此参数影响,必须靠权限禁用:REVOKE TRUNCATE ON mydb.* FROM 'app_ro'@'10.20.%';
从库设为只读(read_only + super_read_only)
如果目标是保护从库不被人工误改,这个配置比权限更底层、更强制。它不影响主从复制本身,只封掉所有手动写入。
- 在从库的
my.cnf中添加:read_only = on和super_read_only = on - 重启后,即使拥有
SUPER权限的账号执行DELETE FROM users;,也会收到ERROR 1290 (HY000) - 主库发来的 binlog 更新(包括
DELETE)照常应用,复制线程不受限 - 注意:该设置对
mysql系统库部分操作(如修改复制参数)仍有例外,不要依赖它做安全隔离的唯一手段
真正难的不是“怎么禁”,而是“谁该有写权限、在哪台实例上、为什么需要”。权限回收后务必实际登录验证,别信配置文件里写了就等于生效。另外,应用连接池里硬编码了 DBA 账号的旧逻辑,比任何数据库配置都更容易让防护失效。











