深入探讨基于ThinkPHP构建企业级多租户系统的完整解决方案
一、多租户SaaS架构核心概念
多租户架构是SaaS(软件即服务)系统的核心设计模式,允许多个客户(租户)共享同一套应用程序实例,同时保证数据隔离和安全性。在ThinkPHP框架中实现多租户系统,需要考虑数据库设计、请求路由、数据隔离等多个关键因素。
主要实现方案对比:
- 独立数据库:每个租户使用独立的数据库,安全性最高
- 共享数据库独立Schema:同一数据库,不同数据表结构
- 共享数据库共享Schema:通过tenant_id字段进行数据隔离
二、ThinkPHP多租户架构设计
2.1 数据库设计策略
本文采用”共享数据库共享Schema”方案,通过tenant_id字段实现数据逻辑隔离,平衡性能与维护成本。
-- 租户表结构
CREATE TABLE `tenants` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '租户名称',
`domain` varchar(255) NOT NULL COMMENT '租户域名',
`status` tinyint(1) DEFAULT '1' COMMENT '状态',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 用户表(多租户)
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tenant_id` int(11) NOT NULL COMMENT '租户ID',
`username` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(255) NOT NULL,
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `tenant_id` (`tenant_id`),
KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 系统架构设计
采用中间件方式进行租户识别和数据隔离,确保代码的整洁性和可维护性。
架构流程图:
请求 → 租户识别中间件 → 设置租户上下文 → 数据库查询自动过滤 → 响应返回
三、核心代码实现
3.1 租户识别中间件
<?php
namespace appmiddleware;
class TenantMiddleware
{
public function handle($request, Closure $next)
{
// 通过子域名识别租户
$subdomain = $this->getSubdomain($request->host());
// 查询租户信息
$tenant = appmodelTenant::where('domain', $subdomain)
->where('status', 1)
->find();
if (!$tenant) {
return json(['code' => 404, 'msg' => '租户不存在']);
}
// 设置租户上下文
appcommonTenantContext::setTenantId($tenant->id);
return $next($request);
}
private function getSubdomain($host)
{
$parts = explode('.', $host);
return count($parts) > 2 ? $parts[0] : 'www';
}
}
3.2 租户上下文管理
<?php
namespace appcommon;
class TenantContext
{
private static $tenantId;
public static function setTenantId($tenantId)
{
self::$tenantId = $tenantId;
}
public static function getTenantId()
{
return self::$tenantId;
}
public static function hasTenant()
{
return !is_null(self::$tenantId);
}
}
3.3 多租户模型基类
<?php
namespace appmodel;
use thinkModel;
use appcommonTenantContext;
class BaseModel extends Model
{
protected static function onBeforeWrite(Model $model)
{
// 自动设置tenant_id
if (TenantContext::hasTenant() && empty($model->tenant_id)) {
$model->tenant_id = TenantContext::getTenantId();
}
}
protected function scopeTenant($query)
{
if (TenantContext::hasTenant()) {
$query->where('tenant_id', TenantContext::getTenantId());
}
}
public static function getAll($where = [])
{
return self::tenant()->where($where)->select();
}
}
3.4 控制器中的使用示例
<?php
namespace appcontroller;
use appBaseController;
use appmodelUser;
class UserController extends BaseController
{
public function index()
{
// 自动过滤当前租户的用户
$users = User::getAll(['status' => 1]);
return json([
'code' => 200,
'data' => $users,
'msg' => 'success'
]);
}
public function create()
{
$data = $this->request->post();
// tenant_id会自动设置
$user = User::create($data);
return json([
'code' => 200,
'data' => $user,
'msg' => '用户创建成功'
]);
}
}
四、完整案例演示
4.1 中间件注册配置
// app/middleware.php
return [
// 全局中间件
appmiddlewareTenantMiddleware::class,
];
4.2 路由配置
// route/app.php
use thinkfacadeRoute;
Route::get('users', 'UserController/index');
Route::post('users', 'UserController/create');
Route::put('users/:id', 'UserController/update');
Route::delete('users/:id', 'UserController/delete');
4.3 数据库配置
// config/database.php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'saas_system',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'debug' => true,
],
],
];
4.4 测试用例
<?php
namespace apptest;
use thinktestingTestCase;
class TenantTest extends TestCase
{
public function testTenantIsolation()
{
// 模拟租户A的请求
$this->withHeader('Host', 'companya.example.com')
->get('/users');
// 验证只返回租户A的用户数据
$this->assertNotEmpty($this->response->getData());
}
}
五、性能优化与最佳实践
5.1 数据库优化策略
- 索引优化:为tenant_id字段建立复合索引
- 查询缓存:使用Redis缓存租户配置信息
- 分库分表:大租户数据量时考虑独立数据库
5.2 缓存策略实现
<?php
namespace appcommon;
use thinkfacadeCache;
class TenantCache
{
public static function getTenant($domain)
{
$key = "tenant:{$domain}";
$tenant = Cache::get($key);
if (!$tenant) {
$tenant = appmodelTenant::where('domain', $domain)->find();
if ($tenant) {
Cache::set($key, $tenant, 3600); // 缓存1小时
}
}
return $tenant;
}
}
5.3 安全性考虑
- 严格验证租户域名合法性
- 实现租户数据导出权限控制
- 定期审计数据访问日志
- 实施SQL注入防护措施
5.4 扩展性设计
为支持未来可能的数据迁移,建议在架构设计时考虑以下扩展点:
- 抽象数据库连接管理,支持动态数据源
- 设计租户数据迁移工具
- 实现租户配置的热加载机制
总结
本文详细介绍了基于ThinkPHP 6.0构建多租户SaaS系统的完整技术方案。通过中间件机制实现租户识别,利用模型基类自动处理数据隔离,确保了系统的安全性和可维护性。这种架构设计既保证了代码的简洁性,又提供了良好的扩展能力,适合中小型SaaS项目的快速开发。
实际项目中,还需要根据具体业务需求考虑租户自定义字段、多级租户管理、数据备份恢复等高级功能。希望本文能为您的ThinkPHP多租户系统开发提供有价值的参考。

