问题描述
我有一个使用 NamedParameterJdbcTemplate
从 OracleDB 获取数据的 Spring5 应用程序。
当我从 intellij DB 控制台执行相同的查询时,我会在 1.5 秒内得到结果,但是当我使用 Java 应用程序执行相同的查询时
jdbcTemplate.queryForList(query,params);
需要 90 秒
这是 DBconfigration 类:
@Configuration
@EnableJpaRepositories("com.xxxx.xxx.relational.repositories")
@PropertySource(value = "classpath:jdbc.properties")
@ComponentScan
@EnableTransactionManagement
@Import(RestTemplateConfig.class)
public class RelationalConfig {
@Bean(destroyMethod = "close")
public BasicDataSource dataSource(@Value("${jdbc.driverClassName}") String driverClassName,@Value("${jdbc.url}") String url,@Value("${jdbc.username}") String username,@Value("${jdbc.password}") String password) {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
@Bean
HibernateJpavendorAdapter hibernateJpavendorAdapter() {
return new HibernateJpavendorAdapter();
}
@Bean
@Autowired
LocalContainerEntityManagerfactorybean entityManagerFactory(BasicDataSource dataSource,HibernateJpavendorAdapter vendor,@Value("${hibernate.dialect}") String dialect,@Value("${jdbc.schema}") String schema) {
LocalContainerEntityManagerfactorybean entityManagerfactorybean = new LocalContainerEntityManagerfactorybean();
entityManagerfactorybean.setDataSource(dataSource);
entityManagerfactorybean.setJpavendorAdapter(vendor);
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect",dialect);
jpaProperties.put("hibernate.default_schema",schema);
// jpaProperties.put("hibernate.jdbc.fetch_size",200);
// jpaProperties.put("hibernate.connection.pool_size",10);
entityManagerfactorybean.setJpaProperties(jpaProperties);
entityManagerfactorybean.setPackagesToScan("com.xxx.xxx.relational.entities");
return entityManagerfactorybean;
}
@Bean
@Autowired
JpaTransactionManager transactionManager(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
@Bean
@Autowired
NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setFetchSize(200);
return new NamedParameterJdbcTemplate(jdbcTemplate);
}
@Bean
@Autowired
SiblingsRepo<LegDTO> legSiblingsRepo(LegRepository legRepo) {
return new SiblingsRepo<LegDTO>() {
@Override
public Optional<LegDTO> byNext(LegDTO next) {
return legRepo.findByNextLegId(next.getId());
}
@Override
public Optional<LegDTO> byId(Integer id) {
return legRepo.findById(id);
}
@Override
public LegDTO save(LegDTO sibling) {
return legRepo.save(sibling);
}
};
}
@Bean
@Autowired
SiblingsRepo<journeyDTO> journeySiblingsRepo(journeyRepository journeyRepo) {
return new SiblingsRepo<journeyDTO>() {
@Override
public Optional<journeyDTO> byNext(journeyDTO next) {
return journeyRepo.findByNextjourneyId(next.getId());
}
@Override
public Optional<journeyDTO> byId(Integer id) {
return journeyRepo.findById(id);
}
@Override
public journeyDTO save(journeyDTO sibling) {
return journeyRepo.save(sibling);
}
};
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
这是查询:
String q = "select journeyid,extid,extccuid
from ACTIVE_journey_VIEW
WHERE coalesce(ata,nextjourneyEtd,sysdate + 1) > sysdate
AND ((organizationid = :orgId AND deleted = 0)
OR organizationid = :orgId
OR enclosingHire_organizationId = :orgId
OR gln_orig_organizationid = :orgId
OR originFacilityId in (
select facilityid
from v_FacilityAndBbrorg
where bbrorgid = :orgId
and (nvl(atd,etd),activeEndDate) overlaps (fromDate,toDate))
OR gln_dest_organizationid = :orgId
OR destinationFacilityId in (
select facilityid
from v_FacilityAndBbrorg
where bbrorgid = :orgId
and (nvl(atd,toDate))
)";
protected <T> List<T> query(String key,RowMapper<T> mapper,Object... kvs) {
Map<String,Object> params = map(kvs);
return jdbcTemplate.queryForList(q,params);
}
解决方法
请记住,在运行查询时,IntelliJ 可能在幕后执行某种优化 - 预取数据切片、使用缓存等。由于这些和其他原因,当应用程序作为纯 Java 代码运行时,这种工具通常会提供更好的性能。
话虽如此,为了改进您的查询,您可以做几件事。
一方面,回顾一下你的sql,我认为可以通过不同的方式改进:
- 去掉
IN
子句,它们的性能通常很差。使用 JOINS 或者在您的示例中使用EXISTS
子句。 - 可能建议对以下列进行索引(无论如何要小心,这些列很多,并且索引维护可能很昂贵并且在运行插入、更新和删除操作时会影响性能,请尝试在查询和 DML 操作之间找到平衡):
- 组织 ID
- 封闭Hire_organizationId
- gln_orig_organizationid
- gln_dest_organizationid
- bbrorgid
- 设施标识
- 您正在对日期应用
nvl
和coalesce
之类的函数:恐怕它们似乎是必要的,但请注意,它可能会阻止使用在该列上创建的任何索引。使用多个OR
有时也不是最佳选择。 - 我认为您可以简化与
v_FacilityAndBbrOrg
表相关的查询。
总而言之,请尝试使用与此类似的查询:
String q = "select journeyid,extid,extccuid
from ACTIVE_JOURNEY_VIEW
WHERE coalesce(ata,nextJourneyEtd,sysdate + 1) > sysdate
AND ((organizationid = :orgId AND deleted = 0)
OR organizationid = :orgId
OR enclosingHire_organizationId = :orgId
OR gln_orig_organizationid = :orgId
OR gln_dest_organizationid = :orgId
OR EXISTS (
select 1
from v_FacilityAndBbrOrg
where bbrorgid = :orgId
and (facilityid = originFacilityId OR facilityid = destinationFacilityId)
and (nvl(atd,etd),activeEndDate) overlaps (fromDate,toDate)
)
)";
此外,尝试使用不同的提取大小对您的应用程序进行基准测试。您指出 200
记录似乎很合理。当然,无论如何,根据您的应用程序的可用内存以及行大小等其他方面,尝试增加该数字:最多 1000
条记录的数量就足够了。
最后,尝试衡量您的应用程序何时消耗您的时间,无论是查询数据库、获取结果并转换记录、序列化为 JSON 还是呈现服务器端页面。当然,为此您有多种选择,但乍一看,使用 StopWatch
之类的简单工具可用于粗略地检测粗略可能的瓶颈。 >