JDBC 模板工作缓慢

Ber*_*kin 3 java jdbctemplate

我在使用 JDBCTemplate 从我的数据库中获取一行时遇到了性能问题。当我在 plsql 中运行 sql 时,我可以在 3 毫秒内获得结果,但代码中的相同查询大约需要 200 毫秒。我认为它运行缓慢,因为在运行查询之前,创建了一个连接并且我在其中浪费了太多时间。我想我需要一个连接池或 smt。别的

在这里写代码之前,想先说一下我的spring boot项目的流程。客户端调用我的端点,在这个调用中,我使用了来自多个表的多个查询。所有查询都运行缓慢,因为对于每个查询,都会创建另一个连接。

数据库配置类

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
Run Code Online (Sandbox Code Playgroud)

此功能最多需要 2 秒。

public interface CustomerInformationTempDao {
    String getSbuCodeByClientNo(Integer clientNo);
}
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案

更改 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Kar*_*cki 6

您可以采取以下措施来提高性能:

  1. 而不是使用DriverManagerDataSource定义具有固定连接计数的连接池。为了简化SingleConnectionDataSource不会执行任何借用连接验证的使用。这不是生产级设置,但您的测试中移动的部分会更少。
  2. 而不是SELECT *仅指定您需要的列。*JDBC 和命令行客户端的解析可能不同,最好删除这个未知的。
  3. 使用准备好的语句client_no = ?代替client_no=" + clientNo连接。在检查不同的客户端编号时,这将导致更好的查询计划缓存。

请注意,JVM 需要 10,000 次以上的方法调用才能启动 JIT 编译和优化。如果您有一个main()不预热 JVM 的简单代码,则代码会变慢。命令行客户端已经编译为本机代码。