read_only=on仅防普通用户手滑,不拦super权限用户;真正全局只读须启用super_read_only=on,且需配合权限回收、磁盘监控与云平台适配。

read_only = ON 本来就不拦 SUPER 用户
这是 MySQL 的设计行为,不是 bug。read_only 的语义是“防普通用户手滑”,不是“防管理员操作”。只要账号有 SUPER 权限(比如默认的 root@'%'),执行 INSERT、DROP TABLE、CREATE FUNCTION 甚至 ANALYZE TABLE(MySQL 8.0+ 对系统表也生效)都不会被拒绝。
常见误判场景:
- 运维连错从库,用
root清表,binlog不记录 → 主库不会同步 → 数据永久不一致 - 监控脚本(如
pt-heartbeat)用高权限账号连从库,顺手写心跳表 - BI 工具或 DBA 日常查数据时,习惯性用
root登录,执行SET GLOBAL或临时建表
真正起作用的是 super_read_only = ON
super_read_only 从 MySQL 5.7.8 引入,它才是兜底机制:开启后,连 SUPER 用户也无法执行任何写操作,包括 CREATE EVENT、INSTALL PLUGIN、甚至 SET GLOBAL 本身——除非是极少数只读变量(如 sql_log_bin 在特定条件下可绕过)。
但要注意依赖关系:
-
super_read_only = ON会自动把read_only设为ON -
read_only = OFF会强制把super_read_only设为OFF - 动态设置必须按顺序:
SET GLOBAL read_only = ON→ 再SET GLOBAL super_read_only = ON;反过来直接报ERROR 1290 - 配置文件里
super_read_only = ON必须写在read_only = ON后面,否则 mysqld 启动失败
云 RDS 环境下 super_read_only 可能被屏蔽
阿里云 RDS、腾讯云 CDB 等托管服务,通常禁用 super_read_only 参数。你执行 SET GLOBAL super_read_only = ON 可能成功返回,但紧接着 SELECT @@super_read_only 仍是 0,日志里提示 Variable 'super_read_only' is a read-only variable。
此时不能依赖该参数:
- 优先使用控制台提供的“只读实例”开关(底层做了连接层拦截)
- 查对应云厂商最新文档确认是否开放该参数
- 补救方案:在 ProxySQL / MaxScale 等中间件侧配置写请求拦截规则
磁盘满也会触发只读,且和配置无关
当 /var/lib/mysql 所在分区使用率 ≥ 95% 时,MySQL 会自动静默启用 read_only = ON,错误现象一模一样(ERROR 1290),但你查 my.cnf 和 SHOW VARIABLES 都看不出异常。
排查步骤:
- 先运行
df -h,重点看/var/lib/mysql所在挂载点 - 清理 binlog:
PURGE BINARY LOGS BEFORE '2026-09-01 00:00:00'; - 检查慢查询日志、general log 是否被误开并占满空间
真正的只读防护,从来不只是改一个参数的事——它得靠参数组合 + 权限回收 + 磁盘监控 + 云平台适配四者闭环,缺一不可。











