[자바성능튜닝이야기] 12. DB를 사용하면서 발생 가능한 문제점들

12. DB를 사용하면서 발생 가능한 문제점들

DB Connection과 Connection Pool, DataSource

JDBC 관련 API는 클래스가 아니라 인터페이스다. JDK의 API에 있는 java.sql 인터페이스를 각 DB 벤더에서 상황에 맞게 구현하도록 되어 있다. 같은 인터페이스라고 해도, 각 DB 벤더에 따라서 처리되는 속도나 내부 처리 방식은 상이하다.

Connection 객체를 생성하는 부분에서 발생하는 대기 시간을 줄이고, 네트워크의 부담을 줄이기 위해서 사용하는 것이 DB Connection Pool이다.

Statement와 PreparedStatement의 가장 큰 차이점은 캐시(cache) 사용 여부이다. Statement를 사용할 때와 PreparedStatement를 처음 사용할 때는 다음과 같은 프로세스를 거친다.

  1. 쿼리 문장 분석
  2. 컴파일
  3. 실행

Statement를 사용하면 매번 쿼리를 수행할 때마다 1~3 단계를 거치고, PreparedStatement는 처음 한 번만 세 단계를 거친 후 캐시에 담아서 재사용한다. 동일한 쿼리를 반복적으로 수행한다면 PreparedStatement가 DB에 훨씬 적은 부하를 주며, 성능도 좋다.

DB를 사용할 때 닫아야 하는 것들

ResultSet 객체가 닫히는 경우는 다음과 같다.

GC가 되면 자동으로 닫히고, Statement 객체가 close되면 알아서 닫히지만, 0.00001초라도 빨리 닫으면 그만큼 해당 DB 서버의 부담이 적어지게 된다.

Conenction 객체는 다음 세 가지 경우에 닫히게 된다.

connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
  ...
} catch (Exception e) {
  ...
} finally {
  try { rs.close(); } catch (Exception rse) {}
  try { ps.close(); } catch (Exception pse) {}
  try { conn.close(); } catch (Exception conne) {}
}

JDK 7에서 등장한 AutoClosable 인터페이스

try 블록이 시작될 때 소괄호 안에 close() 메서드를 호출하는 객체를 생성해 주면 간단하게 처리할 수 있다.

public String readFileNew(String fileName) throws IOException {
  FileReader reader = new FileReader(new FIle(fileName));
  try (BufferedReader br = new BufferedReader(reader)) {
    return br.readLine();
  }
}

별도로 finally 블록에서 close() 메서드를 호출할 필요가 없어졌다. 만약 close() 메서드 호출 대상이 여러 개라면 세미콜론으로 구분하여 try-with-resources 구문에 두 개 이상의 문장을 추가하면 된다.

JDBC를 사용하면서 유의할 만한 몇 가지 팁