hyperf多数据源需在config/autoload/databases.php中显式定义各连接名(如'user_db'、'order_db'),每个连接独立配置pool、charset等参数,模型需声明$connection属性,跨库查询须用全限定表名或分步查询,事务仅限单库,热更新配置后需重启进程生效。

Hyperf 项目里直接用 mysql 多数据源做垂直拆分(比如用户库、订单库分离),不是靠框架自动识别业务边界,而是靠你手动配、手动切、手动管——不写清楚数据源归属,Db::connection('user_db') 就会报 Connection not found。
怎么配多个 MySQL 数据源而不冲突
Hyperf 的 db 组件默认只认 default 连接,加第二个必须显式声明;否则 Db::connection('order_db') 会直接抛异常,而不是 fallback 到 default。
- 在
config/autoload/databases.php里定义多个连接配置,每个以键名区分,例如'user_db'、'order_db',不能只改host或database却共用同一个连接名 - 每个连接的
pool.min_connections和pool.max_connections要按业务读写量单独设,比如订单库写多,max_connections建议设为 50,用户库读多缓存强,可设为 20 - 务必检查
charset和collation是否一致,混合使用utf8mb4_unicode_ci和utf8mb4_general_ci可能导致 JOIN 查询时隐式转换失败 - 如果用了
hyperf/database的模型功能,每个模型顶部要加protected $connection = 'user_db';,否则它默认走default,哪怕你在查询时写了Db::connection('user_db'),模型实例仍可能错连
跨库关联查询时为什么查不到数据
MySQL 本身不支持跨库 JOIN(除非用 db_name.table_name 全限定写法且权限允许),而 Hyperf 的 Db 类不做自动库名补全——from('users') 永远只查当前连接指定的库,不会因为你之前连了 user_db 就自动把 orders 表也拉过去。
- 禁止在单个
Db::connection()链式调用里混用不同库的表,例如Db::connection('user_db')->table('users')->join('orders', ...)必然失败,因为orders不在user_db中 - 真要关联,得用两次查询:先查
users,取出user_id数组,再用whereIn查orders表,或者用Db::raw()写全限定名Db::table('order_db.orders')(前提是账号有跨库权限) - 如果依赖 Eloquent 模型关联,
belongsTo等方法底层仍是单连接查询,需重写newQuery方法强制指定连接,或改用DB::raw手动拼
事务跨多数据源一定会失败
Hyperf 的 Db::transaction() 仅作用于单个连接实例,Db::connection('user_db')->transaction(...) 和 Db::connection('order_db')->transaction(...) 是两个完全隔离的事务,无法保证原子性。XA 事务在 Hyperf + MySQL 场景下基本不可用,官方不推荐。
- 不要写类似“先扣用户余额,再生成订单”的跨库事务逻辑,否则会出现余额已扣但订单未创建,或反之
- 替代方案是最终一致性:用本地事务 + 消息队列(如
hyperf/amqp)发事件,下游服务监听并重试执行;或用状态机 + 定时任务对账 - 如果必须强一致,把相关表强行放在同一库(比如订单和用户基础信息放一起),用水平分表代替垂直分库,牺牲部分解耦换事务能力
垂直拆分最易被忽略的点,是配置热更新失效——改了 databases.php 里的某个库密码,不重启 worker 进程,旧连接池仍拿着过期凭证连老地址;Db::reconnect() 也不能刷新整个连接池,只能重建单次连接。上线前务必验证连接池是否真正切换到了新配置。











