read_only=on拦不住root或super用户,因其仅为“防手滑”开关,非权限控制机制;真正全局只读需启用super_read_only=on(mysql 5.7.8+),它自动设read_only=on并禁止所有用户(含super)写操作,但须按序配置、持久化且配合账号权限收紧。

为什么 read_only = ON 拦不住 root 或 SUPER 用户
因为 read_only 本就不是权限控制机制,而是“防手滑”开关。MySQL 明确设计为:只要用户拥有 SUPER(或 MySQL 8.0.12+ 的 SYSTEM_VARIABLES_ADMIN)权限,read_only = ON 对其完全无效。你看到的 ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement 只会出现在普通账号身上;root 连 DROP TABLE mysql.user 都能成功——这不是 bug,是文档写死的行为。
真正能拦住所有人的只有 super_read_only = ON
这个变量从 MySQL 5.7.8 起引入,启用后:
– 自动把 read_only 设为 ON
– 禁止所有用户(含 root@localhost)执行写操作,包括 INSERT、CREATE FUNCTION、ANALYZE TABLE(8.0+ 影响系统表)、甚至 SET GLOBAL 本身
– 尝试关闭它会直接报错:ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
但要注意:
– 必须先设 read_only = ON,再设 super_read_only = ON(动态设置顺序错会失败)
– 配置文件里 super_read_only = ON 必须写在 read_only = ON 后面,否则 MySQL 启动失败
– MySQL 5.7.20 以下版本不支持该变量,别白费劲
就算开了 super_read_only,为什么还是可能被绕过
常见漏点不是参数没开,而是防护链断在了别处:
- 云 RDS(如阿里云 RDS、腾讯云 CDB)通常屏蔽
super_read_only,SELECT @@super_read_only返回0且日志提示Variable 'super_read_only' is a read-only variable—— 得用控制台“只读实例”开关或中间件拦截 - 磁盘满到 ≥95% 时,MySQL 会自动静默开启
read_only,但super_read_only仍是OFF,此时 root 仍可写入;先跑df -h看/var/lib/mysql所在分区 - 已存在的长连接不会受新
super_read_only设置影响,必须重启应用连接或杀掉旧会话:KILL CONNECTION <id></id> - 某些监控工具(如
pt-heartbeat)默认用 root 写心跳表,得单独建低权限账号并显式授权REPLICATION CLIENT,别让它蹭 root 权限
账号权限比参数更关键:super_read_only 不是银弹
如果你还留着 root@'%' 或给运维账号授了 SUPER,那等于在保险柜上装了指纹锁,却把钥匙挂在门把手上。实操必须同步做:
- 查所有高危账号:
SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y'; - 回收非必要
SUPER:REVOKE SUPER ON *.* FROM 'backup'@'%';,改授REPLICATION SLAVE, PROCESS, REPLICATION CLIENT - 给日常只读需求建专用账号:
CREATE USER 'ro_admin'@'192.168.%' IDENTIFIED BY 'xxx'; GRANT SELECT ON *.* TO 'ro_admin'@'192.168.%'; - 验证权限是否干净:
SHOW GRANTS FOR 'ro_admin'@'192.168.%';,输出里不能出现INSERT、UPDATE、DROP、SUPER
最易忽略的一点:主从切换后,原从库升主库,super_read_only = ON 不会自动关闭——新主库会卡在“假性只读”,所有应用写请求持续失败。自动化故障转移工具(如 MHA、Orchestrator)必须包含关闭该参数的步骤。











