JooLun Plus 百科 ⬇ 源码下载

03.06 多租户分库

JooLun 于 2026-10-21 14:40:14 +08:00 修改了此页面

### 前言

  • 作为多租户系统,为了提升性能和数据的安全性,我们将商城的数据库(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