JDBC 模板运行缓慢
JDBC Template Works Slow
我在使用 JDBCTemplate 从我的数据库中获取一行时遇到性能问题。当我在 plsql 中 运行 sql 时,我可以在 3 毫秒内得到结果,但代码中的相同查询大约需要 200 毫秒。我认为它运行缓慢,因为在 运行ning 查询之前,创建了一个连接,我在其中浪费了太多时间。我想我需要一个连接池或 smt。其他
在这里写代码之前,我想谈谈我的 spring 引导项目的流程。客户端调用我的端点,在此调用中,我使用了来自多个表的多个查询。所有查询 运行 都很慢,因为对于每个查询,都会创建另一个连接。
数据库配置Class
@Configuration
public class DatabaseConfig {
@Autowired
private Environment env;
@Bean(name = "fraudDb")
public DataSource masterDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("driver-class-name"));
dataSource.setUrl(env.getProperty("fraud.url"));
dataSource.setUsername(env.getProperty("fraud.username"));
dataSource.setPassword(env.getProperty("fraud.password"));
return dataSource;
}
@Bean(name = "ndvliveDb")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("driver-class-name"));
dataSource.setUrl(env.getProperty("ndvlive.url"));
dataSource.setUsername(env.getProperty("ndvlive.username"));
dataSource.setPassword(env.getProperty("ndvlive.password"));
return dataSource;
}
@Bean(name = "fraudJdbcTemplate")
@Autowired
public JdbcTemplate masterJdbcTemplate(@Qualifier("fraudDb") DataSource fraudDb) {
return new JdbcTemplate(fraudDb);
}
@Bean(name = "ndvliveJdbcTemplate")
@Autowired
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("ndvliveDb") DataSource ndvliveDb) {
return new JdbcTemplate(ndvliveDb);
}
}
RestController 中的查询
private RbtranServiceInputModel services(FraudActionsRestRequest fraudActionsRestRequest) {
Long start = System.nanoTime();
String debitSegmentId = ndvliveCustomerInfoService.getSBUCode(Integer.parseInt(cifNoSender));
Long end = System.nanoTime();
System.out.println("debitSegmentId " + (end - start) / 1e6);
//10 ms - 20 ms
start = System.nanoTime();
String debitName = ndvliveCustomerInfoService.getNameSurname(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("debitName " + (end - start) / 1e6);
//3 ms
start = System.nanoTime();
ResponseGetBMSInfo bmsInfo = ndvliveCustomerInfoService.getOnlineCustomerInfo(new BigDecimal(cifNoSender));
end = System.nanoTime();
System.out.println("bmsInfo " + (end - start) / 1e6);
//10 MS
start = System.nanoTime();
Date passwordChangeDate = ndvliveCustomerInfoService.getPasswordChangeDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("passwordChangeDate " + (end - start) / 1e6);
//10ms
start = System.nanoTime();
Date smartSmsGsmNoRegistrationDate = ndvliveCustomerInfoService.getSmartSmsGsmNoRegistrationDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("smartSmsGsmNoRegistrationDate " + (end - start) / 1e6);
//6 ms
start = System.nanoTime();
Date membershipDate = ndvliveCustomerInfoService.getMembershipDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("membershipDate " + (end - start) / 1e6);
start = System.nanoTime();
BigDecimal smartSmsNo = ndvliveCustomerInfoService.getSmsGsmNo(new BigDecimal(cifNoSender));//TODO sms;
end = System.nanoTime();
System.out.println("smartSmsNo " + (end - start) / 1e6);
start = System.nanoTime();
String habitInfo = ndvliveCustomerInfoService.getHabitInfo(new BigDecimal(cifNoSender), channel);
end = System.nanoTime();
System.out.println("habitInfo " + (end - start) / 1e6);
start = System.nanoTime();
Date lastBlockSimDate = ndvliveCustomerInfoService.getLastBlockSimDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("lastBlockSimDate " + (end - start) / 1e6);
start = System.nanoTime();
boolean isFamiliar = ndvliveCustomerAccountInfoService.getFamiliarAccount(new BigDecimal(fraudActionsRestRequest.getDebitCustomerId()), fraudActionsRestRequest.getProcessCode(), fraudActionsRestRequest.getCreditAcctNumber(), fraudActionsRestRequest.getSmartSmsNo());
end = System.nanoTime();
System.out.println("isFamiliar " + (end - start) / 1e6);
rbtranServiceInputModel.setKnownAccount(isFamiliar);
start = System.nanoTime();
boolean isInWhitelist = ndvliveDeviceInfoService.isInWhiteList(Long.parseLong(fraudActionsRestRequest.getDebitCustomerId()), fraudActionsRestRequest.getUserData02(), fraudActionsRestRequest.getDeviceId());
end = System.nanoTime();
System.out.println("isInWhitelist " + (end - start) / 1e6);
rbtranServiceInputModel.setInWhitelist(isInWhitelist);
}
此函数最多需要 2 秒。
DAO
public interface CustomerInformationTempDao {
String getSbuCodeByClientNo(Integer clientNo);
}
DAOImpl
@Repository
public class CustomerInformationTempDaoImpl implements CustomerInformationTempDao {
@Autowired
@Qualifier("ndvliveJdbcTemplate")
private JdbcTemplate ndvliveJdbcTemplate;
public String getSbuCodeByClientNo(Integer clientNo) {
String query = "SELECT * FROM mytable WHERE client_no=" + clientNo;
try {
return ndvliveJdbcTemplate.queryForObject(query, (resultSet, i) -> resultSet.getString("SBU_CODE"));
} catch (EmptyResultDataAccessException e) {
return null;
}
}
}
解决方案
正在更改 DriverManagerDataSource
@Configuration
public class DatabaseConfig {
@Autowired
private Environment env;
@Autowired
private DataSourceProperties dataSourceProperties;
@Bean(name = "fraudDb")
public DataSource masterDataSource() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.dataSourceProperties.getClassLoader())
.driverClassName(env.getProperty("driver-class-name"))
.url(env.getProperty("fraud.url"))
.username(env.getProperty("fraud.username"))
.password(env.getProperty("fraud.password"));
return factory.build();
}
@Bean(name = "ndvliveDb")
public DataSource secondDataSource() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.dataSourceProperties.getClassLoader())
.driverClassName(env.getProperty("driver-class-name"))
.url(env.getProperty("ndvlive.url"))
.username(env.getProperty("ndvlive.username"))
.password(env.getProperty("ndvlive.password"));
return factory.build();
}
@Bean(name = "fraudJdbcTemplate")
@Autowired
public JdbcTemplate masterJdbcTemplate(@Qualifier("fraudDb") DataSource fraudDb) {
return new JdbcTemplate(fraudDb);
}
@Bean(name = "ndvliveJdbcTemplate")
@Autowired
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("ndvliveDb") DataSource ndvliveDb) {
return new JdbcTemplate(ndvliveDb);
}
}
您可以采取一些措施来提高性能:
- 而不是使用
DriverManagerDataSource
定义一个具有固定连接数的连接池。为了简化使用 SingleConnectionDataSource
,它不会执行任何借用连接验证。这不是生产级设置,但您的测试中的移动部件会更少。
- 而不是
SELECT *
仅指定您需要的列。解析 *
在 JDBC 和命令行客户端之间可能不同,最好删除这个未知数。
- 使用
client_no = ?
而不是 client_no=" + clientNo
连接的预准备语句。这将在检查不同的客户端数量时产生更好的查询计划缓存。
请注意,JVM 需要 10,000 次以上的方法调用才能开始 JIT 编译和优化。如果您有一个不预热 JVM 的简单 main()
,代码将会变慢。命令行客户端已编译为本机代码。
我在使用 JDBCTemplate 从我的数据库中获取一行时遇到性能问题。当我在 plsql 中 运行 sql 时,我可以在 3 毫秒内得到结果,但代码中的相同查询大约需要 200 毫秒。我认为它运行缓慢,因为在 运行ning 查询之前,创建了一个连接,我在其中浪费了太多时间。我想我需要一个连接池或 smt。其他
在这里写代码之前,我想谈谈我的 spring 引导项目的流程。客户端调用我的端点,在此调用中,我使用了来自多个表的多个查询。所有查询 运行 都很慢,因为对于每个查询,都会创建另一个连接。
数据库配置Class
@Configuration
public class DatabaseConfig {
@Autowired
private Environment env;
@Bean(name = "fraudDb")
public DataSource masterDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("driver-class-name"));
dataSource.setUrl(env.getProperty("fraud.url"));
dataSource.setUsername(env.getProperty("fraud.username"));
dataSource.setPassword(env.getProperty("fraud.password"));
return dataSource;
}
@Bean(name = "ndvliveDb")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("driver-class-name"));
dataSource.setUrl(env.getProperty("ndvlive.url"));
dataSource.setUsername(env.getProperty("ndvlive.username"));
dataSource.setPassword(env.getProperty("ndvlive.password"));
return dataSource;
}
@Bean(name = "fraudJdbcTemplate")
@Autowired
public JdbcTemplate masterJdbcTemplate(@Qualifier("fraudDb") DataSource fraudDb) {
return new JdbcTemplate(fraudDb);
}
@Bean(name = "ndvliveJdbcTemplate")
@Autowired
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("ndvliveDb") DataSource ndvliveDb) {
return new JdbcTemplate(ndvliveDb);
}
}
RestController 中的查询
private RbtranServiceInputModel services(FraudActionsRestRequest fraudActionsRestRequest) {
Long start = System.nanoTime();
String debitSegmentId = ndvliveCustomerInfoService.getSBUCode(Integer.parseInt(cifNoSender));
Long end = System.nanoTime();
System.out.println("debitSegmentId " + (end - start) / 1e6);
//10 ms - 20 ms
start = System.nanoTime();
String debitName = ndvliveCustomerInfoService.getNameSurname(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("debitName " + (end - start) / 1e6);
//3 ms
start = System.nanoTime();
ResponseGetBMSInfo bmsInfo = ndvliveCustomerInfoService.getOnlineCustomerInfo(new BigDecimal(cifNoSender));
end = System.nanoTime();
System.out.println("bmsInfo " + (end - start) / 1e6);
//10 MS
start = System.nanoTime();
Date passwordChangeDate = ndvliveCustomerInfoService.getPasswordChangeDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("passwordChangeDate " + (end - start) / 1e6);
//10ms
start = System.nanoTime();
Date smartSmsGsmNoRegistrationDate = ndvliveCustomerInfoService.getSmartSmsGsmNoRegistrationDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("smartSmsGsmNoRegistrationDate " + (end - start) / 1e6);
//6 ms
start = System.nanoTime();
Date membershipDate = ndvliveCustomerInfoService.getMembershipDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("membershipDate " + (end - start) / 1e6);
start = System.nanoTime();
BigDecimal smartSmsNo = ndvliveCustomerInfoService.getSmsGsmNo(new BigDecimal(cifNoSender));//TODO sms;
end = System.nanoTime();
System.out.println("smartSmsNo " + (end - start) / 1e6);
start = System.nanoTime();
String habitInfo = ndvliveCustomerInfoService.getHabitInfo(new BigDecimal(cifNoSender), channel);
end = System.nanoTime();
System.out.println("habitInfo " + (end - start) / 1e6);
start = System.nanoTime();
Date lastBlockSimDate = ndvliveCustomerInfoService.getLastBlockSimDate(new BigDecimal(cifNoSender), userId);
end = System.nanoTime();
System.out.println("lastBlockSimDate " + (end - start) / 1e6);
start = System.nanoTime();
boolean isFamiliar = ndvliveCustomerAccountInfoService.getFamiliarAccount(new BigDecimal(fraudActionsRestRequest.getDebitCustomerId()), fraudActionsRestRequest.getProcessCode(), fraudActionsRestRequest.getCreditAcctNumber(), fraudActionsRestRequest.getSmartSmsNo());
end = System.nanoTime();
System.out.println("isFamiliar " + (end - start) / 1e6);
rbtranServiceInputModel.setKnownAccount(isFamiliar);
start = System.nanoTime();
boolean isInWhitelist = ndvliveDeviceInfoService.isInWhiteList(Long.parseLong(fraudActionsRestRequest.getDebitCustomerId()), fraudActionsRestRequest.getUserData02(), fraudActionsRestRequest.getDeviceId());
end = System.nanoTime();
System.out.println("isInWhitelist " + (end - start) / 1e6);
rbtranServiceInputModel.setInWhitelist(isInWhitelist);
}
此函数最多需要 2 秒。
DAO
public interface CustomerInformationTempDao {
String getSbuCodeByClientNo(Integer clientNo);
}
DAOImpl
@Repository
public class CustomerInformationTempDaoImpl implements CustomerInformationTempDao {
@Autowired
@Qualifier("ndvliveJdbcTemplate")
private JdbcTemplate ndvliveJdbcTemplate;
public String getSbuCodeByClientNo(Integer clientNo) {
String query = "SELECT * FROM mytable WHERE client_no=" + clientNo;
try {
return ndvliveJdbcTemplate.queryForObject(query, (resultSet, i) -> resultSet.getString("SBU_CODE"));
} catch (EmptyResultDataAccessException e) {
return null;
}
}
}
解决方案
正在更改 DriverManagerDataSource
@Configuration
public class DatabaseConfig {
@Autowired
private Environment env;
@Autowired
private DataSourceProperties dataSourceProperties;
@Bean(name = "fraudDb")
public DataSource masterDataSource() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.dataSourceProperties.getClassLoader())
.driverClassName(env.getProperty("driver-class-name"))
.url(env.getProperty("fraud.url"))
.username(env.getProperty("fraud.username"))
.password(env.getProperty("fraud.password"));
return factory.build();
}
@Bean(name = "ndvliveDb")
public DataSource secondDataSource() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.dataSourceProperties.getClassLoader())
.driverClassName(env.getProperty("driver-class-name"))
.url(env.getProperty("ndvlive.url"))
.username(env.getProperty("ndvlive.username"))
.password(env.getProperty("ndvlive.password"));
return factory.build();
}
@Bean(name = "fraudJdbcTemplate")
@Autowired
public JdbcTemplate masterJdbcTemplate(@Qualifier("fraudDb") DataSource fraudDb) {
return new JdbcTemplate(fraudDb);
}
@Bean(name = "ndvliveJdbcTemplate")
@Autowired
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("ndvliveDb") DataSource ndvliveDb) {
return new JdbcTemplate(ndvliveDb);
}
}
您可以采取一些措施来提高性能:
- 而不是使用
DriverManagerDataSource
定义一个具有固定连接数的连接池。为了简化使用SingleConnectionDataSource
,它不会执行任何借用连接验证。这不是生产级设置,但您的测试中的移动部件会更少。 - 而不是
SELECT *
仅指定您需要的列。解析*
在 JDBC 和命令行客户端之间可能不同,最好删除这个未知数。 - 使用
client_no = ?
而不是client_no=" + clientNo
连接的预准备语句。这将在检查不同的客户端数量时产生更好的查询计划缓存。
请注意,JVM 需要 10,000 次以上的方法调用才能开始 JIT 编译和优化。如果您有一个不预热 JVM 的简单 main()
,代码将会变慢。命令行客户端已编译为本机代码。