JDBC Template Works Slow

Berkin :

I am having performance problem while using JDBCTemplate to fetch a row from my database. When I run sql in plsql, I can get the result in 3 milliseconds but same query in the code works around 200 ms. I think that it works slow because before running query, a connection created and I lost too many time in it. I guess I need a connection pool or smt. else

Before writing code here, I want to talk about my spring boot project's flow. Client calls my endpoint and in this call, I am using multiple queries from multiple tables. All queries runs slow because for each query, another connection creates.

Database Config 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);
    }
}

Queries In 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);
    }

This function takes up to 2 seconds.

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;
        }
    }
}

SOLUTION

Changing 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);
    }
}
Karol Dowbecki :

There are few things you can do to improve the performance:

  1. Instead of using DriverManagerDataSource define a connection pool with a fixed connection count. To simplify use SingleConnectionDataSource which will not perform any on-borrow connection validation. It's not a production grade setup but there will be less moving pieces in your test.
  2. Instead of SELECT * specify only the columns that you need. Resolving * could be different between JDBC and command line client, best to remove this unknown.
  3. Use prepared statements with client_no = ? instead of client_no=" + clientNo concatenation. This will result in better query plan caching when checking different client numbers.

Do note that JVM requires 10,000+ invocations of a method to start JIT compiling and optimizing. If you have a simple main() that doesn't warm up the JVM the code will be slower. The command line client is compiled to native code already.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=145020&siteId=1