SAAS下一个mysql实例多个租户的库
在多租户环境中,根据tenantCode
字段动态切换数据库是常见的需求。这里将展示如何在Spring Boot和MyBatis项目中实现这一功能,具体步骤包括配置数据源、定义数据源路由逻辑以及在业务代码中使用。
1. 配置数据源
首先,你需要为你的应用配置一个主数据源,这个数据源将被用于连接到包含所有租户数据库的MySQL实例。然后,创建一个动态数据源,该数据源将根据tenantCode
来决定使用哪个具体的数据库。
主数据源配置
@Configuration @EnableTransactionManagement public class DataSourceConfig { @Bean(name = "primaryDataSource") public DataSource primaryDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306"); // 注意这里只提供实例URL,不指定数据库 dataSource.setUsername("your_username"); dataSource.setPassword("your_password"); return dataSource; } @Bean public DynamicTenantDataSource dynamicTenantDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource) { return new DynamicTenantDataSource(primaryDataSource); } }
2. 定义动态数据源路由逻辑
接下来,你需要创建一个DynamicTenantDataSource
类,它将继承AbstractRoutingDataSource
并实现数据源的动态切换逻辑。此外,还需要一个TenantContext
类来存储当前线程的tenantCode
。
public class DynamicTenantDataSource extends AbstractRoutingDataSource { private final Map<String, DataSource> tenantDataSources = new HashMap<>(); public DynamicTenantDataSource(DataSource defaultDataSource) { super.setDefaultTargetDataSource(defaultDataSource); } public void addTenantDataSource(String tenantId, String databaseName) { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/" + databaseName); dataSource.setUsername("your_username"); dataSource.setPassword("your_password"); tenantDataSources.put(tenantId, dataSource); } @Override protected Object determineCurrentLookupKey() { return TenantContext.getTenantCode(); } public void removeTenantDataSource(String tenantId) { tenantDataSources.remove(tenantId); } } public class TenantContext { private static final ThreadLocal<String> tenantCodeHolder = new ThreadLocal<>(); public static void setTenantCode(String tenantCode) { tenantCodeHolder.set(tenantCode); } public static String getTenantCode() { return tenantCodeHolder.get(); } public static void clearTenantCode() { tenantCodeHolder.remove(); } }
3. 在业务代码中使用
现在,你可以在业务代码中根据tenantCode
来切换数据源了。通常情况下,这会在服务层或过滤器中完成,以确保在处理请求之前设置正确的tenantCode
,并在请求结束后清除它。
设置tenantCode
@Service public class TenantService { @PostConstruct public void init() { ((DynamicTenantDataSource) dataSource()).addTenantDataSource("tenant1", "tenant_db_1"); ((DynamicTenantDataSource) dataSource()).addTenantDataSource("tenant2", "tenant_db_2"); } @Autowired private DataSource dataSource; public void processRequest(String tenantCode) { TenantContext.setTenantCode(tenantCode); try { // 执行数据库操作 } finally { TenantContext.clearTenantCode(); } } }
注意事项
- 确保在请求结束时清除
tenantCode
,避免影响后续请求。 - 如果有多个线程同时访问,需要保证
DynamicTenantDataSource
的线程安全。 - 在实际部署时,
tenantCode
和数据库名称的映射可能需要从配置文件或外部服务读取,而不是硬编码在代码中。
收藏文章数量从多到少与“把书读薄”是一个道理