MySQL从库开启read_only后主库写入不受影响是正常设计,因为SQL线程作为系统内部线程,重放relay log时跳过read_only检查,仅限制本地用户会话的写操作。

从库开启 read_only 后主库写入不受影响,这是正常设计
MySQL 从库的 read_only 和 super_read_only 只限制**本地发起的写操作**,对 SQL 线程重放 relay log 的行为完全放行。主库产生的任何变更,只要复制链路正常(IO 线程拉取、SQL 线程执行),就会照常写入从库——这不是“绕过只读”,而是 MySQL 复制机制的底层约定。
为什么 SQL 线程能无视 read_only?
SQL 线程属于系统内部线程,它执行的是已验证过的中继日志事件,不是用户会话发起的 DML/DDL。MySQL 源码中明确将 SQL 线程标记为“复制上下文白名单”,read_only 的检查逻辑在权限校验阶段就跳过了这类线程。你可以验证这一点:
-
SELECT THREAD_ID, TYPE, PROCESSLIST_USER FROM performance_schema.threads WHERE TYPE = 'FOREGROUND' AND PROCESSLIST_USER IS NOT NULL;—— 查不到 SQL 线程,因为它不走普通连接路径 -
SHOW PROCESSLIST;中看到的Slave_SQL_Running线程显示为system user,不是你配置的复制账号
误以为“被主库写入”其实是复制延迟或误判
常见混淆点有三个:
- 应用连错了库:本该连主库的写请求发到了从库,而从库恰好没开
super_read_only,导致root或高权限账号成功写入,随后又被主库同一条 binlog 覆盖,看起来像“主库在写从库” - 监控脚本或 pt-heartbeat 在从库建表写入,但没用专用账号,而是复用了带 SUPER 权限的运维账号,
read_only = ON对它无效 - 磁盘空间 ≥95% 触发 MySQL 自动保护,静默置
read_only = ON,但此时 SQL 线程仍在运行,所以主库变更继续同步进来,而你查@@read_only是 1,误以为“只读生效了”,其实只是本地写被拦,复制照旧
真正要防的是从库被“人”或“脚本”误写
只读参数不是防火墙,它不拦截网络包,也不加密流量。它的作用边界非常清晰:只拦当前实例上由非系统线程发起的写语句。所以必须配套做三件事:
- 所有应用账号禁止授予
SUPER权限,用GRANT SELECT ON db.* TO 'app_user'@'%';这类最小集授权 - 运维账号改用专用低权限账号(如
dba_ro)连从库,仅授REPLICATION CLIENT和PROCESS,禁用INSERT/UPDATE/DROP - 云 RDS 用户注意:阿里云、腾讯云等可能屏蔽
super_read_only设置,需确认控制台是否开放该参数,否则只设read_only等于留后门
最易忽略的一点:主从切换后,原从库升主,read_only 和 super_read_only 不会自动关闭。如果没配自动化清理脚本,新主库会持续拒绝写入,而错误提示和真只读一模一样——得靠 SHOW SLAVE STATUS\G 里 Slave_IO_Running: No 和 Seconds_Behind_Master: NULL 组合判断是否已升主,再手动关。这个状态不会自己恢复。


















