Hyperf 3.2 不支持通过 url 数组直接轮询多从库,需配置多个独立从库连接池,并结合轮询策略类(如 RoundRobinSlaveResolver)或手动选库(如 atomic_inc 计数)实现读请求分发,同时注意事务强制走主库、避免 read 配置为数组等限制。

Hyperf 3.2 中,若想对多个从库(slave)使用 URL 数组实现轮询读取,不能直接靠配置 `url` 为数组来自动轮询——Hyperf 的数据库组件(基于 `hyperf/database`)底层使用的是 `PDO` 或 `Swoole\Coroutine\MySQL`,不原生支持多 URL 轮询。但可以通过「自定义读写分离策略」+「手动管理从库连接池」来实现类似效果。
1. 配置多个从库连接池
在 config/autoload/databases.php 中,为每个从库单独定义一个连接池(推荐用不同名字区分),例如:
'slaves' => [
'slave1' => [
'driver' => 'mysql',
'host' => '192.168.1.101',
'port' => 3306,
'database' => 'test_db',
'username' => 'user',
'password' => 'pass',
// ... 其他参数
],
'slave2' => [
'driver' => 'mysql',
'host' => '192.168.1.102',
'port' => 3306,
'database' => 'test_db',
'username' => 'user',
'password' => 'pass',
// ... 其他参数
],
],
注意:不要把它们塞进同一个 `url` 字段里;Hyperf 不解析数组形式的 `url`,会报错或静默失败。
2. 实现轮询式读库选择器
继承 Hyperf\Database\ConnectionResolver 或更常用的是实现自定义 Hyperf\Database\Connectors\ConnectionFactory + 重写读连接获取逻辑。但更轻量的方式是:创建一个轮询策略类,在每次查询前动态切换读库。
示例(简单轮询计数器):
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Database\Connection;
use Hyperf\Database\ConnectionResolverInterface;
use Psr\Container\ContainerInterface;
class RoundRobinSlaveResolver implements ConnectionResolverInterface
{
private array $slaveNames = ['slave1', 'slave2'];
private int $index = 0;
public function __construct(private ContainerInterface $container) {}
public function connection($name = null): Connection
{
$name = $name ?: $this->getRoundRobinSlaveName();
return $this->container->get('db')->connection($name);
}
private function getRoundRobinSlaveName(): string
{
$name = $this->slaveNames[$this->index];
$this->index = ($this->index + 1) % count($this->slaveNames);
return $name;
}
}
然后在 config/autoload/db.php 中替换默认 resolver(需配合自定义 DbFactory)或通过 AOP/事件拦截读操作——但更推荐下面第 3 种方式。
3. 使用中间件或基类统一控制读库路由
Hyperf 默认通过 DB::connection('default') 获取连接,而 default 的读写分离由 read_write_ratio 和 read 配置决定。我们可以绕过它,改用显式指定从库名:
- 在模型中重写
getConnection(),根据上下文返回轮询的 slave 连接 - 封装一个
SlaveDB工具类,提供select()、table()等方法,内部自动轮询 - 最实用做法:在 Service 层调用前手动选库,例如:
$slaveName = match (atomic_inc('slave_round_index') % 2) {
0 => 'slave1',
1 => 'slave2',
};
$result = DB::connection($slaveName)->table('users')->where('id', 1)->first();
⚠️ 注意:atomic_inc 是 Hyperf 提供的原子计数器(需启用 hyperf/atomic),保证协程安全;也可用 Swoole\Atomic 或 Redis 计数器替代。
4. 补充建议与避坑点
- 主从延迟敏感场景下,轮询不如按负载/响应时间/权重调度,可后续扩展为健康检查 + 权重路由
- 事务内务必强制走主库,避免
DB::transaction()中混入从库查询 - 不要在
config/autoload/databases.php中给default的read配置数组——Hyperf 3.2 不支持该写法,会报Invalid argument supplied for foreach() - 如需深度集成,可监听
Hyperf\Database\Events\QueryExecuted事件统计各从库耗时,用于动态调整轮询顺序











