read_only不拦截SUPER权限用户,仅限制普通用户;真正锁死所有写入需启用super_read_only,它强制禁止包括root在内的所有用户写操作,并自动同步开启read_only。

read_only 不拦 SUPER 权限用户
这是最常见也最容易被忽略的原因。read_only=ON 只会拒绝非 SUPER 权限用户的写操作,而 root、备份账号、监控 exporter、甚至某些中间件默认用的高权限账号,只要带 SUPER,INSERT/UPDATE/DELETE 全都能过。
现象:应用日志没报错,监控看到写入持续发生,但 SELECT @@global.read_only 明明是 ON。
- 检查方式:
SELECT user, host, Super_priv FROM mysql.user WHERE user = 'your_app_user'; - 真正生效的限制对象是普通账号,不是“所有账号”
- 云数据库(如阿里云 RDS)常默认禁用
read_only动态设置,改了也无效,需走控制台开关
INSERT/UPDATE 静默跳过 vs DELETE 报错
MySQL 的权限校验分两层:先查语句级权限(如是否有 DELETE 权限),再进 read_only 检查。如果用户没有 DELETE 权限,根本不会走到只读判断,直接报错;但若没授 INSERT 权限,MySQL 5.7+ 会静默丢弃该语句——不报错、不写 binlog、也不提示,看起来就像“成功”了。
- 典型表现:DELETE 失败报
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement,而 INSERT 没任何反馈 - 根本原因在源码
sql/sql_parse.cc的check_access()和check_readonly()调用顺序 - 这种静默行为在 MySQL 5.7–8.0.28 中普遍存在,GreatSQL 8.0.32+ 已修复为统一报错
super_read_only 才能拦住 root
想真正锁死所有写入,必须开 super_read_only。它比 read_only 多一层:连 SUPER 用户也无法绕过,且会自动同步置位 read_only=ON(GreatSQL 还支持反向级联)。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 启用:
SET GLOBAL super_read_only = ON; - 注意:开启后
UNLOCK TABLES会失败,与FLUSH TABLES WITH READ LOCK冲突,不能混用 - MySQL 8.0.12+ 支持,但某些运维脚本(如
mysql_upgrade)会因无法解锁而卡住,需提前关闭 - 检查是否生效:
SHOW VARIABLES LIKE 'super_read_only';返回ON才算真正锁死
还有三类“隐身写入”常被漏掉
即使 super_read_only=ON,仍可能有写入穿透进来——它们往往不走业务账号,而是由系统级任务触发:
- 定时任务:比如每天凌晨跑的
ANALYZE TABLE或OPTIMIZE TABLE,部分版本会隐式触发写操作 - 监控采集:Zabbix 或 Prometheus 的 MySQL exporter 一般只读,但自定义脚本若用了
root@localhost并执行INSERT INTO metrics_log就会突破 - 应用兜底逻辑:有些 SDK 在连接失败时会 fallback 到本地缓存库或直连主库,绕过读写分离路由
真正安全的升级或维护窗口,得靠 FLUSH TABLES WITH READ LOCK + 停 SQL_THREAD + KILL 活跃事务三步并行,read_only 单独扛不住。










