JDBC 동작원리

Dev·2023년 3월 5일
0

1. JDBC

  • Java Database Connectivity 자바와 데이터베이스를 연결하기 위한 JAVA 표준 SQL 인터페이스다. DBMS의 종류(Mysql, MariaDB, Oracle 등)에 상관없이 JVM 위에서 동작하는 모든 애플리케이션들이 JDBC를 통해 표준 인터페이스로 DBMS에 연결할 수 있다. 이때, JDBC의 구현체는 각 DB의 Driver며 소켓을 통해 DBMS와 통신한다.
  • 드라이버에 연결할 Connection 객체 , 쿼리 수행을 위한 PreparedStatement 객체 , DB Record 결과를 담을 ResultSet 객체로 구성되며, 각 객체는 close() 메서드를 통해 닫아줘야 메모리 누수가 생기지 않는다.
  • 사용자가 많은 서비스에서 디비 연결이 있을 때마다 아래와같이 '연결'하고, '해제'하는 반복적인 작업을 한다면 번거롭고, 효율성이 굉장히 떨어지는 과정이다. 이러한 문제를 해결하고자 Connection Pool을 사용한다.
Connection conn = null;
PreparedStatement  pstmt = null;
ResultSet rs = null;

try {
    sql = "SELECT * FROM T_BOARD"

    // 1. 드라이버 연결 DB 커넥션 객체를 얻음
    connection = DriverManager.getConnection(DBURL, DBUSER, DBPASSWORD);

    // 2. 쿼리 수행을 위한 PreparedStatement 객체 생성
    // 참고로 Statement가 아닌 PreparedStatement를 사용하는 이유는 '보안성'과 '캐시'를 활용하기 때문
    pstmt = conn.createStatement();

    // 3. executeQuery: 쿼리 실행 후
    // ResultSet: DB 레코드 ResultSet에 객체에 담김
    rs = pstmt.executeQuery(sql);
    } catch (Exception e) {
    } finally {
        conn.close();
        pstmt.close();
        rs.close();
    }
}

2. DBCP

  • Database Connection Pool : Pool 속에 디비와 연결할 연결 객체(Connection)를 미리 여러 개 만들어둔다. 디비에 접근 요청이 왔을 때, 그 중 하나의 연결 객체(Connection)를 할당하여 사용하고, 사용이 종료되면 연결 객체를 반납하는 방식이다.
  • 커넥션을 계속해서 재 사용하기 때문에 생성되는 최대 커넥션 수를 제한할 수 있으며, Connection Pool의 Connection이 없을 경우 사용자는 커넥션이 반활될 때까지 순서대로 대기한다. 이때 지정한 'Timeout' 시간이 만료되면 예외를 던진다.

3. WAS와 DBMS의 통신 시 Timeout 계층

  • 상위 레벨의 Timeout은 하위 레벨의 Timeout에 의존성을 가진다. 예를 들어, JDBC Driver SocketTimeout이 정상으로 동작하지 않으면, 그 보다 상위 레벨의 Timeout인 StatementTimeout, Transaction Timeout도 정상적으로 동작하지 않는다.

[1] TransactionTimeout

  • 프레임워크(Spring, EJB Container etc)나 애플리케이션 레벨에서 유효한 타임아웃이다.
  • 여러 쿼리를 트랜젝션으로 묶고 수행할 때, 지정된 시간을 초과하면 발생한다.
  • Spring에서 제공하는 TransactionTimeout은 매우 단순하다. 해당 Transaction의 시작 시간과 경과 시간을 기록하면서, 특정 이벤트 발생 시 경과 시간을 확인하여 타임아웃 이상일 경우 예외(Exception)를 발생하도록 하고 있다.

[2] StatementTimeout

  • Statement 하나가 얼마나 오래 수행되어도 괜찮은지에 대한 한계 값이다. JDBC API인 Statement에 타임아웃 값을 설정하며, 이 값을 바탕으로 JDBC 드라이버가 StatementTimeout을 처리한다. JDBC API인 java.sql.Statement.setQueryTimeout(int timeout) 메서드로 설정한다. 즉, 쿼리 하나를 수행할 때, 지정된 시간을 초과하면 발생한다.
  • iBatis를 예로 들어 설명하자면 "sql-map-config.xml" 파일의 sqlMapConfig/settings에 @defaultStatementTimeout 값으로 기본값을 설정할 수 있다. 또한 "sql-map.xml" 파일의 statement, select, insert, update 구문마다 @timeout 값으로 개별적으로 설정할 수 있다.

MySQL JDBC Statement(5.0.8 버전)의 QueryTimeout

  1. Connection.createStatement() 메서드를 호출하여 Statement를 생성한다.
  2. Statement.executeQuery() 메서드를 호출한다.
  3. Statement는 내부 Connection을 사용하여 MySQL DBMS로 쿼리를 전송한다.
  4. Statement는 타임아웃 처리를 위해 새로운 타임아웃 처리용 스레드를 생성한다. 5.1.x 버전에서는 Connection에 한 개의 스레드가 할당되는 것으로 변경되었다.
  5. 스레드에 타임아웃 처리를 등록한다.
  6. 타임아웃이 발생한다.
  7. 타임아웃 처리 스레드가 Statement와 동일한 설정의 Connection을 생성한다.
  8. 생성된 Connection을 사용하여 취소 쿼리(KILL QUERY "connectionId")를 전송한다.

[3] JDBC Driver SocketTimeout

  • JDBC 드라이버의 SocketTimeout 값은 DBMS가 비정상으로 종료되었거나 네트워크 장애(기기 장애 등)가 발생했을 때 필요한 값이다. TCP/IP의 구조상 소켓에는 네트워크의 장애를 감지할 수 있는 방법이 없다. 그렇기 때문에 애플리케이션은 DBMS와의 연결 끊김을 알 수 없다. 이럴 때 SocketTimeout이 설정되어 있지 않다면 애플리케이션은 DBMS로부터의 결과를 무한정 기다릴 수도 있다.
  • 이러한 상태를 방지하기 위해 소켓에 타임아웃을 설정해야 한다. SocketTimeout은 JDBC 드라이버에서 설정할 수 있다. SocketTimeout을 설정하면 네트워크 장애 발생 시 무한 대기 상황을 방지하여 장애 시간을 단축할 수 있다.
  • 단, SocketTimeout 값을 Statement의 수행 시간 제한을 위해 사용하는 것은 바람직하지 않다. 그러므로 SocketTimeout 값은 StatementTimeout 값보다는 크게 설정해야 한다. SocketTimeout값이 StatementTimeout보다 작으면, SocketTimeout이 먼저 동작하므로 StatementTimeout 값은 의미가 없게 되어 동작하지 않는다.
  • 즉, 소켓을 이용해서 데이터를 주고받을 때, 지정된 시간을 초과하면 발생한다.

참고

  1. SocketTimeout에 의해 종료되더라도, DBMS에서 실행중인 쿼리가 종료되는 것은 아니다. 만약 쿼리의 timeout 값을 설정하고 싶다면 StatementTimeout를 사용하자. SocketTimeout 값보다 StatementTimeout 값이 더 작으면, SocketTimeout 발생 전에 StatementTimeout에 의하여 쿼리가 종료될 것이다. 그렇다면 Socket 통신이 중지되더라도 이미 StatementTimeout에 의하여 종료되기 때문에, 불필요한 쿼리 실행을 방지할 수 있다.
  2. mysql의 long transaction exception은 mysql system 값 'innodb_lock_wait_timeout'보다 쿼리 실행 시간이 넘어설 경우 발생한다. 이를 없애고 싶다면 당연히 쿼리를 빠르게 처리하는게 맞긴하다. 하지만 최적화 했음에도 지속하여 발생하고, 이 에러를 없애고 싶다면 (가령 ddl lock(metadata lock)을 방지하고자) StatementTimeout를 값을 'innodb_lock_wait_timeout'보다 작게 가져가자.
profile
성장하는 개발자가 되고싶어요

0개의 댓글