根本原因是read_only仅防误操作而非权限控制,SUPER用户(如root)被设计豁免;真正全局只读须启用super_read_only=ON(5.7.8+),且需先设read_only=ON、持久化配置、收紧SUPER权限,并防范云环境屏蔽、磁盘满自动触发等绕过场景。

只开 read_only = ON 无法防范高权限越权,根本原因在于它不拦截拥有 SUPER 权限的用户——包括 root、DBA 账号、监控脚本账号、甚至某些中间件内置账号。这不是配置失效,而是 MySQL 的明确设计:它把 read_only 当作“防误操作”,不是“防权限滥用”。
为什么 read_only = ON 对 SUPER 用户完全无效
MySQL 在执行写操作前,会先检查当前会话是否具有 SUPER 权限;若有,则直接跳过 read_only 检查。这意味着:
-
INSERT INTO t VALUES (1)、DROP TABLE t、CREATE FUNCTION f()等语句对SUPER用户全部放行 -
FLUSH STATUS、ANALYZE TABLE(MySQL 8.0+)、SET GLOBAL修改变量也照常执行 - 运维连错从库用
root清表、BI 工具执行TRUNCATE、备份脚本调用OPTIMIZE TABLE,都会成功且不留痕迹
super_read_only = ON 为什么有时也拦不住写入
这个参数常被误认为是“终极只读开关”,但它有严格前提和边界:
- 必须在
read_only = ON已生效的前提下才起作用;若read_only是OFF,super_read_only实际为无效状态 - 仅限制非
SUPER用户的写操作,对已拥有SUPER权限的账号无约束力(名字有误导性) - MySQL 5.7.8+ 才支持,旧版本压根没有该变量,仅靠
read_only防护极弱 - 云 RDS(如阿里云、腾讯云)可能屏蔽该参数,配置了也无效果,需查控制台文档确认是否开放
哪些操作天然绕过 super_read_only 校验
MySQL 的权限校验发生在 SQL 层,但有些操作根本不走 SQL 解析器,因此 super_read_only 完全不干预:
-
LOAD DATA INFILE(只要账号有FILE权限) -
CREATE TABLE ... SELECT或INSERT INTO ... SELECT(仅需源表和目标表的对应权限) - 调用含写逻辑的存储过程(过程内执行的写语句,不重新校验调用者权限)
-
SET sql_log_bin = OFF后执行写操作(需SUPER权限,可绕过 binlog 记录,但依然能改数据)
真正锁死从库写入的关键动作组合
单靠参数永远不够,必须参数 + 权限 + 启动约束三者闭环:
- 配置文件中按顺序写死:
read_only = ON在前,super_read_only = ON在后,且都置于[mysqld]段 - 对所有非 DBA 账号显式回收:
REVOKE SUPER ON *.* FROM 'app_user'@'%'; - DBA 账号日常禁用
SUPER,必要时临时授予,操作完立即REVOKE - 验证
sql_log_bin是否可关闭:SET sql_log_bin = OFF;应报错ERROR 1290,否则说明防护未生效 - 检查从库是否跳过
mysql库同步:SHOW SLAVE STATUS\G中Replicate_Ignore_DB不得含mysql,否则权限变更不会同步
最容易被忽略的是:磁盘空间不足(≥95%)时,MySQL 会自动静默启用 read_only = ON,现象和你配的一模一样,但 SHOW VARIABLES 查不到来源。出问题时先 df -h 看 /var/lib/mysql 所在分区。


















