只开 read_only = on 不足以防止误写,必须同时启用 super_read_only = on 并配最小权限账号;因为 read_only 仅拦截普通用户,super 权限用户(如 root)仍可执行 insert、drop table 等操作,导致主从数据不一致。

只开 read_only = ON 不足以防止误写,必须同时启用 super_read_only = ON 并配最小权限账号——否则 root 或任何 SUPER 权限用户仍可执行 INSERT、DROP TABLE、CREATE FUNCTION 甚至 SET GLOBAL,主从数据会在无声中不一致。
为什么从库设了 read_only 还能被写入
因为 read_only = ON 仅拦截普通用户(无 SUPER 权限)的写操作,对拥有 SUPER(5.7)或 SYSTEM_USER/SYSTEM_VARIABLES_ADMIN(8.0+)权限的账号完全无效。常见误写场景包括:
- 运维用
root@localhost登录从库执行FLUSH STATUS或ANALYZE TABLE(MySQL 8.0+ 对系统表也生效) - BI 工具或监控脚本使用高权限账号连接,触发隐式写入(如
SELECT ... FOR UPDATE被拒绝前可能已加锁) - 云 RDS 控制台显示“只读实例”,但底层未开放
super_read_only参数,配置了也无效果
必须同时配置 read_only 和 super_read_only
二者有强依赖顺序和持久化要求,动态设置或配置文件写错都会导致失效:
- 动态设置时,必须先执行
SET GLOBAL read_only = ON,再执行SET GLOBAL super_read_only = ON;反过来会直接报ERROR 1290 - 配置文件中,
super_read_only = ON必须写在read_only = ON之后,否则 MySQL 启动失败 - 必须写入
/etc/my.cnf的[mysqld]段并重启服务;仅SET GLOBAL在重启后丢失 -
super_read_only = ON会自动把read_only设为 ON,但read_only = OFF会强制把super_read_only设为 OFF
只读不是靠参数兜底,而是账号 + 参数 + 磁盘三重校验
即使两个参数都开了,仍可能“看起来只读、实际可写”:
- 检查当前连接账号:
SELECT CURRENT_USER(), USER();+SHOW GRANTS FOR CURRENT_USER();,确认应用没用root或通配符'%'@'%' - 云环境(如阿里云 RDS)可能屏蔽
super_read_only,需查控制台文档或联系支持确认是否开放 - 磁盘满到 ≥95% 时,MySQL 会自动静默把
read_only设为 ON,df -h查/var/lib/mysql所在分区才是第一排查项 - 权限表不复制:主库上
CREATE USER不会同步到从库,只读实例必须单独创建账号并GRANT SELECT,否则连接直接失败
MySQL 8.0 新增的数据库级只读(ALTER DATABASE ... READ ONLY)适用场景有限
该功能(8.0.22+)允许对单个数据库设只读,比如 ALTER SCHEMA lfq READ ONLY = 1,但它:
- 不阻止对其他数据库的写入,无法替代实例级防护
- 对已建立连接的会话立即生效,但无法防跨库操作(如
UPDATE other_db.t1 JOIN lfq.t2) - 不防
mysql系统库写入,mysql.user仍可被高权限账号篡改 - 不能替代
super_read_only,因为后者才真正禁用INSTALL PLUGIN、CREATE EVENT等高危操作
最易被忽略的是:参数开了 ≠ 安全了。你得确认谁在连、连的是哪个实例、账号有没有被绕过、磁盘是不是快满了——只读状态是动态结果,不是静态配置。











