MySQL设read_only=ON后普通用户仍能写入,因该参数默认仅限制非SUPER权限用户,SUPER用户(如root)可绕过;真正全局锁写需收回SUPER权限或用SYSTEM_VARIABLES_ADMIN等细粒度权限。

MySQL 设置 read_only=ON 后为什么普通用户仍能写入?
因为 read_only 默认只限制非 SUPER 权限用户,而 root 或拥有 SUPER 权限的账号(如多数运维账号)依然可以执行 INSERT/UPDATE/DELETE。这不是 bug,是设计行为——MySQL 认为高权限用户应能绕过只读保护做紧急修复。
实操建议:
- 确认当前用户权限:
SELECT CURRENT_USER(), USER();,再查权限:SHOW GRANTS FOR CURRENT_USER(); - 若需真正“全局锁写”,必须同时收回
SUPER权限(或改用SYSTEM_VARIABLES_ADMIN+SESSION_VARIABLES_ADMIN等更细粒度权限),但注意:MySQL 8.0.16+ 才支持用SYSTEM_VARIABLES_ADMIN替代SUPER控制read_only - 临时生效命令:
SET GLOBAL read_only = ON;;但该设置在重启后丢失,必须写入配置文件才持久
my.cnf 中正确配置 read_only 的位置和注意事项
read_only 是动态系统变量,但仅能在 [mysqld] 段落下配置,不能放在 [client] 或其他节里。放错位置会导致 MySQL 启动失败或静默忽略。
实操建议:
- 编辑
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]下添加:read_only = ON
- 不要写成
read_only = 1或read_only = true—— MySQL 只认ON/OFF(大小写不敏感,但推荐大写) - 主从架构中,从库默认应设为
read_only = ON;主库设此值需格外谨慎,否则可能阻断应用写请求 - 配置后必须重启 MySQL:
systemctl restart mysqld(或mysqld_safe),仅 reload 配置不生效
如何验证 read_only 是否真正生效?
不能只看 SELECT @@global.read_only; 返回 1 就认为安全了——得用实际账号测试写操作是否被拒。
实操建议:
- 用业务常用账号连接:
mysql -u app_user -p,执行:INSERT INTO test.t1 VALUES (1);,预期报错:ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement - 检查错误日志是否记录拒绝行为:
tail -n 20 /var/log/mysqld.log | grep "read-only" - 若使用代理(如 ProxySQL、MaxScale),注意它们可能缓存连接状态,需重启代理或刷新会话路由
- 某些 ORM(如 Django)会在连接初始化时执行
SET autocommit=1,这本身不触发只读校验,但后续 DML 会失败——要测完整事务链路
从库开启 read_only 后,哪些操作仍被允许?
read_only = ON 并非禁止所有写,它明确放行以下几类操作,这是容易被忽略的关键点:
- 超级用户(
SUPER或 MySQL 8.0.16+ 的SYSTEM_VARIABLES_ADMIN)执行的 DML - 复制线程自身的写入(如 SQL Thread 执行 relay log),这是从库正常工作的前提
-
TEMPORARY表的创建与操作(CREATE TEMPORARY TABLE不受限制) - 用户级锁(
GET_LOCK())、系统表写入(如mysql.general_log,若启用)、存储过程内部临时表等
也就是说,read_only 防的是“意外 DML”,不是“绝对只读”。真要杜绝一切写,得结合账号权限回收 + 触发器拦截 + 应用层开关,或者用 MySQL 8.0 的 activate_all_roles_on_login 配合角色权限控制。











