super_read_only=ON拦不住写操作,因其仅限制非SUPER权限用户,而DBA或运维的SUPER账号仍可执行INSERT/UPDATE;且依赖read_only=ON生效,MySQL 5.7.20+才支持。

为什么 super_read_only=ON 有时拦不住写操作
因为 super_read_only 只阻止普通用户(包括有 SUPER 权限以外的所有账号)的写入,但**不拦截拥有 SUPER 权限的用户**。运维或 DBA 账号如果带 SUPER,执行 INSERT、UPDATE 时完全不受影响——这是最常被忽略的设计前提。
常见错误现象:SHOW VARIABLES LIKE 'super_read_only'; 显示 ON,但从库日志里仍出现写入记录,甚至主从复制中断。
- 必须确认所有连接从库的应用账号、监控账号、备份脚本账号,都不具备
SUPER权限 -
super_read_only依赖read_only=ON生效,若后者为OFF,前者实际无效 - MySQL 5.7.20+ 才支持该变量;旧版本只能靠
read_only,防护更弱
如何真正锁死从库写入:组合配置 + 权限收口
单靠 super_read_only 是不够的,得叠加权限控制和启动参数限制。
- 启动时加
--read_only=ON --super_read_only=ON,避免运行时被动态关闭 - 对所有非 DBA 账号执行:
REVOKE SUPER ON *.* FROM 'app_user'@'%'; - DBA 账号也应分角色:日常只用无
SUPER的只读账号,必要时临时授权,完事立即回收 - 在从库上禁用
sql_log_bin=OFF的能力(它能让有SUPER的人绕过 binlog 写本地),可通过SET sql_log_bin = OFF;是否报错来验证是否生效
误写发生后怎么快速止损和定位源头
一旦发现从库有写入,别急着重启或重搭,先查清楚是谁、在哪、写了什么。
- 查当前活跃写操作:
SELECT * FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE '%INSERT%' OR SQL_TEXT LIKE '%UPDATE%'; - 看连接来源:
SELECT USER, HOST, COMMAND, STATE FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep'; - 检查最近 binlog 事件:
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/mysql-bin.000001 | grep -A 5 -B 5 'table_name' - 重点盯应用配置里的
writeHost或url参数——很多中间件(如 MyCat、ShardingSphere)默认把从库也当可写节点轮询
哪些场景下 super_read_only 天然失效
不是配置了就万事大吉。有些操作根本不走 SQL 层权限校验,super_read_only 完全不干预。
- 通过
LOAD DATA INFILE导入本地文件(需FILE权限) - 使用
CREATE TABLE ... SELECT或INSERT ... SELECT从本地表读写(只要用户有对应表权限) - 调用存储过程,而过程内含写逻辑且定义者是
SUPER用户(SQL SECURITY DEFINER 模式) - Percona Toolkit 工具如
pt-online-schema-change默认会临时关掉read_only,若在从库误跑,直接穿透防护
真正难防的是那些“合法权限下的非法意图”——比如一个本该只连主库的定时任务,因配置漂移连到了从库并执行了清理脚本。这种问题不会报错,但后果比 crash 还严重。










