TP6读写分离需显式调用->useReadConnection()且满足非事务、未锁主库、配置正确等条件;read_config须为含完整数据库参数的数组,与主库同级配置,字符集须统一,轮询无权重或故障转移。

Db::table('user')->useReadConnection()->select() 这行代码能走从库的前提是:配置全对、没在事务里、没手动调用 ->master(),且没被缓存机制绕过。TP6 的读写分离不是自动的,而是显式控制的——漏掉任何一个环节,查询就回退到主库。
database.php 里 read_config 怎么写才生效
read_config 必须是数组,哪怕只配一个从库也要写成 ['host' => '192.168.1.11'],不能写成字符串或对象;它和主库配置并列放在 connections 下,但不单独占一个连接键名。
- 主库(如
mysql)必须同时定义read_master和read_config两个键:前者控制「读是否允许走主库」(默认false),后者才是真正的从库列表 -
read_config数组里每个元素都是一套完整 MySQL 配置,host、port、username、password、database全得有,少一个就抛InvalidArgumentException: Database config read_config is invalid - 别在
read_config里写prefix或deploy—— 这些只对主连接生效,写了也忽略 - 如果主库用
utf8mb4而某个从库是utf8,字符集不一致会导致 emoji 字段写入失败或乱码,务必显式统一charset
为什么 Db::table('x')->select() 还是连主库
TP6 查询构造器默认不识别 SQL 类型,SELECT 不会自动触发读库路由。它只认你有没有主动声明「这是读操作」。
- 最常见漏掉:
->useReadConnection()没加,或者加在了->where()后面导致链式中断 - 事务中执行
select()会强制降级到主库,这是框架内置安全策略,不是 bug - 模型里用了
$this->db()->master()或全局调用了Db::master(),后续所有查询都会被锁定到主库 - Redis 缓存了旧的 schema(比如
schema_cache_type = redis),改完配置后没清缓存,框架仍按旧连接信息初始化
多个从库怎么轮询?能不能设权重或自动剔除
TP6 的 read_config 是纯顺序轮询(round-robin),不支持权重、延迟探测、健康检查或故障自动恢复。
- 你写的顺序就是访问顺序:
['192.168.1.11', '192.168.1.12']→ 第一次查 11,第二次查 12,第三次再回到 11 - 某台从库宕机时,框架会尝试连接并超时,然后跳到下一个;但不会标记它“下线”,下次轮到还会试一次
- 没有后台心跳机制,也没有运行时 API 可以手动下线某节点;唯一办法是重启服务,或临时注释掉故障配置再 reload
- 如果真需要故障转移,得自己封装一层:用
Db::connect()手动选可用从库,再把连接实例传给查询构造器
Db::connect('mysql_read') 和读写分离是两回事
手动用 Db::connect('mysql_read') 是多数据库连接,不是读写分离。两者底层逻辑完全不同。
立即学习“PHP免费学习笔记(深入)”;
-
Db::connect('mysql_read')返回的是独立 Query 实例,它完全脱离主从路由规则,也不会参与read_config轮询 - 模型类里设
protected $connection = 'mysql_read',只影响该模型初始化时的连接,不触发读写分离的任何逻辑 - 跨库关联(比如
User::with('logs'))不会自动切库——User走mysql,Log模型即使设了$connection = 'mysql_read',关联查询仍是两条独立 SQL,不会合并成 JOIN - 如果你已经用
Db::connect()手动连了从库,就别再混用->useReadConnection(),否则行为不可预测
database.php 后,必须清掉 runtime/config/ 和 runtime/cache/;只要有一次查询进了事务,后面所有 select() 就都回主库,这点没法绕过。



















