前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >多租户的 4 种常用方案

多租户的 4 种常用方案

原创
作者头像
苏三说技术
发布于 2025-02-19 01:07:06
发布于 2025-02-19 01:07:06
74500
代码可运行
举报
运行总次数:0
代码可运行

大家好,我是苏三,又跟大家见面了。

前言

某中型电商平台的报表系统曾在深夜突然崩溃,起因竟是运营误删了共享表中的某租户数据列。

运维团队排查发现,因为缺乏有效租户隔离,一条误操作的ALTER TABLE语句导致全平台数据混乱。

这让我们警惕:选择多租户方案的每一步,都是安全与成本的权衡

今天这篇文章就跟大家一起聊聊,多租户的4种常用方案,希望对你会有所帮助。

一、字段隔离方案

低成本背后的高风险

字段隔离方案,是通过统一数据表+租户ID过滤实现逻辑隔离。

如下图所示:

图片
图片

初期开发成本极低,但将数据安全的压力完全转移到了代码质量控制上。

致命缺陷检查清单

  • 任意一次DAO层查询漏加tenant_id条件 → 数据跨租户泄露
  • 索引必须将tenant_id作为最左前缀 → 性能瓶颈风险
  • 全表扫描类查询(如报表统计)无法避免跨租户干扰

代码防御示范

(1)MyBatis拦截器自动注入租户ID

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Intercepts({@Signature(type = Executor.class, method = "update")})  
public class 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防火墙:强制全表扫描必须声明租户范围

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* 危险操作(可能扫全表) */  
SELECT * FROM orders WHERE status = 'PAID';  


/* 安全写法(强制tenant_id过滤) */  
SELECT * FROM orders   
WHERE tenant_id = 'tenant_01'  
  AND status = 'PAID'  
  /* 必须添加LIMIT防止全量拉取 */  
  LIMIT 1000;

适用场景建议

  • 初期快速验证的MVP产品,用户量比较少的业务系统。
  • 对数据隔离要求较低的内部管理系统。

二、Schema隔离

数据库层的单元房

在同一个数据库实例中为每个租户独立Schema,实现库级别隔离

如下图所示:

图片
图片

各租户表结构相同但数据独立,像小区里的不同住户单元。

运维警告清单

  • 百级Schema数量级后,备份与迁移成本陡增
  • 跨Schema关联查询必须引入中间聚合层
  • 数据库连接池需按最大租户数配置 → 连接风暴风险

动态路由代码实现

(1)Spring动态数据源配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect  
@Component  
public class SchemaAspect {  


    @Before("@annotation(requireTenant)")  
    public void switchSchema(JoinPoint joinPoint) {  
        HttpServletRequest request = getCurrentRequest();  
        String tenantId = request.getHeader("X-Tenant-ID");  
        
        // 验证租户合法性  
        if (!tenantService.isValid(tenantId)) {  
            throw new IllegalTenantException("租户身份异常!");  
        }  
        
        // 动态切换数据源  
        DynamicDataSourceContextHolder.push(tenantId);  
    }  


    @After("@annotation(requireTenant)")  
    public void clearSchema() {  
        DynamicDataSourceContextHolder.clear();  
    }  
}

适用场景建议

  • 需要中等安全级别的行业(教育、零售)。
  • 租户数<50且数据规模可控的系统。

三、独立数据库

数据隔离的终极形态

每个租户享有独立数据库实例

如下图所示:

图片
图片

从存储到底层连接完全隔离。

安全性最高但成本呈线性增长。

财务预警清单

  • 每个实例约增加¥3000/月(云RDS基础配置)
  • 跨租户数据聚合需额外ETL系统支持
  • DBA运维成本随租户数量直线上升

数据源动态路由核心代码

(1)抽象路由控制器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)多租户事务同步器(关键!)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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级

字段过滤

共享表

动态策略选择器

针对不同的租户,我们可以使用策略模式,根据不同的等级,选择不同的数据库访问方式。

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class IsolationStrategyFactory {  


    public IsolationStrategy getStrategy(String tenantId) {  
        TenantConfig config = configService.getConfig(tenantId);  
        switch(config.getLevel()) {  
            case VIP:  
                return new IndependentDBStrategy();  
            case STANDARD:  
                return new SchemaStrategy();  
            case BASIC:  
            default:  
                return new SharedTableStrategy();  
        }  
    }  


    // 示例策略接口  
    public interface IsolationStrategy {  
        DataSource getDataSource();  
        void executeQuery(String sql);  
    }  
}

运维避坑必读

  1. 数据管理:建立租户-资源映射表,避免配置漂移
  2. 迁移工具:开发自动化升降级工具(如VIP客户从共享表迁移到独立库)
  3. 监控分层:不同方案的性能指标需独立采集分析

总结

这篇文章列举了多租户的4种常用方案。

没有最完美的,只有最合适的。

多租户设计的本质是资源、安全、成本的黄金三角博弈

与其追求理论完美,不如根据业务阶段选择最适方案。

毕竟能用可控成本解决问题的,才是真正的架构智慧。

如果看了文章有些收获,记得给我点赞喔,谢谢你的支持和鼓励。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的50万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
基于springboot+jpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源
多租户定义:多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。
鲲志说
2025/04/07
900
基于springboot+jpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源
Springboot项目使用动态切换数据源实现多租户SaaS方案
工作中遇到了多组户的需求,因为以前并没有接触过,所以多番查找资料,最后总算做出来了,再此做个总结,记录一下以便日后复习也可以帮助用得着的朋友。
全栈程序员站长
2022/07/05
4.9K0
mybatis-plus 应用2:【常用的多租户方案对比,以及实现多租户功能】
就用school表举例,每一张数据库表都需要加上tenant_id这一列,记住是每一张,每一张,每一张
danielxiao
2022/08/16
2.5K0
多租户系统如何设计
不知道为什么,最近老是有一些失眠,凌晨睡,两点半还在醒着。脑子里想着自己生活、vlog计划……就是怎么睡不着。实在是没啥可干的了,我拿起了电脑,写着博客,反正迟早是要写的。
shigen
2023/11/05
5890
多租户系统如何设计
Spring Boot 构建多租户系统 实现动态切换数据源
SaaS(Software as a Service),多租户系统(一套系统,不同租户数据不同) 它只是一种软件架构,从技术角度来说很好实现。主要是运营下去会比较难。
@依然范特西
2023/01/12
5.1K0
Spring Boot优雅实现多租户架构:概念与实战
在多租户系统中,一个应用实例服务于多个租户,每个租户享有独立的数据视图,而应用的基础设施被共享。这样的架构不仅优化了资源使用,还能降低维护和运营成本。本文将详细介绍如何在Spring Boot中实现多租户架构,并提供具体的实战案例。
小马哥学JAVA
2024/04/30
1.2K0
Spring Boot集成Mybatis-Plus多租户架构实战
目前公司产品就是对外企业服务,入职后了解到SaaS模式和私有部署,当我第一次听到SaaS时,我不是很理解。经过查阅资料,以及在后续研发功能时,不断的加深了对多租户的理解。
小东啊
2019/08/28
6.8K3
Spring Boot集成Mybatis-Plus多租户架构实战
聊聊 SaaS 多租户系统数据隔离实现方案
开发过SaaS系统平台的小伙伴一定对多租户这个概念不陌生,简单来说一个租户就是一个公司客户,多个租户共用同一个SaaS系统,一旦SaaS系统不可用,那么所有的租户都不可用。你可以这么理解SaaS系统就像一栋大楼,而租户就是大楼里面租办公楼层的公司,平时每家公司做着自己的业务,互不干扰,但是一旦大楼的电梯坏了,那么影响到的就是所有的公司。
码猿技术专栏
2023/08/10
2.3K0
聊聊 SaaS 多租户系统数据隔离实现方案
通过租户id实现的SaaS方案
项目开发到一半,用户突然提出需要多个分公司共同使用,这种需要将系统设计成SaaS架构,将各个分公司的数据进行隔离。
程序员大彬
2024/05/28
2980
通过租户id实现的SaaS方案
Spring Boot 构建多租户SaaS平台核心技术指南
笔者从2014年开始接触SaaS(Software as a Service),即多租户(或多承租)软件应用平台;并一直从事相关领域的架构设计及研发工作。机缘巧合,在笔者本科毕业设计时完成了一个基于SaaS的高效财务管理平台的课题研究,从中收获颇多。最早接触SaaS时,国内相关资源匮乏,唯一有的参照资料是《互联网时代的软件革命:SaaS架构设计》(叶伟等著)一书。最后课题的实现是基于OSGI(Open Service Gateway Initiative)Java动态模块化系统规范来实现的。
JAVA葵花宝典
2020/03/10
2.8K0
Spring Boot 构建多租户SaaS平台核心技术指南
多租户技术
多租户技术(Multi-TenancyTechnology)又称多重租赁技术,用于实现如何在多用户的环境下共用相同的系统或程序组件,并且仍可确保各用户间数据的隔离性。 具体的多租户隔离技术有多种,数据库通常有如下三种。 1. 独立数据库 这是第一种方案,即一个租户一个数据库。这种方案的用户数据隔离级别最高,安全性最好,但成本也高。 优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,则恢复数据比较简单。 缺点:增大了数据库的安装数量,随之带来维护成本和购置
大数据和云计算技术
2018/03/08
7.1K0
多租户技术
Spring Boot:实现MyBatis动态数据源
在很多具体应用场景中,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库。又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决。接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理。
朝雨忆轻尘
2019/06/19
1.9K0
Spring Boot:实现MyBatis动态数据源
利用 Spring 多租户库掌握多租户技术
Spring 多租户库为实施多租户应用程序提供了标准化方法。本指南将引导您使用 Spring 多租户库创建一个稳健、可扩展的游戏平台。
JavaEdge
2025/01/04
1320
SaaS 多租户系统数据隔离方案
开发过SaaS系统平台的小伙伴一定对多租户这个概念不陌生,简单来说一个租户就是一个公司客户,多个租户共用同一个SaaS系统,一旦SaaS系统不可用,那么所有的租户都不可用。你可以这么理解SaaS系统就像一栋大楼,而租户就是大楼里面租办公楼层的公司,平时每家公司做着自己的业务,互不干扰,但是一旦大楼的电梯坏了,那么影响到的就是所有的公司。
用户10002156
2023/08/07
8580
SaaS 多租户系统数据隔离方案
【WTM-多租户改造】「建议收藏」
WtmPlus是建立在WTM开源框架基础上的低代码开发平台,他提供了可视化的模型和页面编辑,更加复杂和智能的代码生成,可使开发效率提升50%以上。
全栈程序员站长
2022/11/15
6740
【WTM-多租户改造】「建议收藏」
【愚公系列】2023年02月 WMS智能仓储系统-006.租户功能的配置
多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且仍可确保各用户间数据的隔离性。
愚公搬代码
2023/03/16
3840
【愚公系列】2023年02月 WMS智能仓储系统-006.租户功能的配置
efcore使用ShardingCore实现分表分库下的多租户
本期主角:ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵
落寞的鱼丶
2022/02/21
1.6K0
SaaS多租户架构数据源动态切换解决方案
随着云计算和SaaS(Software as a Service)模型的兴起,多租户系统成为了构建灵活、高效应用的重要架构。在构建多租户SaaS平台时,数据库方案的选择直接关系到数据隔离、性能和可扩展性。
Tinywan
2024/04/01
1.1K0
SaaS多租户架构数据源动态切换解决方案
如何解决mybatis-plus提供的多租户插件出现Column ‘tenant_id‘ specified twice问题
本文案例来源于业务开发部门进行多租户开发时发生的案例。用过mybatis-plus多租户插件的朋友,可能会知道,该插件的租户id值基本都是从上下文得来,这个上下文可以是cookie、session、threadlocal等。据业务部门反馈,在某次插入时,他们发现获取不到租户id值,于是他们在他们的代码层面上做了这么一层操作,在保存的时候,设置租户id。保存的时候,很成功的出现了Column 'tenant_id' specified twice
lyb-geek
2021/01/21
4.3K0
如何解决mybatis-plus提供的多租户插件出现Column ‘tenant_id‘ specified twice问题
SpringBoot整合MybatisPlus 实现多租户
代码已经上传到码云:https://gitee.com/lezaiclub/springboot-hyper-integration.git,欢迎白嫖
AI码师
2022/09/19
1.4K0
SpringBoot整合MybatisPlus 实现多租户
推荐阅读
相关推荐
基于springboot+jpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验