根本原因是super_read_only=off,该变量未启用导致super权限用户仍可写入;需同时设置read_only=on和super_read_only=on,并限制super权限账号,云环境还需依赖控制台只读开关或中间件拦截。

super_read_only=OFF 是管理员仍能写入的根本原因
从库设了 read_only=ON 却还能被 root 或 DBA 账号执行 INSERT、DROP TABLE,不是配置没生效,而是 read_only 本身对拥有 SUPER 权限的用户完全无效。MySQL 的设计就是如此:它只拦普通用户,不拦高权限账号。
真正起作用的是 super_read_only——这个变量从 MySQL 5.7.8 开始引入,启用后连 SUPER 用户也无法执行写语句(除极少数元数据操作,如 SET 变量)。但它不会自动开启,必须显式设置。
实操建议:
- 立刻检查:
SELECT @@read_only, @@super_read_only;—— 两个都得是1才算真正锁死 - 若
@@super_read_only是0,用SET GLOBAL super_read_only = 1;动态开启(需SUPER权限) - 但动态设置重启即失效,必须同步写进
my.cnf的[mysqld]段,并重启mysqld
管理员账号权限过大,super_read_only 形同虚设
即使 super_read_only=ON,如果管理员仍用 root@'%' 这类全通配账号连从库,就等于把钥匙交给了所有人。某些工具(比如 pt-heartbeat、云厂商监控 agent)默认用 root 连接,它们触发的写逻辑(哪怕只是 UPDATE 心跳表)也会直接绕过防护。
实操建议:
- 查所有带
SUPER权限的账号:SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y'; - 非必要不授
SUPER:DBA 日常查数据用专用只读账号,例如CREATE USER 'ro_admin'@'192.168.%' IDENTIFIED BY 'xxx'; GRANT SELECT ON *.* TO 'ro_admin'@'192.168.%'; - 降权示例:
REVOKE SUPER ON *.* FROM 'backup'@'%'; GRANT REPLICATION SLAVE, PROCESS ON *.* TO 'backup'@'%';
云 RDS 环境下 super_read_only 可能被屏蔽
阿里云 RDS、腾讯云 CDB 等托管服务,出于自身管控策略,会禁用或忽略 super_read_only 参数。你明明在控制台配置了,或者通过 SET GLOBAL 成功执行了,但 SELECT @@super_read_only 仍返回 0,甚至错误日志里提示 Variable 'super_read_only' is a read-only variable。
实操建议:
- 不要依赖
super_read_only在云环境生效;优先使用控制台提供的“只读实例”开关(它底层做了更严格的连接层拦截) - 确认是否支持:查该云厂商最新文档,或直接运行
SELECT @@super_read_only;验证 - 补救手段:在 ProxySQL / MaxScale 等中间件侧配置写请求拦截规则,作为数据库端防护的兜底
磁盘满导致自动只读,和 super_read_only 无关但现象相似
当 /var/lib/mysql 所在分区使用率 ≥ 95%,MySQL 会强制将 read_only 设为 ON 并拒绝所有写入,报错类似:The MySQL server is running with the --read-only option so it cannot execute this statement。此时你查 @@super_read_only 可能仍是 0,误以为是参数没开,其实根本没走到那一步。
实操建议:
- 先跑
df -h,重点看数据目录所在挂载点 - 临时清理:
PURGE BINARY LOGS BEFORE '2026-04-01';或删过期慢日志文件 - 长期方案:加磁盘监控告警,阈值设在 85% 以下
真正麻烦的从来不是参数怎么设,而是谁有权限去改它、连它、绕过它——super_read_only 是个开关,但钥匙还在管理员手里。











