必须同时确认read_only和super_read_only均为on,否则super权限用户仍可写入;云rds可能屏蔽super_read_only,磁盘满≥95%也会静默启用read_only。

从库写入报错却没拦住?先查 super_read_only 是否真生效
只开 read_only = ON 是最常见的“假只读”陷阱。它确实会拦截普通账号(如 app_user)的 INSERT、DROP TABLE,但对拥有 SUPER 权限的账号(比如 root、监控脚本用的 monitor@%)完全无效。你看到的 ERROR 1290 报错,很可能只是“别人被拦住了”,而真正执行写入的账号根本没触发这个错误。
必须同时确认两个变量:
-
SELECT @@GLOBAL.read_only;返回ON -
SELECT @@GLOBAL.super_read_only;也返回ON
如果后者是 OFF,哪怕前者是 ON,从库就仍可被高权限用户写入。注意:super_read_only = ON 在 MySQL 5.7.8+ 才支持,云 RDS(如阿里云、腾讯云)可能屏蔽该参数,需查控制台文档或联系支持确认是否开放。
写入痕迹藏在哪?重点查 mysql.general_log 和 SHOW PROCESSLIST
从库被误写入后,不能只看当前状态——得找到谁、什么时候、执行了什么。默认 general_log 是关闭的,但如果已开启,它是第一手证据源:
- 检查是否启用:
SELECT @@GLOBAL.general_log; - 查日志表:
SELECT * FROM mysql.general_log WHERE argument LIKE '%INSERT%' OR argument LIKE '%UPDATE%' ORDER BY event_time DESC LIMIT 20; - 若日志未开,退而求其次:
SHOW PROCESSLIST;看是否有活跃的非system user连接正在执行写操作(注意:该命令只显示当前快照,需高频轮询或配合performance_schema持久化)
特别注意 BI 工具、定时监控脚本(如调用 FLUSH STATUS 或 ANALYZE TABLE)常带 SUPER 权限,它们的写行为容易被忽略,且 MySQL 8.0+ 中这些语句对系统表也生效,会悄悄破坏一致性。
为什么 read_only 突然变成 ON?排查磁盘与自动化脚本
运维人员手动设置后忘记关,是最常见原因;但更隐蔽的是磁盘空间自动触发:
- 当磁盘使用率 ≥95% 时,MySQL 会静默把
read_only设为ON,错误提示和你手动配置一模一样,但my.cnf里什么都没改 - 检查磁盘:
df -h,重点关注/var/lib/mysql所在分区 - 查是否有定时任务或部署脚本执行了
SET GLOBAL read_only = ON却没配对应的恢复逻辑(比如 Ansible Playbook 只写了开启步骤) - 注意:
SET GLOBAL不影响已存在的连接会话,所以刚连上的应用可能还能写,新连接才报错,这会让问题显得“时有时无”
账号权限才是最后一道防线:收紧 SUPER 和系统库访问
即使 read_only 和 super_read_only 都开了,如果账号本身权限过大,仍可能绕过限制或造成意外副作用:
- 执行
SELECT CURRENT_USER(), USER(); SHOW GRANTS FOR CURRENT_USER();,确认应用、监控、DBA 连接用的都不是root@%或带SUPER的泛用账号 - 禁用非必要
SUPER:用最小权限原则重建账号,例如应用账号只需SELECT, INSERT, UPDATE, DELETE,监控账号只需PROCESS, REPLICATION CLIENT - 收紧
mysql系统库权限:REVOKE CREATE, DROP, ALTER ON mysql.* FROM 'app_user'@'%';,避免通过创建函数、事件等方式间接写入
真正的只读不是靠一个开关,而是参数 + 权限 + 监控三者咬合。最容易被忽略的点是:云环境不支持 super_read_only,以及磁盘满导致的静默只读——这两类问题不会出现在你的配置文件里,但会真实让从库“看起来只读、实际可写”或“突然只读、查无实据”。











