error 1290报错源于read_only=on拦截普通用户写操作,但root等高权限账号因拥有super或system_variables_admin权限而豁免;真正兜底的是super_read_only,且磁盘满时mysql会自动静默启用read_only。

报错 ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement,说明当前连接被 read_only = ON 拦截了——但这只是表象。真正要问的是:为什么这个拦截“生效”了?还是“没生效”?答案取决于你用的是什么账号、连的是哪台实例、以及有没有启用更严格的防护。
为什么普通账号一写就报 ERROR 1290
因为 read_only = ON 的设计目标就是拦普通用户。它不改权限系统,只在语句执行前加一道检查:如果当前会话的用户没有 SUPER(MySQL 5.7)或 SYSTEM_VARIABLES_ADMIN/CONNECTION_ADMIN(MySQL 8.0+)这类高权限,就直接拒绝写操作。
- 典型触发场景:应用用
app_user@'10.10.%'连从库,执行INSERT INTO orders→ 立刻报错 - 注意:
SELECT ... FOR UPDATE也算写操作,同样被拦(即使没改数据,但涉及锁) -
innodb_read_only是另一回事,影响 InnoDB 表空间级只读,和这个报错无关
为什么 root 或 DBA 账号写入却不报错
因为 read_only = ON 对拥有 SUPER 或动态管理权限的账号完全无效——这不是 bug,是 MySQL 官方文档明确写死的行为。
- 执行
SELECT CURRENT_USER(), @@session.super_priv;,若返回'root'@'localhost'和'Y',那这个连接天然豁免read_only - 常见漏点:监控脚本、备份工具(如
mysqldump --single-transaction)、甚至云厂商 agent 默认用root连接,它们的写操作(比如更新心跳表)会绕过所有read_only防护 - 此时必须查
@@super_read_only:如果它是OFF,那整个“只读实例”形同虚设
为什么开了 read_only 还是报错,但 super_read_only=OFF 却没被发现
因为很多运维只记得配 read_only = ON,却忽略 super_read_only 才是真正兜底项。而它的状态容易被误判:
-
SET GLOBAL read_only = ON;成功 ≠ 实例已全局只读;必须紧接着SET GLOBAL super_read_only = ON;(顺序不能反,否则报ERROR 1290) - 配置文件里如果
super_read_only = ON写在read_only = ON前面,MySQL 启动直接失败,但日志可能被滚动覆盖,没人看到 - 云 RDS(如阿里云、腾讯云)常屏蔽
super_read_only变量:你SET GLOBAL成功,但SELECT @@super_read_only仍返回0,且错误日志提示Variable 'super_read_only' is a read-only variable
为什么磁盘满导致只读,但报错看起来一样
MySQL 在 /var/lib/mysql 所在分区使用率 ≥95% 时,会自动静默设置 read_only = ON,但 super_read_only 保持 OFF 不变——这会导致:普通用户报错,root 却还能写,进一步加剧磁盘压力。
- 第一排查动作永远是
df -h,尤其盯住mysql数据目录所在挂载点 - 别急着
SET GLOBAL read_only = OFF,先清理 binlog、slow log 或临时文件释放空间 - 该机制无告警、无日志(默认级别),靠监控覆盖不到就只能靠经验
最易被忽略的点是:参数只是最后一道防线。账号权限没收干净、磁盘没监控、云环境没确认控制台开关是否真等价于 super_read_only,任何一环断掉,“只读实例”就只剩名字好听。











