根本原因是super_read_only=ON后,MySQL服务端硬拦截所有写操作(含存储过程内隐式写入),连SUPER用户也无法绕过;CALL被中间件误判为读操作而路由至只读实例,导致执行即报ERROR 1290。

存储过程在只读实例上执行直接触发 --super-read-only 拦截
根本原因不是存储过程本身“不能执行”,而是它内部或隐含的写操作被 MySQL 实例拒绝。只要 super_read_only = ON(且 read_only = ON),任何尝试修改数据、创建临时表、写入日志、甚至某些 DDL 辅助操作(如 CREATE TEMPORARY TABLE)都会立即报错:ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement。注意:这个错误是服务端硬拦截,不看用户权限等级,连 SUPER 用户也无法绕过。
为什么 CALL 语句会被路由到只读节点
常见于使用 MySQL Router 或类似中间件的架构中:
- MySQL Router 默认按语句类型做读写分离,但
CALL proc_name()被识别为“读操作”(因为语法上不带INSERT/UPDATE/DELETE关键字) - Router 无法解析存储过程体内的实际行为——哪怕过程里有
INSERT INTO log_table,Router 也看不到 - 结果就是:写逻辑的存储过程被发往从库(只读实例),一执行就崩
- 加了
&autoReconnect=true只会让连接断开后重连到**同一个路由目标**(仍可能是只读节点),不会切换角色
如何确认当前连接是否落在只读实例上
别猜,直接查:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 执行
SELECT @@global.super_read_only, @@global.read_only;—— 两个都返回1就是只读实例 - 执行
SHOW SLAVE STATUS\G—— 如果Slave_IO_Running和Slave_SQL_Running都是Yes,基本可判定是复制从库 - 检查连接来源:用
SELECT @@hostname, @@port;结合部署拓扑比对,确认是不是连到了预期的主库地址
绕过问题的实操路径
核心原则:让存储过程调用强制走主库,而不是依赖中间件自动识别。
- 如果用 Spring Boot +
@Transactional,确保该方法标注了@Transactional(propagation = Propagation.REQUIRED),且事务管理器指向主数据源 - 若用 MySQL Router,改用其提供的「读写专用端口」(如
6446)直连主库,而非通用端口(如6450) - 在应用侧显式维护两套数据源:一个只读(给报表查询)、一个读写(专用于
CALL和写操作),并手动路由 - 避免在只读实例上执行任何
CALL—— 即使过程体里全是SELECT,也不能保证安全;某些系统表访问、权限校验或 SQL mode 衍生行为也可能触发写意图
DEFINER = 'admin'@'%' 的过程,在只读实例上调用时,即使 admin 有 SUPER 权限,super_read_only 也会直接封死所有写路径——这时候权限再高也没用。










