【问题标题】:Slow Performance with NamedParameterJdbcTemplateNamedParameterJdbcTemplate 性能缓慢
【发布时间】:2021-06-21 15:19:27
【问题描述】:

我有一个 Spring5 应用程序使用 NamedParameterJdbcTemplate 从 OracleDB 获取数据。

当我从 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, etd), activeEndDate) overlaps (fromDate, 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 和您的应用程序连接到同一个数据库服务器?
  • 是的,都连接到同一个 dB 服务器
  • 您是否在控制台中获取完整的结果?
  • 是的,在 java 应用程序和控制台上执行相同的查询,我得到相同的结果,但响应时间非常不同
  • 比较苹果和橙子。发生的情况是您还有一个将结果转换为对象的行映射器。根据数据量,这需要很多时间。还有你测量的时间是什么时候?只有 db 执行或 HTTP 页面的呈现。

标签: java sql oracle spring-data-jpa jdbctemplate


【解决方案1】:

请记住,在后台运行查询时,IntelliJ 可能正在执行某种优化 - 预取数据切片、使用缓存等。由于这些和其他原因,这类工具通常会提供比应用程序作为纯 Java 代码运行时更好的性能。

话虽如此,为了改进您的查询,您可以做几件事。

一方面,检查你的sql,我认为它可以通过不同的方式改进:

  • 摆脱IN 子句,它们的性能通常很差。改用 JOINS 或在您的示例中使用 EXISTS 子句。
  • 可能建议对以下列进行索引(无论如何要小心,这些列很多,索引维护成本很高,并且会影响运行插入、更新和删除操作时的性能,请尝试在查询和 DML 操作之间找到平衡点):
    • 组织 ID
    • 封闭Hire_organizationId
    • gln_orig_organizationid
    • gln_dest_organizationid
    • bbrorgid
    • 设施 ID
  • 您正在将nvl 和coalesce 之类的函数应用于您的日期:恐怕它们似乎是必要的,但请注意,它可能会阻止使用在该列上创建的任何索引。使用多个ORs 有时也不是最佳选择。
  • 我认为您可以简化与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 这样的简单工具可以粗略地检测grosso modo可能的瓶颈。

【讨论】:

    猜你喜欢
    • 2012-08-05
    • 2010-10-14
    • 2015-01-27
    • 2021-06-04
    • 2016-11-09
    • 2012-05-11
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多