TP6.0强制读主库有四种方法:①链式调用->master(true);②显式切换连接对象调用->master();③事务内自动走主库;④原生SQL配合useReadConnection(false)。

当业务要求“刚写入的数据必须立刻读到”,而主从延迟导致从库查不到最新数据时,TP6.0 必须绕过读写分离机制,强制将特定查询发往主库——这不是可选项,是避免脏读的硬性需求。
确认当前配置是否已启用读写分离
打开 config/database.php,检查 connections['mysql'](或你实际使用的连接名)中是否存在以下四项且值完全匹配:
① 【'deploy' => 1】:必须是整数 1,写成 'deploy' => '1' 或 'deploy' => true 均无效;
② 【'rw_separate' => true】:必须是布尔 true,1 或 "true" 会导致静默失效;
③ 'read' 是二维数组,例如 [['hostname' => '192.168.1.11', 'username' => 'ro', 'password' => 'xxx', 'database' => 'test']];
④ 'write' 是单维数组,例如 ['hostname' => '192.168.1.10', 'username' => 'rw', 'password' => 'xxx', 'database' => 'test']。
缺任一条件,所有 Db::name()->select() 都会静默走主库,此时“强制读主库”失去意义。
方法一:链式调用 ->master(true)
在查询构造器末尾追加 ->master(true),立即锁定本次查询走主库:
Db::name('user')->where('id', 123)->master(true)->find();
这一步操作起来很简单,直接加链式方法即可;但注意:【该标记仅对当前查询生效,不会影响后续任何查询】,无需手动重置。
适用于单次关键读取,比如用户注册后立即查新生成的 token,或订单创建后立刻读订单状态。
方法二:显式切换连接对象
调用 Db::connect('mysql') 获取默认连接实例后,再调用 ->master():
$db = Db::connect('mysql');<br>$db->master();<br>$db->name('user')->where('id', 123)->find();
此方式与方法一效果等价,但更利于封装复用——比如在 Service 层统一处理强一致性读逻辑。
⚠️ 注意:Db::connect('slave1') 是绕过读写分离的硬指定,它不参与 read/write 路由,也不响应 ->master(),不要混用。
方法三:事务内自动走主库(隐式强制)
只要当前处于事务上下文中,所有查询(包括 select、find、count)均自动路由至主库:
Db::transaction(function () {<br> Db::name('user')->insert(['name' => 'Alice']);<br> $user = Db::name('user')->where('name', 'Alice')->find(); // 此处必走主库<br>});
这是 TP6 内置保障,无需额外代码;但前提是事务已开启,且未在事务块外提前执行过写操作触发上下文污染。
若事务中需混合读写,务必确保所有读操作都在同一事务内完成,否则出事务后首次读仍可能落到延迟从库。
方法四:原生 SQL 查询 + useReadConnection(false)
对 Db::query() 或 Db::execute() 发起的原生语句,框架无法自动识别读写意图,必须显式声明:
Db::query("SELECT * FROM user WHERE id = ?", [123])->useReadConnection(false);
这里 useReadConnection(false) 表示“不使用读连接”,即走主库;false 不可省略,传 null 或不传将按默认规则路由。
该方法专用于复杂 SQL 场景,如含子查询、UNION 或自定义函数的 SELECT,避免因框架语义误判而走错库。











