从库仅设 read_only = on 无法阻止 super 权限用户写入,必须同时启用 super_read_only = on 才真正只读;二者需按序配置且持久化到 my.cnf,还需配合最小权限账号与系统库权限收紧。

从库设了 read_only = ON 还能写入?因为 SUPER 权限用户没被拦住
只开 read_only = ON 是最常见也最容易误判的操作。它确实会让普通账号(比如 app_user)执行 INSERT 或 DROP TABLE 时立刻报 ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement,但 root 或任何拥有 SUPER 权限的账号完全不受影响。
这不是配置失败,是 MySQL 的设计逻辑:它把 read_only 当作“防手滑”,不是“防权限”。所以运维连错从库用 root 清表、脚本带 FLUSH STATUS 或 ANALYZE TABLE(MySQL 8.0+ 对系统表也生效)都会成功,主从数据就悄悄不一致了。
实操建议:
- 先查当前账号权限:
SELECT CURRENT_USER(), USER();+SHOW GRANTS FOR CURRENT_USER(); - 确认应用连接是否用了高权限账号;BI 工具、监控脚本尤其容易忽略这点
-
read_only = ON必须配合super_read_only = ON才算真正兜底,否则等于没锁
必须同时启用 read_only 和 super_read_only,且顺序不能错
super_read_only = ON 在 MySQL 5.7.8+ 引入,开启后连 root 都不能执行写操作,包括 CREATE FUNCTION、EVENT、INSTALL PLUGIN,甚至 SET GLOBAL 修改变量本身也会被拒绝——这才是从库“不该动”的完整语义。
但这两个参数有强依赖关系:
-
super_read_only = ON会自动把read_only设为ON -
read_only = OFF会强制把super_read_only设为OFF - 动态设置时,必须先
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 会在重启后丢失。
只读不是光靠参数,账号权限和磁盘空间都可能让 read_only 失效
即使两个参数都开了,仍可能“看起来只读、实际可写”或“突然只读、但不是你配的”:
- 云 RDS(如阿里云、腾讯云)可能屏蔽
super_read_only参数,配置了也无效果,得查控制台文档或联系支持确认是否开放 - 磁盘满到 ≥95% 时,MySQL 会自动触发保护机制,静默把
read_only设为ON,错误提示一模一样,但你查配置文件什么都没改——先df -h看/var/lib/mysql所在分区 - 如果主库同步了
mysql.user表,从库上app_user可能继承了SUPER权限;必须显式REVOKE SUPER ON *.* FROM 'app_user'@'%',否则super_read_only形同虚设 - 别给只读账号授
ALL PRIVILEGES,哪怕只读也要GRANT SELECT ON `db_name`.* TO 'ro_user'@'%',并确认SELECT Super_priv FROM mysql.user WHERE user = 'ro_user'返回N
read_only 不是只读账号的替代品,两者必须配合使用
read_only 是实例级开关,作用对象是“连接进来的人”;而只读账号是权限级控制,作用对象是“谁连进来”。它们解决的是不同层面的问题:
- 开了
read_only = ON,但应用仍用root连接,照样能写 - 没开
read_only,但只给ro_user授了SELECT,那他就真写不了 - 二者叠加才最稳:从库上
super_read_only = ON+ 应用层只用最小权限账号连接
最容易被忽略的一点是:从库上 read_only 默认不限制对 mysql、information_schema 等系统库的写操作。如果你的只读账号被意外授予了 UPDATE mysql.user,它就能改自己权限——所以系统库权限也得单独收紧。











