START TRANSACTION READ ONLY 失败的根本原因不是语法问题,而是账号缺少基础权限:必须同时具备 USAGE(登录权)、SELECT ON .(查询权)和 SHOW VIEW ON .(查视图权),否则因无法执行隐含的 SELECT 操作而报错。

MySQL 8.0 中不存在“只读事务权限”这种独立权限类型——所谓只读事务能力,完全依赖账号是否具备 SELECT 权限 + 是否被显式禁止写类操作,且必须额外处理元数据访问、登录权和系统库边界。
为什么 START TRANSACTION READ ONLY 会失败?
运维人员执行 START TRANSACTION READ ONLY 报错 ERROR 1290 (HY000): The MySQL server is running with the --read-only option... 或 ERROR 1142 (42000): SELECT command denied,根本原因不是事务语法问题,而是账号缺少基础能力:
-
START TRANSACTION READ ONLY本身不消耗额外权限,但它隐含要求后续能执行SELECT—— 如果账号连SELECT都没被授予,自然失败 - 若实例级
read_only=1已开启,而账号拥有SUPER权限(如误授了ALL PRIVILEGES),则该账号仍可写;但若没SUPER却又没SELECT,就卡在“既不能读也不能写” - MySQL 不校验事务模式与权限的匹配性,它只校验每条语句本身的权限。所以
READ ONLY事务里执行INSERT会立刻报错,但事务能否启动,只看第一条语句(通常是SELECT)有没有权限
必须显式授予的三项最小权限组合
一个能稳定使用 START TRANSACTION READ ONLY 的运维账号,至少要满足以下三者同时存在:
-
USAGE:确保能登录。漏掉就直接ERROR 1045 (28000),和事务无关,但常被忽略 -
SELECTON*.*或具体库表:否则SELECT语句失败,READ ONLY事务无法执行任何查询 -
SHOW VIEWON*.*:运维查视图定义是刚需,否则SHOW CREATE VIEW v_summary返回空或报错ERROR 1142 (42000): SHOW VIEW command denied
注意:SELECT 权限不自动包含 SHOW VIEW,也不包含对 INFORMATION_SCHEMA 的访问权——这意味着 DESCRIBE t 或 SHOW CREATE TABLE t 也会失败,需补授:GRANT SELECT ON INFORMATION_SCHEMA.* TO 'maint_ro'@'10.20.%';
必须立即撤销的高危权限项
只读运维账号一旦拥有下列任一权限,READ ONLY 就形同虚设:
-
SUPER:绕过所有权限检查,包括read_only=1实例级限制 -
LOCK TABLES:允许SELECT ... FOR UPDATE或显式加锁,虽不改数据但可能阻塞业务 -
EXECUTE:若数据库含 SQL 函数或存储过程,且函数内有写逻辑,调用即越权 -
mysql.*或performance_schema.*的SELECT:暴露用户密码哈希、连接状态、锁等待链等敏感信息;MySQL 8.0+ 的sys库依赖performance_schema,撤销后者后需单独授权sys.*
撤销命令示例:REVOKE SUPER, LOCK TABLES, EXECUTE ON *.* FROM 'maint_ro'@'10.20.%'; 和 REVOKE SELECT ON mysql.* FROM 'maint_ro'@'10.20.%';
验证时最容易被跳过的三个动作
配置完别急着交付,务必用新连接(非当前会话)执行以下验证:
- 执行
SELECT 1;→ 必须成功;否则检查USAGE和SELECT是否真生效 - 执行
START TRANSACTION READ ONLY; SELECT COUNT(*) FROM information_schema.TABLES; COMMIT;→ 必须成功;这同时验证了事务模式、INFORMATION_SCHEMA访问、以及无隐式写行为 - 执行
INSERT INTO mysql.user (User) VALUES ('test');→ 必须报ERROR 1142 (42000): INSERT command denied;若报ERROR 1142以外的错误(如权限拒绝访问mysql库),说明系统库隔离成功
真正麻烦的点不在授权本身,而在权限叠加不可见性:旧账号残留的 GRANT OPTION 或角色继承的 BACKUP_ADMIN 都可能让只读失效,所以 SHOW GRANTS FOR 'maint_ro'@'10.20.%' 的输出必须逐行人工核对,不能只看有没有 SELECT。


















