0%

多租户的实现

多租户概述

多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:在一台服务器上运行单个应用实例,它为多个租户提供服务。

数据隔离方案

  1. 独立数据库

    即一个租户一个数据库。

  2. 共享数据库,独立Schema

    即多个或所有租户共享Database,但是每个租户一个Schema(也可叫做一个user)。

  3. 共享数据库,共享Schema,共享数据表

    即租户共享同一个Database、同一个Schema,但在表中增加租户标识的数据字段。

实现方式

MySQL没用schema和database的区分,所以上述1、2两种方案大体一致。当前选择最低成本的共享数据库,共享Schema,共享数据表方案。后续讨论全部针对于方案3展开。

简单来说,意味着每条数据都需要区分出属于哪个租户。为了减少后期开发成本,需要为用户表以及用户相关的表全都增加租户标识的字段(如tenantId)。

此时涉及与租户相关的SQL都需要拼接tenantId = ?,若手动拼接,改造过程过于繁琐。此时我们想到的方案是根据当前用户所属的租户动态的拼接SQL,恰巧MyBatis-Plus为我们提供了这样功能的插件,仅需要简单的配置即可。

MyBatis-Plus 的多租户 SQL 解析器配置

参考官方文档及demo https://mybatis.plus/guide/tenant.html

此处贴一下核心配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@MapperScan("com.liuqitech.demo.dao")
@Configuration
public class MyBatisConfig {

/**
* 多租户的标识字段
*/
private static final String TENANT_ID_COLUMN = "TENANT_ID";

/**
* 忽略多租户的表名
*/
private static final List<String> IGNORE_TENANT_TABLES = Lists
.newArrayList("table1", "table2", "table3", "table4");

@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
TenantSqlParser tenantSqlParser = new TenantSqlParser();
tenantSqlParser.setTenantHandler(new TenantHandler() {
@Override
public Expression getTenantId(boolean where) {
// TODO 此处的tenantId需要自己获取当前用户所属的租户
String tenantId = "liuqitech";
return new StringValue(tenantId);
}

@Override
public String getTenantIdColumn() {
return TENANT_ID_COLUMN;
}

@Override
public boolean doTableFilter(String tableName) {
return IGNORE_TENANT_TABLES.stream().allMatch(e -> e.equalsIgnoreCase(tableName));
}
});
paginationInterceptor.setSqlParserList(Lists.newArrayList(tenantSqlParser));
return paginationInterceptor;
}

}