반응형

사업부서의 요청으로 OLAP 관련 쿼리를 짜던 중에 뜻하지 않던 슬로우쿼리를 만났다.

select 절은 복잡했지만 from 이하절은 테이블 2개의 양쪽에 pk를 join해서 값을 가져오는 단순한 쿼리였다. 

limit 200으로 끊어서 1차 데이터 이상없음을 검증하고

전체 쿼리를 수행하려는데 근데 이게 느려도 너무 느렸다...

into outfile로 수행해도 느렸다.. 뭔가 문제가 있어보였지만 query 상 문제는 아닌 것으로 보였다.

 

//나머지 제거하고 문제되는 부분만 본다.
select
T1.*
from TMP_LOAD_20200313_01 T1
LEFT OUTER JOIN TMP_LOAD_20200313_02 T2
ON T1.SEQ_NO = T2.SEQ_NO;

1) plan 확인

뭐가 문제일까 일단 쿼리를 단순화 시켜 plan을 떠봤다.

id select type table  type  possible_key key  key len ref  rows  extra
1 SIMPLE  T1  ALL         559741  
1 SIMPLE  T2 ALL PRIMARY       66728 Range checked for each record (index map: 0x1)

 

흠..일단 pk인데 T2 절에 인덱스스캔이 안된 것이 이상했고

Range checked for each record (index map: 0x1) 이게 뭔지 궁금해서 찾아보았다.

 

# range checked for each record (index map:N)

Mysql은 사용하기에 좋은 인덱스를 찾지 못했으나, 이전 테이블에서 컬럼 값을 찾고 난 후에는 사용할 수도 있을 법한 인덱스는 알아냈다. 이전 테이블에 있는 각 열 조합에 대해서는, Mysql은 그 조합이 열을 추출하기 위해서 range 또는 index_merge 접근방식을 사용할 수 있을지를 검사한다. 이방법은 그리 빠른 방법은 아니지만, 인덱스를 전혀 사용하지 않는 것보다는 빠르게 진행한다.

--> 인덱스를 찾지 못했다?.. pk이라고... 잴빠른 b-tree 인덱스인데..왜 못찾을까;;

인덱스를 못찾았는데 어떻게든 옵티마이져가 인덱스 비슷하게 만들어서 scan 했다는 얘기같은데.. 이게 정확히 뭘 의미하는지 몰랐다.

 

# 테이블 스키마를 확인했다.

TMP_LOAD_20200313_01

SEQ_NO bigint (15) unsigned not null

 

TMP_LOAD_20200313_02

SEQ_NO varchar (20) not null

 

--> 같은 컬럼 네이밍에 데이터 타입이 다르다니; 솔직히 생각지 못했다.

seq_no는테이블 200 여개에서 같은 컬럼 네이밍을 쓰고 있는데 데이터형이 다를 줄이야...

결국 range checked for each record 의미는 컬럼의 데이터형이 맞지 않는데 옵티마이져가 강제 형변환해서 scan을 한 것이었다.

 

2) 형변환후 plan 확인

id select type table type possible_key key key len ref rows extra
1 SIMPLE T1 ALL         559741  
1 SIMPLE T2 eq_ref  PRIMARY PRIMARY  8 xxx.T1.SEQ_NO 1  

 

전체 데이터 추출시간 2초이내로 끝났고, 인덱스 스캔이 (eq_ref ) 제대로 되는 것을 확인할 수 있다.

mysql의 옵티마이져는 생각보다 똑똑했다..;

--> 최근에 개발된 테이블 스키마가 문제였고, 데이터 형변환을 적용하려 개발쪽과 협의해서 추가반영을 하였다.

 

반응형
블로그 이미지

dung beetle

취미는 데이터 수집 직업은 MYSQL과 함께 일하는 DBA의 소소한 일상 이야기

,