진행 중인 개인 프로젝트에서 HikariCP + MySQL을 사용 중 인데 기본적으로 쿼리캐싱 기능이 꺼져있다. 반복되어 사용되는 쿼리를 캐싱하여 사용하면 쿼리분석의 시간을 줄일 수 있어 속도면에서 이득이 있으니 적용해보기로 했다.
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문은 ?로 처리하고 파라메터는 따로 받아 가독성이 높다. 높은 가독성과 사용구조의 차이로 유지보수에도 장점이 있다.
DB는 쿼리를 트리로 만들어 구문 분석을하고 이때 문법 오류도 검출 된다. Client PrepareStatement 경우 서버에 보내기 전에 완전한 SQL문을 만들어 보내니 요청마다 구문 분석을 하고, Server PrepareStatement는 ?토큰으로 된 SQL을 분석하고 이걸 캐시하고 있어 한번 구문 분석을 하면 그걸 재사용 가능하여 성능면에서 재사용이 가능한 Server PrepareStatement가 좋다.
dataSource:
hikari:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
중요한 옵션 몇 개만 알아보자