문제
SQL 쿼리를 조회하는데 15초가 걸려버리는 일이 발생했다. 이 문제를 보자마자 바로 SQL 쿼리 문제라는 생각이 들었다. 역시나 예상대로 쿼리가 문제였다.
정확히 말하면, ORM이 문제였다. ORM으로 작성하다보니, 쿼리 최적화를 마음대로 할 수 없었고, 그래서 문제가 발생했던 것이다.
이제 한번 문제를 자세히 살펴보자
원인
특정 쿼리의 결과가 아래와 같았다. 그리고 해당 쿼리에는 select에 서브쿼리가 작성되어 있다.
c.id | c.content | b.id | l.id |
---|---|---|---|
1 | “반가워요” | 1 | 1 |
1 | “반가워요” | 1 | 2 |
1 | “반가워요” | 1 | 3 |
2 | “안녕” | 4 | 5 |
2 | “안녕” | 4 | 6 |
원하는 결과는 c의 리스트이다. 노출되는 c의 개수는 2개인데, 총 row의 개수는 5개로 뻥튀기된 것을 볼 수 있다. 그렇다면 2번만 서브쿼리를 실행하면 될 것을, 5번이나 실행하고 있었다는 것이다.
원하는 결과에 c의 갯수가 많아지면 많아 질 수록 row의 개수는 뻥튀기 될 것이다. 만약 20개의 c를 원한다고 하면 100개의 row가 조회될 수도 있고, 100개의 row에 모두 서브쿼리가 실행된다면 20개의 서브쿼리보다 훨씬 느릴 것이다.
원인파악 완료! 개선해 보자
Result Row의 갯수를 줄이자.
ORM 코드를 잘 살펴보니 특정 조건이 빠져있었다. 그래서 필요한 행 말고 모든 행에 대해 join을 걸면서 row의 갯수가 늘어났던 것이었다.
그 조건을 넣어서 테스트 해봤더니 row의 갯수도 잘 줄어들었고, 15s -> 1s로 속도가 빨라진 것을 확인했다.
후기
생각보다 간단하게 수정되어서 조금은 허탈했다.
코드의 의도를 읽기 위해서 온갖 시도를 했었는데(실제 코드는 많이 복잡하다.) 의도를 파악하고, 문제를 파악하고 나니, 정말 어이없는 곳에서 문제가 있었던 케이스였던 것 같다.
이 트러블슈팅 경험으로 인해 ORM에 대한 단점이 더 눈에 들어오더라. ORM이 마냥 좋은 것만은 아니라는 건 체감하고 있었지만, 이번 경험으로 인해 그 생각이 확고히 굳어진 것 같다. SQL, 그리고 DBMS에 대한 이해 없이 ORM만 사용하게 된다면, 좋은 개발자가 될 수 없을 것이라고 생각한다.
앞으로도 여러 가지 방법을 잘 조합해서 사용해 보도록 해야겠다.