👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事上“练”
《互联网高频面试题》:面朝简历学习,春暖花开
《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题
《精进 Java 学习指南》:系统学习,互联网主流技术栈
《必读 Java 源码专栏》:知其然,知其所以然
👉这是一个或许对你有用的开源项目
国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构
RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRM、AI大模型、IoT物联网等功能:
多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro
微服务:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本
前言
某中型电商平台的报表系统曾在深夜突然崩溃,起因竟是运营误删了共享表中的某租户数据列。
运维团队排查发现,因为缺乏有效租户隔离,一条误操作的ALTER TABLE
语句导致全平台数据混乱。
这让我们警惕:选择多租户方案的每一步,都是安全与成本的权衡 。
今天这篇文章就跟大家一起聊聊,多租户的4种常用方案,希望对你会有所帮助。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
一、字段隔离方案
低成本背后的高风险
字段隔离方案,是通过统一数据表+租户ID过滤 实现逻辑隔离。
如下图所示:
初期开发成本极低,但将数据安全的压力完全转移到了代码质量控制上。
致命缺陷检查清单 :
任意一次DAO层查询漏加
tenant_id
条件 → 数据跨租户泄露索引必须将
tenant_id
作为最左前缀 → 性能瓶颈风险全表扫描类查询(如报表统计)无法避免跨租户干扰
代码防御示范
(1)MyBatis拦截器自动注入租户ID
@Intercepts({@Signature(type = Executor.class, method = "update")})
publicclass TenantInterceptor implements Interceptor {
public Object intercept(Invocation iv) throws SQLException {
MappedStatement ms = (MappedStatement) iv.getArgs()[0];
Object param = iv.getArgs()[1];
// 实体类自动填充tenant_id
if (param instanceof BaseTenantEntity) {
Field tenantIdField = param.getClass().getDeclaredField("tenantId");
tenantIdField.setAccessible(true);
if (tenantIdField.get(param) == null) {
tenantIdField.set(param, TenantContext.get());
}
}
return iv.proceed();
}
}
(2)SQL防火墙:强制全表扫描必须声明租户范围
/* 危险操作(可能扫全表) */
SELECT * FROM orders WHERE status = 'PAID';
/* 安全写法(强制tenant_id过滤) */
SELECT * FROM orders
WHERE tenant_id = 'tenant_01'
AND status = 'PAID'
/* 必须添加LIMIT防止全量拉取 */
LIMIT 1000;
适用场景建议
初期快速验证的MVP产品,用户量比较少的业务系统。
对数据隔离要求较低的内部管理系统。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
二、Schema隔离
数据库层的单元房
在同一个数据库实例中为每个租户独立Schema,实现库级别隔离 。
如下图所示:
各租户表结构相同但数据独立,像小区里的不同住户单元。
运维警告清单 :
百级Schema数量级后,备份与迁移成本陡增
跨Schema关联查询必须引入中间聚合层
数据库连接池需按最大租户数配置 → 连接风暴风险
动态路由代码实现
(1)Spring动态数据源配置
spring:
datasource:
dynamic:
primary: master
strict: true
datasource:
master:
url: jdbc:mysql://主库地址
tenant_001:
url: jdbc:mysql://从库地址?currentSchema=tenant_001
tenant_002:
url: jdbc:mysql://从库地址?currentSchema=tenant_002
(2)AOP切面动态切换Schema
@Aspect
@Component
publicclass SchemaAspect {
@Before("@annotation(requireTenant)")
public void switchSchema(JoinPoint joinPoint) {
HttpServletRequest request = getCurrentRequest();
String tenantId = request.getHeader("X-Tenant-ID");
// 验证租户合法性
if (!tenantService.isValid(tenantId)) {
thrownew IllegalTenantException("租户身份异常!");
}
// 动态切换数据源
DynamicDataSourceContextHolder.push(tenantId);
}
@After("@annotation(requireTenant)")
public void clearSchema() {
DynamicDataSourceContextHolder.clear();
}
}
适用场景建议
需要中等安全级别的行业(教育、零售)。
租户数<50且数据规模可控的系统。
三、独立数据库
数据隔离的终极形态
每个租户享有独立数据库实例 。
如下图所示:
从存储到底层连接完全隔离。
安全性最高但成本呈线性增长。
财务预警清单 :
每个实例约增加¥3000/月(云RDS基础配置)
跨租户数据聚合需额外ETL系统支持
DBA运维成本随租户数量直线上升
数据源动态路由核心代码
(1)抽象路由控制器
public class TenantDataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContextHolder.get();
}
@Override
protected DataSource determineTargetDataSource() {
String tenantId = (String) determineCurrentLookupKey();
DataSource ds = dataSourceMap.get(tenantId);
if (ds == null) {
ds = createNewDataSource(tenantId); // 动态创建新租户数据源
dataSourceMap.put(tenantId, ds);
}
return ds;
}
}
(2)多租户事务同步器(关键!)
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager() {
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
TenantDataSourceRouter router = (TenantDataSourceRouter) getDataSource();
router.initTenantDataSource(TenantContextHolder.get()); // 确保事务绑定正确数据源
super.doBegin(transaction, definition);
}
};
}
适用场景建议
金融、医疗等强合规行业
付费能力强且需要独立资源池的KA客户
四、混合架构
没有银弹的平衡术
核心原则 :按租户等级提供不同隔离方案
在系统中创建租户时,根据租户的实际情况,给它分配一个等级。
不同的等级,使用不同的隔离方案。
如下图所示:
租户等级 | 隔离方案 | 资源配置 |
---|---|---|
S级 | 独立数据库 | 独占RDS实例+只读副本 |
A级 | Schema隔离 | 共享实例独立Schema |
B级 | 字段过滤 | 共享表 |
动态策略选择器
针对不同的租户,我们可以使用策略模式,根据不同的等级,选择不同的数据库访问方式。
代码如下:
public class IsolationStrategyFactory {
public IsolationStrategy getStrategy(String tenantId) {
TenantConfig config = configService.getConfig(tenantId);
switch(config.getLevel()) {
case VIP:
returnnew IndependentDBStrategy();
case STANDARD:
returnnew SchemaStrategy();
case BASIC:
default:
returnnew SharedTableStrategy();
}
}
// 示例策略接口
publicinterface IsolationStrategy {
DataSource getDataSource();
void executeQuery(String sql);
}
}
运维避坑必读
元数据管理 :建立租户-资源映射表,避免配置漂移
迁移工具链 :开发自动化升降级工具(如VIP客户从共享表迁移到独立库)
监控分层 :不同方案的性能指标需独立采集分析
总结
这篇文章列举了多租户的4种常用方案。
没有最完美的,只有最合适的。
多租户设计的本质是资源、安全、成本的黄金三角博弈 。
与其追求理论完美,不如根据业务阶段选择最适方案。
毕竟能用可控成本解决问题的,才是真正的架构智慧。
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)