SpringBoot多数据源⽆法并发执⾏的问题
1 问题描述
在配置多数据源后,如果配置了定时任务,同时调⽤两个数据源的Mapper进⾏查询,会出现卡死的现象。
2 原因
猜测原因为,数据库源Bean并不是在创建完成后就连接数据库,⽽是在进⾏查询的时候才会连接,如果在某⼀时刻同时调⽤两个数据源进⾏查询,且两个数据源都没有进⾏⾸次连接,那么会造成卡死现象。
3 解决⽅法
在两个数据源的Bean初始化完成后,⼿动连接数据库,确保在调⽤前已经完成连接,这样就可以确保不会卡死。
4 相关排查指令
查询 Java 堆栈信息:jstack -l 线程号 > ⽂件名
作⽤:在项⽬卡住不动时,检查到疑似数据库连接卡死,猜测有可能是由于两个数据库同时连接导致卡死,
进⽽试验能否在 Bean 创建完成后就先连接数据库。
4 相关代码
数据源1(MySQL):
package;
import MybatisSqlSessionFactoryBean;
import Slf4j;
import SqlSessionFactory;
import SqlSessionTemplate;
import MapperScan;
import Qualifier;
import DataSourceBuilder;
import ConfigurationProperties;
import Bean;
import Configuration;
import PropertySource;
import JdbcTemplate;
import DataSourceTransactionManager;
import PostConstruct;
import Resource;
import DataSource;
/**
* 初始化 MySQL 数据库源
*/
@Configuration
@MapperScan(basePackages ="sql.dao", sqlSessionFactoryRef ="mysqlSqlSessionFactory")
@PropertySource({"classpath:jdbc.properties"})
@Slf4j
public class MysqlDataSourceConfig {
/**
* 必须指定是那个数据源,否则可能会错误注⼊别的数据源
*/
@Resource(name ="mysqlDataSource")
private DataSource dataSource;
/**
* 在创建完 Bean 后需要先建⽴连接,否则在后⾯多线程执⾏任务会卡死
*/
@PostConstruct
@PostConstruct
public void init()throws Exception {
log.info("[MySQL 数据库源] - 初始化完毕");
}
jdbctemplate查询一条数据@Bean(name ="mysqlDataSource")
@ConfigurationProperties(prefix ="sql")
public DataSource mysqlDataSource(){
ate().build();
}
// JdbcTemplate
@Bean(name ="mysqlJdbcTemplate")
public JdbcTemplate mysqlJdbcTemplate(@Qualifier("mysqlDataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
//事务管理
@Bean(name ="mysqlTransactionManager")
public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("mysqlDataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean(name ="mysqlSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource)throws Exception {
MybatisSqlSessionFactoryBean factoryBean =new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
Object();
}
@Bean(name ="mysqlSqlSessionTemplate")
public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory)throws Excepti on {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
数据源2(Oracle):
package;
import MybatisSqlSessionFactoryBean;
import Slf4j;
import SqlSessionFactory;
import SqlSessionTemplate;
import MapperScan;
import Qualifier;
import DataSourceBuilder;
import ConfigurationProperties;
import Bean;
import Configuration;
import Primary;
import PropertySource;
import JdbcTemplate;
import DataSourceTransactionManager;
import PostConstruct;
import Resource;
import DataSource;
/**
* 初始化 Oracle 数据库源
* 初始化 Oracle 数据库源
*/
@Configuration
@MapperScan(basePackages ={"acle.dao"}, sqlSessionFactoryRef ="oracleSqlSessionFactory")
@PropertySource({"classpath:jdbc.properties"})
@Slf4j
public class OracleDataSourceConfig {
/**
* 必须指定是那个数据源,否则可能会错误注⼊别的数据源
*/
@Resource(name ="oracleDataSource")
private DataSource dataSource;
/**
* 在创建完 Bean 后需要先建⽴连接,否则在后⾯多线程执⾏任务会卡死
*/
@PostConstruct
public void init()throws Exception {
log.info("[Oracle 数据库源] - 初始化完毕");
}
@Bean(name ="oracleDataSource")
@ConfigurationProperties(prefix ="acle")
@Primary
public DataSource oracleDataSource(){
ate().build();
}
// JdbcTemplate
@Bean(name ="oracleJdbcTemplate")
public JdbcTemplate oracleJdbcTemplate(@Qualifier("oracleDataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
//事务管理
@Bean(name ="oracleTransactionManager")
@Primary
public DataSourceTransactionManager oracleTransactionManager(@Qualifier("oracleDataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean(name ="oracleSqlSessionFactory")
MybatisSqlSessionFactoryBean masterSessionFactory(@Qualifier("oracleDataSource") DataSource dataSource)throws Exception {
MybatisSqlSessionFactoryBean mybatisPlus =new MybatisSqlSessionFactoryBean();
mybatisPlus.setDataSource(dataSource);
return mybatisPlus;
}
@Bean(name ="oracleSqlSessionTemplate")
@Primary
public SqlSessionTemplate oracleSqlSessionTemplate(@Qualifier("oracleSqlSessionFactory") SqlSessionFactory sqlSessionFactory)throws Excep tion {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
数据库配置⽂件:
# Mysql 数据源
sql.sql.cj.jdbc.Driver
sql.url=jdbc:mysql://x:3306/database?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOver ReadOnly=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false
sql.username=username
sql.password=password
# Oracle 数据源
# Oracle 数据源
acle.driver-class-name=oracle.jdbc.driver.OracleDriver
acle.url=jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = x)(PORT = 1650))(LOAD_ BALANCE = yes)(CONNECT_DATA =(SERVICE_NAME = xx))))
acle.username=username
acle.password=password
st-while-idle=true
st-on-Borrow=false
st-on-return=false
sql.validation-query=SELECT 1 FROM DUAL
sql.validation-query-timeout=1
sql.time-between-eviction-runs-millis=600000
sql.min-evictable-idle-time-millis=900000
sql.num-tests-per-eviction-run=100
st-while-idle=true
st-on-Borrow=false
st-on-return=false
acle.validation-query=SELECT 1 FROM DUAL
acle.validation-query-timeout=1
acle.time-between-eviction-runs-millis=600000
acle.min-evictable-idle-time-millis=900000
acle.num-tests-per-eviction-run=100