Database/SQL 튜닝

NL 조인 쿼리 튜닝

Debin 2023. 8. 25.
반응형

이번에는 생애 처음으로 토이 프로젝트의 쿼리를 튜닝해보았습니다.

프로젝트에서 제일 많이 호출되는 쿼리를 튜닝했는데 그 경험을 적어보겠습니다.

쿼리 튜닝 환경

  • M1 air
  • RAM 16GB, SSD 512GB
  • Docker 컨테이너 Mysql 8.x 버전

다대다 관계 튜닝하기

project라는 테이블과 member라는 테이블은 다대다 관계다.

중간 관계 테이블로 project_member라는 테이블이 존재한다.

 

테스트 데이터는 MySQL 프로시저를 활용해서 주입했으며 데이터 갯수는 아래와 같다.

  • member: 200만
  • project: 500만
  • project_member: 1400만

먼저 쿼리를 살펴보자. 내가 참여한 모든 프로젝트를 조회하는 쿼리다.

디스코드 사이드 바에서 내가 참여한 모든 채팅방을 보여줄 때 사용하는 쿼리라고 보면 되겠다.

select
      p.id,
      p.title,
      p.content
from member as m
join project_member as pm on m.id = pm.member_id
join project as p on p.id = pm.project_id
where m.id = ? and p.status = ? and pm.status = ? and m.status = ? ;

해당 쿼리를 튜닝하지 않고 실행했을 때 5.9초가 걸렸다.

 

5.9초는 매우 긴 시간이라고 생각한다. 반드시 튜닝이 필요..!!

튜닝을 위해 explain 키워드를 사용해 실행 계획을 확인했다.

쿼리 실행 계획

type이 all이라고 써 있는데 이건 모든 테이블의 데이터를 풀 스캔하는 것이다.

rows를 봐도 어마어마한 값이 적혀있는 것을 알 수 있다.

 

단순히 인덱스를 걸기 전에 최근에 튜닝 공부를 하고 어떻게 수정해야 하는지 생각해보았다.

현재 흐름

현재 모든 테이블은 pk에 대해 클러스터링 인덱스다.

  1. member 테이블에서는 pk인 id를 사용해 굉장히 빠르게 데이터를 찾는다.
  2. member 테이블의 id를 활용해 project_member에서 데이터를 찾아야 하는데, 정렬되어 있지 않다. 따라서 모든 테이블을 풀스캔하는 것이다. 여기가 문제다.
  3. project 테이블도 마찬가지로 pk인 id를 사용해 굉장히 빠르게 데이터를 찾는다.

이렇게 머릿속으로 스캔의 흐름을 그려보았다.

 

project_member 테이블에서 member_id를 가지고 정렬을 시켜야 한다고 생각했으므로 다음과 같이 인덱스를 추가했다.

CREATE INDEX pm_index ON project_member(member_id);

그런 다음 이제 실행 계획을 확인해보았다.

index 생성 후 실행 계획 확인

pm_index를 사용하는 것을 확인할 수 있다.

 

그런 다음 시간 초를 재보았다.

SET @start_time = NOW(6);

select
      p.id,
      p.title,
      p.content
from member as m
join project_member as pm on m.id = pm.member_id
join project as p on p.id = pm.project_id
where m.id = ? and p.status = ? and pm.status = ? and m.status = ? ;

SET @end_time = NOW(6);
SELECT TIMEDIFF(@end_time, @start_time) AS execution_time;

 

다음과 같이 시간이 0.1초로 줄어든 것을 확인할 수 있다.

튜닝 후 쿼리 수행 시간

 

 

이렇게 성공적으로 쿼리 튜닝을 진행했다.

쿼리 튜닝 예제가 아니라 본인 프로젝트의 쿼리를 튜닝을 진행한 것은 처음이다. 

프로젝트의 더 많은 쿼리를 튜닝해봐야겠다.

 

반응형

댓글