본문 바로가기

Development Note

[MySQL] 실행 계획

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

 책으로 MySQL 스터디를 하게 되었다. 첫 항목으로 접하게 되는 부분이 다름이 아닌 "실행 계획" 이라는 주제를 가진 챕터이다. 보통 데이터베이스라고 하면 SQL 이 떠오를테고 SQL이 떠오르면 어떻게 쿼리를 잘 하면 되는건가? 하는 생각이 들 것 같다. SELECT, INSERT, CREATE, DROP 등등 여러가지 명령어들이 떠오를테고.. 학부생때 배웠던 이런 간단한 쿼리들과 정보계 시스템들을 개발하면서 배웠던 관계형 데이터베이스를 활용하는 방법들은 잘 알았지만.. 실제로 이 데이터베이스라는 것이 어떤 알고리즘에 따라서 동작하는지에 대한 내용들은 잘 몰랐다. 그래서인지는 몰라도 쿼리를 튜닝하거나 처리 비용이 너무 많이 드는 형태의 쿼리를 짜는 것을 지양한다던가 하는 행위가 잘 안됐던 것 같다. 이 실행 계획에 대해서 개괄적으로 보고 나서는 여러 가지 느낀바가 많았다.


MySQL 데이터 베이스의 구조



먼저 SQL 을 어떤 식으로 읽어 들이는 지에 대한 궁금증이 앞섰다. SQL 을 처리하고 실행하기 전에 SQL 파싱이라는 단계를 거치게 되고 그 다음으로 실행 계획이라는 것을 만들게 된다. 이 두 단계의 과정에서의 주요 역할을 하는 것이 Parser 와 Optimizer 라고 불리우는 것이다. SQL 파싱은 문법적으로 틀린 것이 없는지 확인하는 단계이다. Parser의 역할 중에 하나인데 단순히 문법을 확인하는 것 이외에도 "SQL Parse Tree" 라는 것을 생성 한다고 한다. (명령어들의 연산이나 키워드를 보관하는 자료 구조 인 듯 하다, 참고 URL : 바로가) 다음으로는 Parser 와 Optimizer 가 함께 수행하는 작업이 "최적화와 실행 계획 수립" 이라고 불리우는데 최적화라고 불리우는 작업이 꽤나 정교한 작업을 거친다. 책에는 아래와 같이 내용이 나와 있다.


  • 불필요한 조건의 제거 및 복잡한 연산의 단순화
  • 여러 테이블의 조인이 있는 경우 어떤 순서로 테이블을 읽을지 결정
  • 각 테이블에 사용된 조건과 인덱스 통계 정보를 이용해 사용할 인덱스 결정
  • 가져온 레코드들을 임시 테이블에 넣고 다시 한번 가공해야 하는지 결정


그렇게 되면 실행 계획이라는 의미적인 내용만 살펴보아도 이 실행 계획의 내용을 파악할 수 있다면 SQL 의 성능 향상에 대해서 고민 해볼 수 있을텐데 이런 실행 계획을 살펴보는 방법은 생각보다 간편한 일이지만 그 내용을 분석하는 것은 매우 쉽지 않은 일로 보인다. 실행 계획은 아래와 같이 볼수가 있다.


EXPLAIN
SELECT
   e.first_name
   ,e.last_name
   ,s.salary
FROM 
   employees e JOIN salaries s ON e.emp_no = s.emp_no
WHERE 
   s.salary > 50000 
   AND s.from_date BETWEEN '2000-01-01' AND '2000-12-31'
   AND e.emp_no in (
      SELECT a.emp_no FROM dept_manager a JOIN employees b on a.emp_no = b.emp_no
   )
ORDER BY salary desc
;

위의 SQL 문장을 실행시키면 다음과 같은 결과가 나온다 아래 표시된 테이블 내용을 실행 계획이라고 부른다.



생각보다 실행 계획을 보는 것은 어렵지 않았다. EXPLAIN 이라는 키워드를 사용하게 되면 직접 쿼리가 실행 되는 것이 아니라 해당 쿼리에 대한 실행 계획만 표시가 되게 된다. 각 컬럼별로는 분석된 실행 계획에 대한 정보들이 노출이 되고 이를 토대로 성능이나 의존성 혹은 데이터의 양, 순서까지도 파악을 할 수 있도록 도와주게 된다. 일일이 전부다 짚고 넘어가기에는 양이 너무 방대 해서 어떠한 형태의 내용들을 주의 해야하는지를 살펴 보았다.


  • select_type : DERIVED, UNCACHEABLE SUBQUERY, DEPENDENT SUBQUERY
  • type : ALL, index
  • extra : Range checked for each record (index map: N), Using filesort, Using join buffer, Using temporary, Using where


위와 같은 컬럼 명에 표시된 내용들을 각각의 사유들과 함께 주의 해야할 것으로 보인다. 내가 실행시킨 쿼리에도 그런 내용들이 상당히 보인다. 이런 비밀(?) 을 알고 나니 앞으로 작성할 쿼리나 이미 작성된 쿼리에 대해서 위의 주의 항목들을 찾아내야하나?? 했지만.. 선무당이 사람 잡는다고 섣불리 시도해서는 안되는 것이기도 하지만 리팩토링에 대한 나의 철학과도 맞지 않는 방법이기에 앞으로 짜나갈 쿼리에 대해서 조금은 느슨하게 대응해야 하지 않을까 하는 생각이 든다.


다음은 쿼리 작성에 대한 내용을 다뤄야 겠다.