hyperf分库分表核心是按业务规则自动路由读写,需配置多连接池并实现分片策略类与分片repository;连接池须独立配置、命名规范,策略类负责库表双路由计算,repository封装路由逻辑,禁止直连原始表名。

Hyperf 实现分库分表,核心不是简单配多个数据库,而是让数据写入和查询能按业务规则自动路由到正确的库和表。它依赖两层控制:一是多连接池的正确配置,二是运行时根据分片键(如 user_id)动态选择连接与表名。漏掉任一环节,就会出现数据错写、查不到或跨库事务失败等问题。
配置多个语义清晰的数据库连接池
在 config/autoload/database.php 中定义至少两个独立连接池,命名需全小写、无符号、有含义(如 db_shard_0、db_shard_1),不能复用 default 或混用不同版本实例:
- 每个连接必须显式声明 driver、host、port、database、username、password
- 为每个连接单独配置 pool 参数,例如
'pool' => ['min_connections' => 2, 'max_connections' => 20],避免统一调优导致低版本实例拒绝连接 - 若涉及 MySQL 5.7 和 8.0 混合部署,需分别指定
PDO::MYSQL_ATTR_SSL_MODE和PDO::ATTR_EMULATE_PREPARES等兼容性参数 - default 连接池建议保留为空或禁用,防止未显式指定连接时意外命中主库
编写分片策略类,实现库表双路由
创建 app/Sharding/ShardingStrategy.php,封装基于分片键(如 user_id)的计算逻辑:
-
getTableName(int $userId):对用户 ID 取模,返回
orders_3这类带后缀的表名 -
getConnection(int $userId):结合分表数与分库数,算出应使用的连接名,例如
db_shard_1 - getAllShards():返回全部库+表组合数组,用于后台全量扫描或数据迁移
- 策略类不操作数据库,只做纯计算,便于单元测试和复用
封装分片 Repository,屏蔽底层细节
所有业务对分片表的操作都通过统一入口,比如 app/Repository/ShardingOrderRepository.php:
-
create():先调
$sharding->getTableName($userId)和$sharding->getConnection($userId),再用Db::connection(...)->table(...)->insertGetId() - findByUserId():同样路由到唯一库表,确保单用户数据物理聚集,支持高效查询
- 禁止直接使用
Db::table('orders')或 Eloquent 模型访问原始表名,否则绕过分片逻辑 - 关联查询需额外处理——如果订单要关联用户,用户表未分片则走默认连接;若用户也分库,则需在关联中再次调用分片策略
读写分离与分片协同注意事项
当分库分表叠加读写分离时,规则更严格:
- 事务只能绑定单一连接池,跨分片库的事务不被支持,例如不能在一个事务里同时写
db_shard_0.orders_1和db_shard_1.orders_5 - 从库仅用于查询,
on('read_pool')后不可调用save()或update(),否则触发SQLSTATE[HY000]: General error: 1290 - 原生查询必须前置
Db::connection('xxx'),Db::table()默认永远走 default,不会继承 Model 的 on() 设置 - 关联预加载(with)需逐层指定连接,例如
Order::on('db_shard_0')->with(['user' => fn($q) => $q->on('db_master')])











