1. Configure the connection information of the master database and slave database
# 主库配置
spring.datasource.master.jdbc-url=jdbc:mysql://ip:port/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.master.username=master
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
# 从库配置
spring.datasource.slave.jdbc-url=jdbc:mysql://ip:port/slave?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.slave.username=slave
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
2. Create data source configuration classes for the master database and slave database
public class MasterDataSourceConfiguration {
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
}
public class SlaveDataSourceConfiguration {
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
}
3. Create master-slave data source enumeration
public enum DataSourceTypeEnum {
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE,
;
}
4. Create a dynamic routing data source
4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
"${DB_RW_SEPARATE_SWITCH:false}") (
private boolean dbRwSeparateSwitch;
protected Object determineCurrentLookupKey() {
if(dbRwSeparateSwitch && DataSourceTypeEnum.SLAVE.equals(DataSourceContextHolder.getDataSourceType())) {
log.info("DynamicRoutingDataSource 切换数据源到从库");
return DataSourceTypeEnum.SLAVE;
}
log.info("DynamicRoutingDataSource 切换数据源到主库");
// 根据需要指定当前使用的数据源,这里可以使用ThreadLocal或其他方式来决定使用主库还是从库
return DataSourceTypeEnum.MASTER;
}
}
5. Create a dynamic data source configuration class
"spring.datasource.master.jdbc-url") (
public class DynamicDataSourceConfiguration {
"dataSource") (
public DataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DataSourceTypeEnum.SLAVE, slaveDataSource);
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
return dynamicDataSource;
}
}
6. Create the DatasourceContextHolder class and use ThreadLocal to store the data source type of the current thread.
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(DataSourceTypeEnum dataSourceType) {
contextHolder.set(dataSourceType);
}
public static DataSourceTypeEnum getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
7. Create custom annotations to mark master and slave data sources
public MasterDataSource {
}
public SlaveDataSource {
}
8. Create aspect classes, intercept database operations, and switch data source parameters according to annotation settings
public class DataSourceAspect {
"@annotation(xxx.MasterDataSource)") (
public void setMasterDataSource(JoinPoint joinPoint) {
DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.MASTER);
}
"@annotation(xxx.SlaveDataSource)") (
public void setSlaveDataSource(JoinPoint joinPoint) {
DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.SLAVE);
}
"@annotation(xxx.MasterDataSource) || @annotation(xxx.SlaveDataSource)") (
public void clearDataSource(JoinPoint joinPoint) {
DataSourceContextHolder.clearDataSourceType();
}
}
9. Use custom annotation tags on the Service layer method to query the data source
public class TestService {
private TestDao testDao;
public Test test() {
return testDao.queryByPrimaryKey(11L);
}
}
10. Exclude data source automatic configuration class
SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
1. Use connection pool, take Hikari as an example
# 主库配置
spring.datasource.master.jdbc-url=jdbc:mysql://ip:port/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.master.username=master
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.master.hikari.name=master
spring.datasource.master.hikari.minimum-idle=5
spring.datasource.master.hikari.idle-timeout=30
spring.datasource.master.hikari.maximum-pool-size=10
spring.datasource.master.hikari.auto-commit=true
spring.datasource.master.hikari.pool-name=DatebookHikariCP
spring.datasource.master.hikari.max-lifetime=1800000
spring.datasource.master.hikari.connection-timeout=30000
spring.datasource.master.hikari.connection-test-query=SELECT 1
# 从库配置
spring.datasource.slave.jdbc-url=jdbc:mysql://ip:port/slave?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.slave.hikari.name=master
spring.datasource.slave.hikari.minimum-idle=5
spring.datasource.slave.hikari.idle-timeout=30
spring.datasource.slave.hikari.maximum-pool-size=10
spring.datasource.slave.hikari.auto-commit=true
spring.datasource.slave.hikari.pool-name=DatebookHikariCP
spring.datasource.slave.hikari.max-lifetime=1800000
spring.datasource.slave.hikari.connection-timeout=30000
spring.datasource.slave.hikari.connection-test-query=SELECT 1
2. Integrate mybatis and force switch to the main library when writing
({
"update", args = {MappedStatement.class, Object.class}), (type = Executor.class, method =
})
public class WriteInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
// 获取 SQL 类型
DataSourceTypeEnum dataSourceType = DataSourceContextHolder.getDataSourceType();
if(DataSourceTypeEnum.SLAVE.equals(dataSourceType)) {
DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.MASTER);
}
try {
// 执行 SQL
return invocation.proceed();
} finally {
// 恢复数据源 考虑到写入后可能会反查,后续都走主库
// DataSourceContextHolder.setDataSourceType(dataSourceType);
}
}
}
This article is shared from the WeChat public account - JD Cloud Developers (JDT_Developers).
If there is any infringement, please contact [email protected] for deletion.
This article participates in the " OSC Source Creation Plan ". You who are reading are welcome to join and share together.
{{o.name}}
{{m.name}}