read_only对super用户无效是mysql的设计行为,仅super_read_only=on才能真正阻止写入,且需手动开启并配合权限回收和中间件拦截。

read_only 对 SUPER 用户根本无效是设计行为
MySQL 的 read_only 变量从一开始就没打算拦住 SUPER 权限用户——它只是“普通只读”,不是“强制只读”。root、DBA 账号、备份工具(如 mysqldump --single-transaction 默认用 root)、监控 agent(如 pt-heartbeat)只要带 SUPER 或 SYSTEM_VARIABLES_ADMIN 权限,就能照常执行 INSERT、DROP TABLE、CREATE FUNCTION。这不是配置没生效,是 MySQL 明确放行的逻辑。
真正起作用的是 super_read_only=ON,但必须手动开启
super_read_only 从 MySQL 5.7.8 引入,启用后连 root@localhost 都无法写入(除 SQL 线程自身重放 relay log 外)。但它不会随 read_only 自动打开,必须显式设置:
- 动态开启(需当前会话有
SUPER):SET GLOBAL read_only = ON;→SET GLOBAL super_read_only = ON;(顺序不能反,否则报错ERROR 1290) - 持久化必须写进
/etc/my.cnf的[mysqld]段:read_only = ON<br>super_read_only = ON
- 验证是否生效:
SELECT @@global.read_only, @@global.super_read_only;两个都得是1
云 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。此时别硬刚参数,应:
- 优先使用控制台提供的“只读实例”开关(底层做了连接层拦截,比数据库参数更可靠)
- 确认是否支持:查该云厂商最新文档,或直接运行
SELECT @@super_read_only;验证 - 兜底方案:在 ProxySQL / MaxScale 等中间件侧配置写请求拦截规则,过滤
INSERT、UPDATE、DROP等语句
即使 super_read_only=ON 生效,权限回收和账号隔离仍不可少
参数只是第一道防线。如果主库同步了 mysql.user 表,从库上就可能残留带 SUPER 的应用账号;某些运维脚本也可能用通配账号(如 'root'@'%')连从库。这些都会让 super_read_only 形同虚设:
- 查所有高权限账号:
SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y'; - 回收权限示例:
REVOKE SUPER ON *.* FROM 'root'@'%';(MySQL 8.0.12+ 还要REVOKE SYSTEM_VARIABLES_ADMIN ON *.* FROM 'root'@'localhost';) - 日常只读操作改用专用账号:
CREATE USER 'ro_admin'@'192.168.%'; GRANT SELECT ON *.* TO 'ro_admin'@'192.168.%';
最容易被忽略的是:super_read_only=ON 后,连 SET GLOBAL read_only = OFF 都会被拒绝——想调参,得先关它自己。这个闭环设计,恰恰说明它不是可选补丁,而是必须被纳入变更流程的硬性依赖。











