Hyperf 3.1 事务内必须统一走主库连接,根本原因在于事务的原子性、一致性与 MySQL 服务端会话级状态强绑定于单一写节点,从库不维护事务上下文、不响应 COMMIT/ROLLBACK、无法参与 ACID 保障,混用主从连接会导致静默失败、过期读或 ERROR 1290 等问题。

Hyperf 3.1 事务内必须统一走主库连接,根本原因在于事务的原子性、一致性与 MySQL 本身的机制强绑定在单一写节点上,而读从库不仅无法参与事务上下文,还会直接破坏 ACID 保障。
事务状态只存在于主库,从库无法感知或参与
MySQL 的事务(BEGIN / COMMIT / ROLLBACK)是服务端会话级状态,仅在执行该事务的连接所连的数据库实例中生效。
- 主库负责事务的开启、锁管理、binlog 写入、提交确认;
- 从库通过复制延迟同步数据,本身不接受写操作,也不维护任何事务上下文;
- 若你在事务中混用从库连接执行 SELECT,查到的数据可能尚未应用主库已提交但还未同步的变更(即“过期读”),甚至因主从延迟导致业务逻辑误判。
Hyperf 的 DB::transaction() 严格绑定单协程 + 单连接
-
DB::transaction()内部自动获取连接、开启事务、执行闭包、统一 commit/rollback,整个流程强制复用同一个连接实例; - 这个连接来自配置中指定的默认连接池(如
'mysql'),而该池默认指向主库; - 如果你手动切到
Db::connection('mysql_read'),就等于在事务中途换了一个完全无关的连接——它既没开启事务,也无法响应 COMMIT,框架也不会帮你透传事务状态。
混用主从会导致静默失败或报错
常见错误表现:
-
SAVEPOINT does not exist(因从库不支持事务控制语句); -
ERROR 1290 (HY000): The MySQL server is running with the --read-only option(从库设为只读,拒绝 INSERT/UPDATE); - 数据写入主库成功,但事务内后续读从库返回旧值,业务校验失败;
- 更隐蔽的是:事务回滚后,从库上查到的“中间态”数据仍存在,造成逻辑幻觉。
强一致性读必须和写同源
对“写后立刻读”的场景(例如创建订单后立即查订单详情),唯一可靠方式是:
- 全部走主库(最简单、最安全);
- 或显式加
SELECT ... FOR UPDATE锁住主库记录,确保读写隔离; - 不要依赖“先写主、再读从”,这不是最终一致性问题,而是事务完整性被主动绕过的风险行为。
本质上,这不是 Hyperf 的限制,而是分布式数据库架构下对事务本质的尊重。主库是唯一权威源头,事务就是围绕它构建的确定性执行单元。











