### 前言
- 作为多租户系统,为了提升性能和数据的安全性,我们将商城的数据库(
joolun_plus_mall)进行了分库,即不同租户连不同的库,通过租户ID区别,如租户ID为1租户的数据库名为joolun_plus_mall_1
实现原理
- mybatisPlus多租户+动态数据源
- 请先查看【多租户功能】文档
实现逻辑
在TenantContextHolder自动维护租户ID时候添加一个自动切换维护数据源的操作,即可实现分库
核心代码TenantContextHolder.setTenantId
/**
* TTL 设置租户ID
*
* @param tenantId
*/
public void setTenantId(String tenantId) {
THREAD_LOCAL_TENANT.set(tenantId);
//分库实现,根据租户ID,切换当前租户的数据源
if(shardingDataBaseProperties != null && shardingDataBaseProperties.getEnabled() != null
&& shardingDataBaseProperties.getEnabled() && StrUtil.isNotBlank(tenantId) && !"null".equals(tenantId)){
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
//拼接当前租户的数据源名
String dsName;
if(CommonConstants.SYSTEM_TENANT_ID.equals(tenantId)){
dsName = shardingDataBaseProperties.getDbname();
}else{
dsName = StrUtil.format("{}_{}", shardingDataBaseProperties.getDbname(), tenantId);
}
String dbName = dsName;
//如数据源中没有该数据源,则添加
if(!ds.getDataSources().containsKey(dsName)) {
addDataSources(ds, dataSourceCreator, dsName, dbName, shardingDataBaseProperties);
}
//切换到当前租户数据源
DynamicDataSourceContextHolder.push(dsName);
}
}
/**
* 添加数据源,如数据源中没有该数据源,则添加
* @param dataSourceCreator
* @param dsName
* @param dbName
* @param shardingDataBaseProperties
* @return
*/
@Synchronized
public void addDataSources(DynamicRoutingDataSource ds, DefaultDataSourceCreator dataSourceCreator,
String dsName, String dbName, ShardingDataBaseProperties shardingDataBaseProperties){
if(!ds.getDataSources().containsKey(dsName)) {
//获取主库账号密码url
String pwd = shardingDataBaseProperties.getDbpassword();
String username = shardingDataBaseProperties.getDbusername();
String url = StrUtil.format(shardingDataBaseProperties.getUrl(), dbName);
//检查数据源是否可用
try {
DriverManager.getConnection(url, username, pwd);
} catch (Exception e) {
log.error("数据源不可用:"+e.getMessage());
throw new CheckedException("数据源不可用:"+e.getMessage());
}
DataSourceProperty dataSourceProperty = new DataSourceProperty();
dataSourceProperty.setPoolName(dsName);
dataSourceProperty.setPassword(pwd);
dataSourceProperty.setUsername(username);
dataSourceProperty.setUrl(url);
//添加数据源
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
ds.addDataSource(dataSourceProperty.getPoolName(), dataSource);
}
}
配置分库信息,如:joolun-mall-admin-dev.yml
spring.datasource.sharding配置的其实是基础库的库名、账号密码、和url,系统会根据基础库自动拼接库名joolun_plus_mall_租户ID维护数据源spring.datasource.dynamic多数据源配置,系统启动时需要连接一个库,这里直接用默认的即可;系统运行后会自动根据spring.datasource.sharding配置的基础库拼接租户对应的数据源
# 数据源配置
spring:
datasource:
# 分库信息
sharding:
enabled: true
dbname: joolun_plus_mall
dbusername: ${joolun.mysql.username}
dbpassword: ${joolun.mysql.password}
url: jdbc:mysql://${joolun.mysql.host}:${joolun.mysql.port}/{}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
# 多数据源,只设置主数据源,其他租户数据源,后期动态加载
dynamic:
primary: master
strict: false
datasource:
master:
username: ${joolun.mysql.username}
password: ${joolun.mysql.password}
url: jdbc:mysql://${joolun.mysql.host}:${joolun.mysql.port}/joolun_plus_upms?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
初始化租户数据库
- 上述我们通过mb多租户机制+动态数据源实现了根据当前租户ID自动切换数据源
- 在添加租户的时候我们还需给新租户生成数据库,
SysTenantServiceImpl.initTenantDataBase方法根据上述配置的spring.datasource.sharding.dbname基础库为新租户克隆一个新库 - 后台租户管理调用
已实现分库的数据库
- joolun_plus_mall
