yii2多商户saas数据库隔离有三路径:独立数据库(物理隔离,合规首选)、共享库+独立schema(平衡解,需动态schema注入)、共享库+行级隔离(轻量但风险高,须全局tenant_id过滤)。租户识别必须前置稳定。

Yii2做多商户SaaS,数据库隔离不是选“怎么做”,而是选“走哪条路”——独立数据库、共享库+独立Schema、共享库+行级隔离,三者在安全、性能、可维护性上差异显著,选错一种,后期改造成本远超初期投入。
独立数据库:强合规场景的首选
每个商户独占一个数据库,物理级隔离,适合金融、医疗或对数据主权要求极高的客户。
- 不能只改 DSN 字符串,必须重置 db 组件并显式重建连接:用 Yii::$app->setComponents(['db' => $newConfig]) + Yii::$app->db->open()
- 切换动作必须放在 beforeRequest 阶段,否则 RBAC、日志、缓存等系统组件可能已初始化并连错库
- 迁移需按租户执行:./yii migrate --migrationPath=@app/migrations/tenant --db=db_merchant_abc
- 注意连接池复用问题:不同商户连接不能混用,建议禁用长连接或配置独立连接池
共享库 + 独立 Schema:中型 SaaS 的平衡解
所有商户共用一个 MySQL 实例,但各自拥有独立 database(MySQL 中 schema ≈ database),兼顾隔离与资源效率。
- 模型中统一注入 schema:重写 getTableSchema(),动态设置 $schema->schemaName = Yii::$app->tenant->schemaName
- 原生 SQL 查询不自动加前缀,必须显式写 "SELECT * FROM {$schema}.order"
- MySQL 不支持跨 database 事务,一个事务内禁止操作多个商户的表
- 避免用 CREATE DATABASE 动态建库,应预建好 schema 并由平台统一授权
共享库 + 行级隔离:轻量启动但风险最高
所有商户数据存在同一张表,靠 tenant_id 字段过滤,开发快、运维省,但极易因漏写条件导致数据裸奔。
- 必须重写 ActiveRecord 基类的 find(),全局注入 andWhere(['tenant_id' => Yii::$app->tenant->id])
- createCommand() 禁止直调,封装为 tenantCommand() 工厂方法
- 关联查询(with())默认不继承 tenant 条件,关系定义中要加 'on' => ['tenant_id' => new \yii\db\Expression('[[tenant_id]]')]
- 统计类操作(count()、sum()、exists())和软删除逻辑常被忽略,需专项检查
不复杂但容易忽略:无论选哪种方案,租户识别必须前置且稳定——子域名、请求头、JWT claim 或登录态,一旦出错,后续所有隔离都失效。











