\_super\_read\_only=ON无法阻止root写入,因其仅为云厂商代理层开关,直连mysqld实例无效;真正生效的是原生read_only和super_read_only,且需同时启用并禁用skip_grant_tables。

为什么 _super_read_only=ON 无法阻止 root 写入?
因为 _super_read_only 不是 MySQL 原生变量,它是某些云厂商(如阿里云 RDS、腾讯云 CDB)自定义的扩展配置,仅作用于其管控层代理逻辑,**对直连 mysqld 实例完全无效**。真实生效的是原生的 read_only 和 super_read_only(MySQL 5.7.8+)。如果你改了 _super_read_only 却发现 root 还能建库删表,说明你正在绕过代理直连——这恰恰暴露了权限回收的盲区。
- 云数据库控制台里设置的
_super_read_only,只影响通过代理端口(如 3306)发起的请求 - 若应用或 DBA 直连后端真实 IP:Port(如 3307),该开关形同虚设
- MySQL 原生命令
SET GLOBAL super_read_only = ON才真正锁死所有 SUPER 用户写操作,包括 root
super_read_only=ON 后 root 仍能写,一定是没关 skip_grant_tables
启用 super_read_only 后,root 被禁止写入是默认行为,但有一个例外:如果 MySQL 启动时加了 --skip-grant-tables 或配置文件里写了 skip_grant_tables=1,权限系统被跳过,super_read_only 就直接失效。
- 检查运行参数:
mysql -e "SELECT @@skip_grant_tables;" - 检查配置文件(
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf)是否残留skip_grant_tables - 该选项本就只该用于紧急密码重置,生产环境必须禁用
收回写权限必须同步处理 read_only 和 super_read_only
单独开 read_only=ON 只限制普通用户;SUPER 用户(如 root)仍可写。必须两者都开,才能真正封死所有写路径。
- 顺序很重要:先
SET GLOBAL read_only = ON;,再SET GLOBAL super_read_only = ON; - 反过来可能触发报错:
ERROR 1238 (HY000): Variable 'super_read_only' is a read only variable - 持久化需写入配置文件,否则重启失效:
read_only=ON和super_read_only=ON都要加进[mysqld]段 - 注意:MySQL 5.7.6 之前不支持
super_read_only,强行设会报错
云数据库里改 _super_read_only 之后还要手动踢掉活跃连接
云厂商的 _super_read_only 是会话级拦截,已建立的连接不会自动中断,旧事务、未提交的写操作仍可能成功。
- 执行后立刻查活跃连接:
SHOW PROCESSLIST;,重点关注User为root或高权限账号的Command为Query的行 - 主动终止:
KILL <code>id;(id来自PROCESSLIST第一列) - 尤其注意长事务、备份工具(如
mysqldump --single-transaction)可能持有旧连接,持续写入临时表
真正难的不是开关本身,而是确认所有连接路径都被覆盖——代理、直连、监控采集、备份任务、中间件连接池……漏掉任意一个,写权限就等于没收回。










