hyperf多租户需动态绑定独立数据库连接池,核心是租户标识→上下文存储→运行时生成连接名→模型/查询自动路由;禁用静态配置与硬编码,须运行时注册连接池、重写getconnectionname、显式指定db::connection。

Hyperf 多租户系统中,数据库连接不能靠“一套配置复用所有租户”,必须按租户动态绑定独立连接池。核心思路是:**租户标识(如 X-Tenant-Id)→ 上下文存储 → 运行时生成/选择对应数据库连接 → 模型或查询自动路由**。静态配置多个固定连接名(如 tenant_1、tenant_2)只适用于租户数极少且不变的场景,生产环境应避免硬编码。
租户上下文与连接名动态生成
租户 ID 通常从请求头、JWT 或子域名中提取,并存入 Hyperf 的协程级 Context:
- 在中间件中调用
TenantContext::set($tenantId),确保后续逻辑可访问 - 连接名建议按规则生成,例如
"tenant_{$tenantId}"或"mysql_tenant_{$tenantId}",避免特殊字符和大写 - 不推荐把租户库地址直接写死在
databases.php中;应通过运行时扩展 Config 组件注入连接配置
运行时注册租户专属连接池
框架启动后(如监听 MainWorkerStart 事件),根据租户元数据(可存在 Redis 或本地缓存中)动态注册连接配置:
- 调用
$config->set("databases.connections.{$connectionName}", $dbConfig),其中$dbConfig包含完整 driver/host/database/username/password/pool 等字段 - 每个租户连接池需独立设置
pool.max_connections,避免高租户并发挤占低租户资源 - 若租户使用不同 MySQL 版本(如部分租户用 5.7,部分用 8.0),必须显式指定
options,例如认证插件、预处理模式等
模型层自动绑定租户连接
Eloquent 模型需感知当前租户并切换连接,有两类可靠方式:
- 重写模型的
getConnectionName()方法,内部读取TenantContext::get()并返回对应连接名,例如:return 'tenant_' . TenantContext::get(); - 在基类模型中统一处理,避免每个模型重复写;注意关联查询(
with())默认不继承父模型连接,需对每个关联模型也做同样处理或显式调用on() - 禁用
$connection属性硬编码,否则无法支持动态租户
原生查询与事务的租户隔离
Query Builder 和事务不自动继承模型连接,必须显式绑定:
- 所有
Db::table()查询前,必须先Db::connection($connectionName),顺序不可颠倒 - 事务
Db::transaction()只能作用于单个连接池,跨租户事务不支持;多租户间强一致性需靠最终一致+消息补偿 - 慎用
Db::select()等无链式调用的方法——它们不接受连接名参数,必须前置connection()











