ThinkPHP 6 默认不支持严格轮询读库,需继承 think\db\Connection 重写 getReadConnection 方法,用静态索引实现 db1→db2→db3→db1 的顺序切换,并添加异常跳过与容器绑定;同时注意主从延迟、事务一致性及强制走主库场景。

ThinkPHP 6 如何配置多个读库并轮询访问
ThinkPHP 6 原生支持读写分离,但「轮询」不是默认行为——它默认用的是 random(随机)或 weight(权重),不是严格轮询。要实现真正按顺序逐个尝试从库(比如 db1 → db2 → db3 → db1…),得自己补逻辑。
常见错误现象:Db::name('user')->select() 总是打到同一个从库,以为配置生效了,其实只是随机选中后缓存了连接;或者改了 read_master 为 true,结果写操作也跑到从库去了。
- 必须在数据库配置里显式声明多个
read节点,不能只写一个hostname -
'read' => ['192.168.1.10', '192.168.1.11', '192.168.1.12']这种写法可行,但 TP6 默认用array_rand随机取,不是轮询 - 想轮询,得重写
think\db\Connection的getReadConnection方法,或在中间件/基础模型里手动控制Db::connect()的参数 - 注意连接池和 PDO 持久化:同一请求内多次查询,TP 默认复用首个选中的从库连接,不会“每次查都换一个”
为什么不能直接靠配置项开启轮询
ThinkPHP 官方没提供 round_robin 类型的 read_type 配置项。你翻遍 think\db\connector\Mysql 和 think\db\Connection 源码,会发现读库选择逻辑集中在 getReadConfig 和 getReadConnection 里,而它们只认 random 和 weight 两种策略。
使用场景:你有三台 MySQL 从库,希望流量尽量均摊,且能感知某台宕机后自动跳过——这时候靠配置做不到“自动跳过”,得结合健康检查 + 自定义连接器。
立即学习“PHP免费学习笔记(深入)”;
-
weight看似可配权重,但权重值写在配置里,运行时无法动态调整 - 所有读库共用同一套
database/username,没法为每个从库单独设超时或字符集 - 如果某从库网络延迟高,TP 不会自动降权或剔除,除非你手动抛异常并捕获重试
自定义轮询连接器的关键三步
最轻量的做法是继承 think\db\Connection,覆盖 getReadConnection,用静态变量记下上次用的索引。别动核心包,放在 app\common\db\RoundRobinConnection.php 里就行。
示例要点:
- 在
getReadConnection里先调$this->getConfig('read')拿到从库列表 - 用
static $index = 0记位置,$index = ($index + 1) % count($reads)实现循环 - 必须加异常兜底:万一当前选中的从库连不上,要 catch
PDOException并递归调用自身(跳过该节点) - 别忘了把新连接器注册进容器:
Container::getInstance()->bind('think\db\Connection', RoundRobinConnection::class)
注意兼容性:TP6.0.x 和 6.1.x 的 getReadConfig 返回结构略有不同,6.1+ 多了一层 config['read'] 嵌套,需适配。
生产环境绕不开的坑:主从延迟与事务穿透
轮询读库解决不了主从延迟。用户刚注册完立刻查自己资料,可能查不到——这不是轮询的问题,是架构问题。很多团队踩坑在于:以为换了轮询就万事大吉,结果订单状态查错、消息重复推送。
真实使用场景里,以下情况必须强制走主库:
- 刚执行过
insert/update的同一事务内,后续select必须指定master=true,例如Db::master(true)->name('order')->where('id', 123)->find() - 涉及
SELECT ... FOR UPDATE的场景,TP 不会自动识别,得手动加master(true) - 配置里
'read_master' => true是危险开关,它会让所有读都走主库,彻底废掉读写分离 - 某些分页查询(如
count+limit)如果跨多个从库,数据一致性无法保证,不如全走主库
轮询本身不难,难的是怎么让业务代码清楚知道自己该读哪——这需要约定大于配置,比如在 Repository 层统一拦截带 _fresh 后缀的方法名,自动切主库。



















