read_only=ON仅限制普通用户,SUPER权限账号(如root)可绕过;真正全局只读需super_read_only=ON,且须配合最小权限账号、磁盘空间检查及持久化配置。

因为 read_only = ON 本就不是权限控制机制,它只拦普通用户,不拦 SUPER 权限账号——这是 MySQL 的明确设计,不是配置错误。
为什么 read_only 对 root 或 SUPER 用户完全无效
MySQL 文档写死:只要用户拥有 SUPER(5.7)或 SYSTEM_VARIABLES_ADMIN(8.0.12+),read_only = ON 就形同虚设。你看到的 ERROR 1290 (HY000): The MySQL server is running with the --read-only option... 永远只报给普通账号;而 root@localhost 连 DROP TABLE mysql.user 都能成功。
-
read_only是“防手滑”开关,不是安全围栏 - 它不校验用户权限,只在 SQL 执行前做一层轻量拦截
- 常见被绕过操作包括:
FLUSH STATUS、ANALYZE TABLE(8.0+ 影响系统表)、CREATE FUNCTION、甚至SET GLOBAL自身
真正能拦住所有人的只有 super_read_only = ON
这个变量从 MySQL 5.7.8 起引入,启用后:read_only 自动置为 ON,且所有用户(含 root)执行写语句都会被拒绝,包括 INSERT、UPDATE、INSTALL PLUGIN,甚至 SET GLOBAL super_read_only = OFF 本身也会报 ERROR 1290。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 动态设置必须按序:
SET GLOBAL read_only = ON→ 再SET GLOBAL super_read_only = ON;反过来直接失败 - 配置文件中
super_read_only = ON必须写在read_only = ON之后,否则 mysqld 启动失败 - MySQL 5.7.20 以下版本不支持该变量,查版本别白忙:
SELECT VERSION();
开了 super_read_only 还被写入?先排查这三处漏点
参数开了 ≠ 真的锁死了。真实线上故障里,一半以上是防护链断在了别处:
- 云 RDS(如阿里云 RDS、腾讯云 CDB)通常屏蔽
super_read_only,SELECT @@super_read_only返回0,日志提示Variable 'super_read_only' is a read-only variable—— 得用控制台“只读实例”开关,或在 ProxySQL 层拦截写请求 - 磁盘满到 ≥95%,MySQL 会自动静默开启
read_only = ON,但super_read_only仍是OFF,此时root仍可写入;第一动作是df -h查/var/lib/mysql所在分区 - 已存在的长连接不受新参数影响,
SHOW PROCESSLIST找出旧连接 ID,再KILL CONNECTION <id></id>,否则应用还在用旧会话偷偷写
账号权限比参数更关键:super_read_only 不是银弹
就算两个参数都为 1,只要还留着 root@'%' 或给监控脚本授了 SUPER,就等于在保险柜上装了指纹锁,却把钥匙扔在门口。
- 立刻查高权限账号:
SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y'; - 运维日常查数据该用专用只读账号:
CREATE USER 'ro_admin'@'192.168.%'; GRANT SELECT ON *.* TO 'ro_admin'@'192.168.%'; - 监控工具(如
pt-heartbeat)默认用root写心跳表,得单独建低权限账号并显式授权REPLICATION CLIENT
真正锁死从库,靠的是参数顺序 + 持久化配置 + 账号最小权限 + 磁盘空间检查四者缺一不可;任何一环松动,super_read_only = ON 就只是个好看的值。

















