쿼리 캐싱을 위한 Server PrepareStatement

무지성개발자·2024년 1월 8일

서론

진행 중인 개인 프로젝트에서 HikariCP + MySQL을 사용 중 인데 기본적으로 쿼리캐싱 기능이 꺼져있다. 반복되어 사용되는 쿼리를 캐싱하여 사용하면 쿼리분석의 시간을 줄일 수 있어 속도면에서 이득이 있으니 적용해보기로 했다.

Statement의 종류들

Statement VS PrepareStatement

Statement 와 PreparedStatement 둘 다 SQL문을 사용하기 위한 객체며 같은 동작을 한다. 하지만 PreparedStatement의 사용이 권장 되는데 왜일까?

PreparedStatement의 사용이 권장이유로는 여러가지가 있지만 그전에 Statement와 PreparedStatemet의 사용방법을 알아보자.

// Statement
String sqlstr = "SELECT name, memo FROM TABLE WHERE num = " + num 
Statement stmt = conn.credateStatement();
ResultSet rst = stmt.executeQuerey(sqlstr); 

//PreparedStatemet
String sqlstr = "SELECT name, memo FROM TABLE WHERE num = ? "
PreparedStatement stmt = conn.prepareStatement(sqlstr);
pstmt.setInt(1, num);
ResultSet rst = pstmt.executeQuerey(); 
  • 캐싱
    먼저 캐싱 사용여부다. Statement와 PreparedStatement의 쿼리 실행 순서는 쿼리 문장 분석 - 컴파일 - 실행이다. 근데 PreparedStatement는 이 과정을 처음 한번만 하고 캐시에 담아 같은 쿼리에 대해서 재사용을 한다. 때문에 DB에 훨씬 적은 부하를 주며 성능도 좋다.

  • 보안
    PreparedStatement는 SQL 인젝션 공격을 방지하는 효과가 있다. Statement는 쿼리에 직접 파라메터를 집어 넣지만, PreparedStatement는 ?토큰을 사용하여 SQL문을 작성 후, 파라메터는 따로 받는다. 이 때 파라메터가 문자열로 처리가 되어 SQL 인젝션 공격을 방어한다.

  • 가독성
    Statement의 경우 조건문이 많아지면 일일히 다 넣어줘야 하기 때문에 작성이 번거롭고 가독성도 떨어지지만, PreparedSatement는 조건이 많아도 SQL문은 ?로 처리하고 파라메터는 따로 받아 가독성이 높다. 높은 가독성과 사용구조의 차이로 유지보수에도 장점이 있다.

Client PrepareStatement VS Server PrepareStatement

  • Client PrepareStatement
    • ?토큰으로 만들어진 SQL문장을 서버에 보내기 전 ?토큰을 리터럴 값으로 바꿔 완전한 SQL문장으로 만드는 것을 말한다.
    • Client PrepareStatement는 ?토큰의 값만 다른 SQL을 여러번 사용하면 각가 완전한 SQL문장으로 바꿔서 여러번 호출한다.
  • Server PrepareStatement
    • ?토큰으로 만들어진 SQL문장을 한 번만 서버로 전송하여 캐시하고 실행 할 때 토큰의 값만 전송 받아 수행한다.

DB는 쿼리를 트리로 만들어 구문 분석을하고 이때 문법 오류도 검출 된다. Client PrepareStatement 경우 서버에 보내기 전에 완전한 SQL문을 만들어 보내니 요청마다 구문 분석을 하고, Server PrepareStatement는 ?토큰으로 된 SQL을 분석하고 이걸 캐시하고 있어 한번 구문 분석을 하면 그걸 재사용 가능하여 성능면에서 재사용이 가능한 Server PrepareStatement가 좋다.

ServerPrepareStatement를 이용하여 쿼리 캐시하기

HikariCP에서 권장되는 MySQL옵션

  dataSource:
    hikari: 
      cachePrepStmts: true           
      prepStmtCacheSize: 250        
      prepStmtCacheSqlLimit: 2048      
      useServerPrepStmts: true    
      useLocalSessionState: true   
      rewriteBatchedStatements: true
      cacheResultSetMetadata: true  
      cacheServerConfiguration: true
      elideSetAutoCommits: true
      maintainTimeStats: false

중요한 옵션 몇 개만 알아보자

  • cachePrepStmts (cachePreparedStatements)
    • default : false, 권장 : true
    • 캐싱기능 사용을 결정하는 옵션으로 false면 다른 관련 설정들을 사용할 수 없다.
  • prepStmtCacheSize (preparedStatementsCacheSize)
    • default : 25, 권장 : 250-500
    • Mysql 드라이버 커넥션당 캐시할 PrepareStatement 갯수.
  • useServerPrepStmts
    • default: false, 권장 : true
    • Server PrepareStatement 사용 유무.
  • prepStmtCacheSqlLimit (preparedStatementsCacheSqlLimit)
    • default: 256, 권장: 2048
    • Mysql 드라이버에 캐시할 PreparedStatement의 최대 길이 설정.
    • ORM을 사용하면 턱없이 부족하여 2048을 권장.

profile
no-intelli 개발자 입니다. 그래도 intellij는 씁니다.

0개의 댓글