读写分离导致主从延迟引发脏读,tp6.0需强制主库查询:①确认deploy=1、rw_separate=true、read为二维数组、write为一维数组;②用->master(true)、db::connect('master_db')或事务内查询强制主库。

当用户提交订单后立即查询订单状态却查不到最新数据,或管理员修改配置后前端刷新仍显示旧值,这类问题往往不是代码逻辑错误,而是读写分离导致的主从延迟引发的数据脏读。TP6.0 默认将 select 类操作路由至从库,但关键业务场景下必须绕过自动路由,强制走主库连接。
确认当前读写分离是否已启用
打开 config/database.php 文件,定位到 connections['mysql'] 配置块,检查是否存在且同时满足以下四项:
① 【'deploy' => 1】 —— 必须为整数 1,写成 true 或 "1" 均无效;
② 【'rw_separate' => true】 —— 必须为布尔 true,设为 1 或字符串会静默失效;
③ 'read' 是二维数组(如 [['hostname' => 's1'], ['hostname' => 's2']]),一维写法不报错但完全忽略;
④ 'write' 是单维数组(如 ['hostname' => 'm1', 'database' => 'shop']),二维写法启动即抛异常。
关键业务查询强制走主库的三种方法
方法一:链式调用 ->master(true)
在模型或 Db 查询中直接追加该方法,例如:OrderModel::where('id', $id)->master(true)->find()。这会临时锁定本次查询使用主库连接,不影响后续其他查询的路由逻辑。
方法二:使用 Db::connect() 指定主库连接名
若已在 connections 中定义了独立主库连接(如名为 'master_db'),可显式调用:Db::connect('master_db')->table('orders')->where('id', $id)->find()。注意:此方式绕过所有读写分离机制,【必须确保该连接配置完整且未启用 rw_separate】,否则可能意外回退到默认分离逻辑。
方法三:事务内自动强制主库(无需额外代码)
只要包裹在 Db::transaction(function () { ... }) 内的任何查询(包括 select()、count()、value()),TP6.0 一律发往主库。这是框架内置行为,无需手动干预,但需注意事务范围不可过大——长事务会持续占用主库连接并放大延迟风险。
避免脏读的典型场景与操作路径
第一步:用户提交表单 → 后端执行 Db::name('user_config')->insert($data);
第二步:立即查询刚写入的配置 → 必须用 Db::name('user_config')->master(true)->where('user_id', $uid)->find();
第三步:若需批量验证写入结果 → 改用 Db::transaction 包裹 insert + select,而非分两次独立请求;
这一步不能省略:任何含 FOR UPDATE 或 LOCK IN SHARE MODE 的原生 SQL,即使不在事务中,也会被 TP6 自动识别并强制路由至主库——但前提是 SQL 字符串中必须包含这些关键字,大小写敏感,空格不可省略。











