SQL 파라미터 입력 방식에 따른 속도 저하

Reading-Snail·2023년 12월 13일
1

MyBatis 파라미터 바인딩 오류?

Mybatis에서 #을 사용하여 파라미터를 넘길 경우 ${}을 사용할 때 보다 약 60배 넘는 속도 차이가 발생하는 현상이 발견되었습니다. ${}의 경우 SQL Injection에 취약하기에 원인을 파악하여 #{}을 사용하는 방법으로 바꾸어야 했습니다.

처음에는 DB 매니저 프로그램을 사용해서 값을 직접 입력해도 속도가 느리게 나와 문제의 원인을 발견하기 매우 어려웠습니다. ${} 사용하여 일부 쿼리가 생략되는 것 같은 이상 현상 때문에 소요시간이 줄어든 것으로 느껴졌기 때문입니다.

그러다 혹시 몰라 직접 값을 해당 SQL에 파라미터를 하드코딩 하여 실행해 보았습니다. 실행 결과 ${}을 적용하였을 때와 비슷한 속도가 나왔습니다. 즉, SQL이나 다른 문제가 아닌 파라미터를 preparedStatement에 바인딩 할 때, 무언가가 문제가 있어 속도 저하가 발생한다는 결론에 이를 수 있었습니다.

좀 더 조사를 한 결과, 파라미터를 바인딩 할 때 형 변환을 거치게 되는데 이때 형 변환이 잘못되거나 어떤 타입으로 형 변환을 할지 정확하게 지정되지 않아 발생하는 이슈로 판단되었습니다. 특히, 문제가 발생한 내용은 WHERE 절에 조건으로서 날짜를 비교하는 구문이었습니다. 테이블에서 불러오는 값은 타입이 VARCHAR이었고, 파라미터의 값은 당연히 STRING이라고 판단하여 VARCHAR로 변형 될 것으로 기대 되었습니다. 하지만 실제로는 OBJECT 타입으로 숫자만 담은 값만 넘어오는 코드였고, VARCHAR가 아닌 NUMBER로 변형된 것으로 보였습니다.

해결책은 분명해 보였습니다. TO_CHAR()로 해당 파라미터를 감싸서 강제로 VARCHAR로 형변환시켜 주는 방법이었습니다. 값을 비교하기 위해 사용된 부등호 양쪽에 타입이 같아질 수 있었습니다. 최종적으로 속도 저하는 사라졌고 ${}를 사용했을 때와 비슷한 정상적인 소요 시간으로 돌아왔습니다.

앗, SQL 튜닝 문제?

MyBatis가 문제가 아닌 것 같다는 피드백이 있었습니다.
비교를 명확하게 하기 위해서 Map\<String,Object>를 Map\<String,String>으로 변환하고 테스트를 해봤습니다. 결과는 기대와 달리 느렸습니다. 즉, 피드백 대로 MyBatis 문제가 아니었습니다. 그리고 #{}을 사용할 경우 따옴표가 붙어 어떤 값도 결국 String 값으로 입력된다는 사실도 알게 되었습니다.

속도 차이가 나는 두 쿼리(TO_CHAR()를 사용하는 것과 아닌 것)를 SQL 매니저를 돌려 플랜을 확인해 봤습니다. Range Scan과 Skip Scan에서 분명한 속도 차이가 발생하는 것을 확인했습니다.
관련 내용을 더 조사해보니 정확한 속도 저하의 원인는 SQL에서 묵시적으로 일어난 타입 변환 때문이라는 사실을 알게 되었습니다. 묵시적 타입 변환의 경우 인덱스가 생략되어 속도 저하가 발생될 수 있다는 경고였습니다. 이를 해결하는 방법은 TO_CHAR, TO_NUMBER와 같은 명시적 타입변환을 사용하여 인덱스를 정확하게 탈 수 있게 변경해주는 것이었습니다.

되돌아보며..

머리가 프로펠러처럼 돌아가는 비둘기 밈은 개발자들의 돌아만 간다면 괜찮다, 오히려 만졌다가 더 큰일 만들기보다는 날아다니니 괜찮다는 의미를 비꼬는 내용으로 많이 사용 되는 듯 합니다. 동작만 하는 프로그램은 폭탄과 같다고 생각합니다. 더 훌륭한 개발자는 정확한 원인을 진단하고, 더 근본적인 해결책을 제공해줘야 된다고 생각합니다.

참조:

profile
책읽는 달팽이 || 공학도에서 개발자로! || 결국 과거의 흐름을 이해했을 때 지금의 것들을 통찰력있게 바라볼 수 있다고 믿습니다.

0개의 댓글

관련 채용 정보