read_only 是权限感知型开关,仅对无 SUPER 或 SYSTEM_VARIABLES_ADMIN 权限的用户生效;super_read_only 才是真正封禁所有写操作的强化参数,但需正确配置、回收相关权限并重启服务。

read_only 是权限感知型开关,不是强制访问控制
MySQL 的 read_only = ON 本意是防普通用户误写,而非封禁所有写入口。它只在 SQL 层做一次权限检查:若当前连接用户拥有 SUPER 或 SYSTEM_VARIABLES_ADMIN 权限(MySQL 8.0.12+ 默认赋予 root@localhost),该语句就直接放行,不走只读拦截逻辑。
这不是 bug,是设计使然——DBA 需保留紧急修复能力。所以你执行 SET GLOBAL read_only = ON 后用 root 登录仍能 DROP TABLE,完全正常。
- 验证方式不是查变量:
SELECT @@global.read_only;返回ON不代表安全 - 必须实测:
mysql -u app_user -p -e "INSERT INTO t VALUES (1);"才算真正生效 -
SELECT CURRENT_USER(), USER();+SHOW GRANTS FOR CURRENT_USER();查清当前会话到底有没有豁免权限
super_read_only = ON 才是生产环境该开的开关
super_read_only 是 MySQL 5.7.20+ 引入的强化参数,它自动置 read_only = ON,并禁止所有用户(含 SUPER)执行写操作,包括 INSERT、CREATE FUNCTION、ANALYZE TABLE(因更新系统表)等。
但要注意:
Agent 记忆系统 — 五路融合检索 + 双时间线 + 因果链 + Spirit管家 + 记忆回声 + 弹性配置 + Circuit Breaker + GDPR合规 + 192项安全审计修复
- 它依赖
read_only,配置文件中必须先写read_only = ON,再写super_read_only = ON(顺序不能颠倒) - 仅
SET GLOBAL super_read_only = ON是临时的,重启即失效;必须写进[mysqld]段落或用SET PERSIST super_read_only = ON(MySQL 8.0.22+) - 开启后,连
SET GLOBAL read_only = OFF都会报错:ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
配置写进 my.cnf 却不生效?排查这三处
常见“设了但没用”,往往卡在这几个硬性约束上:
- 必须放在
[mysqld]段下,写在[client]或[mysql]节里会被 MySQL 静默忽略,甚至导致启动失败 - 值只认
ON/OFF字符串,别写成1、true、yes—— MySQL 不识别,当无效处理 - 确认配置路径正确:
mysqld --help --verbose | grep "Default options"查实际加载的文件;云数据库(如阿里云 RDS)可能禁用该参数,需走控制台开关 - 改完必须
systemctl restart mysqld,mysqladmin reload或systemctl reload mysqld不触发重载
真正封死写入,光靠参数远远不够
即使 super_read_only = ON 生效,仍有两个关键缺口:
-
super_read_only可被拥有SYSTEM_VARIABLES_ADMIN权限的用户动态关闭(不需要SUPER),所以必须回收该权限:REVOKE SYSTEM_VARIABLES_ADMIN ON *.* FROM 'root'@'localhost'; - 已存在的长连接不受影响——
read_only和super_read_only都是全局变量,只对新连接生效;活跃连接仍按旧状态运行,需主动 kill 或等其断开 - 某些操作表面只读,实则隐式写入:比如
INSERT ... SELECT、函数内含写逻辑的SELECT、FLUSH LOGS等,super_read_only会拦,但容易被忽略
最易被忽视的一点:从库的复制 SQL 线程本身不受 super_read_only 限制,这是为了保障主从同步正常回放 relay log;但这也意味着,只要 relay log 里有写操作,它就会被执行——只读模式拦不住合法的复制流,只拦客户端主动发起的写请求。

















