read_only=1不能替代只读账号,因其仅限制非super用户写操作,super用户(如root)仍可执行写语句;真正只读账号须显式授予SELECT权限并禁用SUPER、FILE等高危权限。

read_only=1 不能代替只读账号
MySQL 的 read_only 是实例级开关,只限制非 super 权限用户的写操作,但 super 用户(包括 root)依然能执行 INSERT/UPDATE/DELETE。它不是权限控制机制,而是运维防护手段。如果你要给开发、BI 或第三方工具配一个真正不能改数据的账号,read_only 完全不生效——他们用自己账号连进来,照样能删表(只要被授予了 DROP 权限)。
常见错误现象:SET GLOBAL read_only = 1 后,应用仍报 ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement,但换个账号登录就一切照旧;或者误以为开了 read_only 就能省掉授权步骤,结果账号被赋予了 ALL PRIVILEGES,形同裸奔。
创建只读账号必须显式授 SELECT 权限
MySQL 没有内置的 “readonly” 角色,只读 = 只给 SELECT,且仅限指定库表。其他任何权限都可能绕过只读约束:比如 INSERT 显然不行,但 CREATE TEMPORARY TABLE + INSERT 也能间接写数据;LOCK TABLES 虽不改数据,却可能阻塞线上业务;SHOW VIEW 如果视图定义含子查询或函数,执行时也可能触发副作用。
实操建议:
- 用
CREATE USER 'rpt_user'@'%' IDENTIFIED BY 'xxx';创建账号,别复用已有高权限账号 - 只对目标库授权:
GRANT SELECT ON `sales_db`.* TO 'rpt_user'@'%';,不要用GRANT SELECT ON *.* - 执行
FLUSH PRIVILEGES;(8.0.16+ 非必需,但低版本和某些中间件兼容性要求它) - 验证权限:
SHOW GRANTS FOR 'rpt_user'@'%';,确认输出里只有SELECT,没有INSERT、UPDATE、DELETE、DROP等
全局 read_only 和 super 权限的冲突点
当 read_only = 1 开启时,super 用户可绕过限制,这是设计使然。但问题在于:很多 DBA 为图省事,把只读账号也加进 super 权限组(比如通过 GRANT SUPER ON *.*),以为“只是加个权限方便排障”,结果直接废掉了 read_only 的防护意义。
更隐蔽的坑是复制环境:read_only = 1 默认允许 SQL 线程写入(因为从库需要回放 binlog),但如果你手动在从库执行 SET SESSION sql_log_bin = 0,再用 super 账号写数据,就不会记 binlog,主从数据就悄悄不一致了。
所以务必检查:SELECT user, host, Super_priv FROM mysql.user WHERE user = 'rpt_user';,确保返回 N。如果看到 Y,立刻 REVOKE SUPER ON *.* FROM 'rpt_user'@'%';。
只读账号也要防「伪只读」操作
有些语句看起来只读,实则隐式写入:比如 SELECT ... INTO OUTFILE 会写文件系统;SELECT SLEEP(10) 虽无害,但配合 INFORMATION_SCHEMA 查询可能触发元数据锁争用;SELECT * FROM performance_schema.threads 在高并发下可能拖慢性能。
真正安全的只读账号,还要限制资源和访问范围:
- 加连接限制:
CREATE USER 'rpt_user'@'%' IDENTIFIED BY 'xxx' WITH MAX_CONNECTIONS_PER_HOUR 100; - 禁用 outfile:
REVOKE FILE ON *.* FROM 'rpt_user'@'%';(FILE 权限默认关闭,但显式 revoke 更稳妥) - 避免授予
PROCESS或REPLICATION CLIENT,这些权限能暴露运行时信息甚至主从状态 - 如果用 MySQL 8.0+,考虑用角色管理:
CREATE ROLE 'readonly_role'; GRANT SELECT ON `sales_db`.* TO 'readonly_role'; GRANT 'readonly_role' TO 'rpt_user'@'%';
权限不是配完就一劳永逸的事。每次变更表结构、新增库、切换中间件,都要重新核对账号实际拥有的权限,而不是只看当初那条 GRANT 语句。










