Laravel 的 DB::connection() 默认不轮询,因它仅按配置名(如'mysql')固定连接指定 host;多主库轮询需手动选连接名(如'mysql_1'),并配合原子计数或 Redis 实现,事务中必须复用同一连接。

为什么 DB::connection() 默认不走轮询?
Laravel 的数据库连接池机制本身不自动做写入轮询,它只按配置名查连接——'mysql' 就永远连 connections.mysql 里定义的那个 host。所谓“多主库轮询写入”,本质是你得手动决定这次写操作该用哪个连接,框架不会替你判断哪台主库此刻负载低。
常见错误现象:DB::connection('mysql')->table('users')->insert(...) 始终打到同一台主库,监控发现三台主库流量 90%:5%:5%,根本没轮起来。
- 必须显式指定连接名,比如
DB::connection('mysql_1')、DB::connection('mysql_2') - 连接名不能重复,且每个都要在
config/database.php的connections数组里独立配置 - 如果用 Eloquent 模型,默认走
$connection = 'mysql',不改就永远不轮询
怎么配多个主库连接并实现简单轮询
核心是把“选连接”这个动作从框架里拎出来,自己控制。Laravel 不提供内置轮询器,但给你留了钩子:用 DB::extend() 或封装一个 ConnectionRouter 类都行,最轻量的是在业务层用取模或原子计数器选连接名。
示例(放在 Service 或 Repository 中):
// 简单原子计数(注意:仅适用于单机部署,多实例需换 Redis 计数)
static $counter = 0;
$connections = ['mysql_1', 'mysql_2', 'mysql_3'];
$connectionName = $connections[(++$counter) % count($connections)];
DB::connection($connectionName)->table('orders')->insert($data);
- 不要在
.env里共用DB_HOST配多个连接——必须拆成DB_HOST_1、DB_HOST_2等,再在 config 中分别引用 - 每个连接的
'sticky' => false必须显式设为false,否则读操作可能被绑定到刚写的那个连接,破坏读写分离逻辑 - 事务场景下禁止轮询:一个事务所有操作必须走同一个连接,否则报
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active
用 DB::transaction() 时怎么保连接一致
事务强制要求单连接上下文,一旦你在事务里混用不同连接名,PDO 会直接报错或数据不一致。这不是 Laravel 的 bug,是底层数据库协议限制。
正确做法只有一种:事务开始前先选定连接,并全程复用它。
$connectionName = $this->getWriteConnection(); // 你自己的轮询逻辑
DB::connection($connectionName)->transaction(function ($db) use ($data) {
$db->table('users')->insert($data);
$db->table('logs')->insert(['action' => 'user_created']);
});
- 别在事务闭包里再调
DB::connection('xxx'),闭包参数$db就是当前连接实例 - 如果模型用了
$connection属性,记得在事务内临时覆盖:User::on($connectionName)->create(...) - 队列任务里做轮询写入时,要确保
$connectionName被序列化传入,而不是靠运行时全局状态
读写分离 + 多主轮询时最容易漏的点
很多人以为配好多个 write 连接就能自动分流,其实 Laravel 的 read/write 分离机制只认配置里的 'read' 和 'write' 键,不认连接名后缀。也就是说,即使你写了 mysql_1 到 mysql_3,只要它们都在 'write' 数组里,Laravel 仍只会随机挑一个——不是轮询,是随机。
- 真正可控的轮询必须绕过
'read'/'write'配置,直接用DB::connection('xxx')显式调用 - 如果你依赖
DB::table()->insert()这种语法,就得重写Illuminate\Database\Connectors\ConnectionFactory的createReadWriteConnection方法,代价大、升级易冲突 - 健康检查缺失会导致故障转移失败:某台主库宕机后,轮询仍会把 1/3 请求打过去,需配合自定义连接探测 + 缓存失效逻辑
轮询本身很简单,难的是状态一致性、事务边界和故障时的降级策略。别指望配几个 connection 就能全自动高可用——连接路由逻辑得自己兜底,而且得在每个写入口校验。


















