Java 领域 MyBatis 多租户数据隔离的实现方案
关键词:MyBatis、多租户、数据隔离、动态数据源、SQL拦截器、租户路由、SaaS架构
摘要:在SaaS(软件即服务)应用开发中,多租户数据隔离是核心技术挑战之一。本文系统解析基于MyBatis的多租户数据隔离方案,涵盖独立数据库、共享数据库独立Schema、共享表字段级隔离三种主流模式的技术实现。通过动态数据源路由、MyBatis拦截器、SQL语法解析等核心技术,结合Spring Boot实战案例,演示如何在不侵入业务代码的前提下实现透明化数据隔离。同时分析不同方案的适用场景、性能影响及安全考量,为企业级SaaS架构设计提供可落地的技术方案。
1. 背景介绍
1.1 目的和范围
随着云计算和SaaS模式的普及,多租户架构成为企业级应用的标配。多租户数据隔离要求在同一套应用系统中为多个租户提供独立的数据存储和访问控制,确保租户数据互不干扰。本文聚焦Java生态中最流行的持久层框架MyBatis,深入探讨三种主流数据隔离模式的技术实现,包括:
- 独立数据库模式(Database Per Tenant)
- 共享数据库独立Schema模式(Schema Per Tenant)
- 共享表字段级隔离模式(Column Per Tenant)
1.2 预期读者
本文适合以下读者群体:
- 负责SaaS架构设计的后端开发工程师
- 希望优化MyBatis数据访问层的技术负责人
- 对多租户数据隔离技术感兴趣的架构师
1.3 文档结构概述
全文分为三个技术模块:
- 基础理论:多租户核心概念、隔离模式对比、MyBatis技术特性
- 技术实现:动态数据源路由、SQL拦截器开发、语法解析优化
- 工程实践:Spring Boot整合案例、性能优化策略、安全加固方案
1.4 术语表
1.4.1 核心术语定义
- 多租户(Multi-Tenancy):一套系统同时为多个租户提供服务,租户间数据逻辑隔离
- 数据隔离(Data Isolation):确保租户数据在存储、访问、传输过程中的独立性
- 租户ID(Tenant ID):标识租户身份的唯一标识符,通常为UUID或数字ID
- 数据源(DataSource):数据库连接配置的抽象,包含URL、用户名、密码等信息
- SQL拦截器(SQL Interceptor):MyBatis插件机制,用于修改SQL执行前的语句
1.4.2 相关概念解释
- 透明化隔离:业务代码无需感知数据隔离逻辑,由底层框架自动处理
- 读写分离:结合多租户实现读库和写库的路由分发
- 弹性扩展:支持动态添加租户数据库或Schema,满足水平扩展需求
1.4.3 缩略词列表
缩写 | 全称 |
---|---|
DPT | Database Per Tenant |
SPS | Schema Per Tenant |
CPT | Column Per Tenant |
SQL | Structured Query Language |
JDBC | Java Database Connectivity |
2. 核心概念与联系
2.1 多租户数据隔离模式对比
2.1.1 三种隔离模式架构图
graph TD
A[多租户隔离模式] --> B[独立数据库(DPT)]
A --> C[共享数据库独立Schema(SPS)]
A --> D[共享表字段级(CPT)]
B --> B1[每个租户独立数据库]
B1 --> B2[物理隔离级别最高]
C --> C1[同一数据库不同Schema]
C1 --> C2[通过schema_name区分]
D --> D1[同一表添加tenant_id字段]
D1 --> D2[逻辑隔离级别最低]
2.1.2 核心技术对比表
维度 | DPT | SPS | CPT |
---|---|---|---|
隔离级别 | 物理隔离 | 逻辑隔离 | 逻辑隔离 |
资源利用率 | 低 | 中 | 高 |
扩展成本 | 高 | 中 | 低 |
跨租户查询 | 不支持 | 需跨Schema | 支持 |
SQL复杂度 | 低 | 中 | 高(需字段过滤) |
2.2 MyBatis技术生态适配性分析
2.2.1 MyBatis插件机制
MyBatis提供四大接口插件:
Interceptor
:拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler- 常用拦截点:
prepare
(连接获取前)、parameterize
(参数设置前)、query
(查询执行前)
2.2.2 动态数据源关键接口
public interface DataSourceRouter {
String determineDataSource(TenantContext tenantContext);
}
2.2.3 数据隔离核心流程
flowchart TD
用户请求 --> 获取租户ID[从请求头/线程变量获取tenantId]
获取租户ID --> 路由数据源[调用DataSourceRouter]
路由数据源 --> 拦截SQL[MyBatis插件解析SQL]
拦截SQL --> 添加隔离条件[DPT/SPS无需添加,CPT添加tenant_id=?]
添加隔离条件 --> 执行SQL[MyBatis执行修改后的SQL]
3. 核心实现原理与关键技术
3.1 动态数据源路由实现(DPT/SPS模式)
3.1.1 数据源容器设计
public class DynamicDataSource extends AbstractRoutingDataSource {
private Map<Object, DataSource> targetDataSources;
private DataSource defaultTargetDataSource;
@Override
protected Object determineCurrentLookupKey() {
TenantContext tenantContext = TenantContextHolder.get();
return tenantContext.getDataSourceKey(); // DPT返回databaseName,SPS返回schemaName
}
}
3.1.2 租户上下文管理
public class TenantContextHolder {
private static final ThreadLocal<TenantContext> CONTEXT = new ThreadLocal<>();
public static void set(TenantContext context) {
CONTEXT.set(context);
}
public static TenantContext get() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
public class TenantContext {
private String tenantId;
private String dataSourceKey; // 数据库名或Schema名
// Getter/Setter
}
3.2 SQL拦截器实现(CPT模式)
3.2.1 语法解析器设计
使用ANTLR4实现SQL解析:
public class SQLParser {
public static List<TableNode> parse(String sql) {
SQLLexer lexer = new SQLLexer(CharStreams.fromString(</