SlideShare a Scribd company logo
1 of 33
1
MySQL/MariaDB SQL Tunning
(For Beginner)
㈜ 네오클로바
DB 기술지원팀
2
3
목차
1. 개요
2. DBMS Tunning 을 위해 알아야 할것들.
2-1. DBMS 성능개선 (모델링 > 시스템 > SQL)
2-2. MySQL/MariaDB Architecture
2-3. InnoDB 구조
2-4. Primary Key 와 Secondary Key
2-5. Join 방식
2-6. Limit
3. SQL 실행계획
3-1. 쿼리실행과정
3-2. 통계정보
3-3. 옵티마이저
3-4. Explain
3-5. Profiling
4. Explain 파해치기
5. SQL 을 위한 인덱스전략
6. SQL 작성가이드
7. 사례로보는 SQL Tunning
- 별첨
4
1. 개요
오픈소스의 관계형 데이터베이스 관리 시스템(RDBMS)에서 가장 많이 쓰이는 소프트웨어가 MySQL 과
MariaDB 이다.
MySQL 은 MySQL AB 가 관리/지원하다 2008 년 썬마이로그시스템즈에 인수되었으며, 2010 년
오라클이 썬마이크로시스템즈를 인수하면서 현재 오라클이 MySQL 소유권을 가지고 있습니다.
MariaDB 는 MySQL 과 동일한 소스코드를 기반으로 한 GPL2 라이선스를 따른다. 썬이 오르클에
인수되면서, 2009 년 Michael Monty Widenius 가 썬을 떠나서 “Monty Program AB”사를 창립합니다.
2012 년 비영리단체인 “Maria DB Foundation”을 설립해서 MariaDB 를 GPL 라이선스로 배포하기
시작했다. 이후 SkySQL 이 “Monty Program AB”를 합병하여 2014 년 “MariaDB Corporation”으로
회사명을 변경하여 오늘에 이르고 있습니다. “MariaDB Foundation”(www.mariadb.org)은
비영리단체로 GPL 라이선스를 소유하고 있으며, “MariaDB Corporation”(www.mariadb.com)은
영리단체입니다.
5
MySQL fork
- MariaDB
- Percona Server
- Drizzle
- WebScaleSQL
- Facebook
- Twitter
- AliSQL
- 등등등
2015’
6
2. DBMS Tunning 을 위해 알아야 할 것들
2.1. DBMS 의 성능개선
데이터베이스의 성능개선은 DBMS 자체, Application, OS 조정등을 통하여 최적의 자원으로 최적의
성능(시간/응답속도)를 얻을 수 있도록 개선하는 작업을 의미합니다.
그렇다면 성능개선의 관점에서 보면 다음과 같은 목적을 가질 수 있다.
관점 목적 내용
대기 분석
(Wait time)
대기시간 최소화 - 동시 세션이 데이터베이스에 접근 할 경우
- DB 자원을 취득하기 위해 대기하는 시간 분석
응답 시간
(Response Time)
응답시간 최소화 - SQL 실행 후 결과가 피드백 되기까지의 시간 분석
비율 분석
(Hit Ratio)
Hit Rate 최대화 - 데이터베이스의 버퍼 캐쉬의 히트 율 분석,
- DISK IO 를 최소화 하도록 메모리 히트 율 향상
이러한 성능개선의 목적을 달성하기 위해서는 크게 세가지의 개선분야(요소)로 구분할 수 있다.
개선요소 설 명 사 례
DB 설계
(모델링관점)
- 데이터베이스 설계 단계에서 성능을 고려하여 설계
- 데이터 모델링 (논리모델)
- 인덱스설계, 데이터파일, 테이블, 스페이스 설계(물리모델)
- 데이터베이스 용량 산정
- 정규화/반정규화
- 분산파일배치
DBMS 환경
(환경 측면)
- 성능을 고려하여 메모리나 블록 크기 등을 지정
- CPU, 메모리 I/O 에 관한 관점
- Buffer, Cache 크기
- 고성능 Disk
OS, HW, N/W
SQL 요소
(응용프로그램 측면)
- SQL 작성 시 성능 고려
- join, Indexing, SQL Execution Plan
- Hash / Join
- Hint
성능에 영향을 미치는 세가지 개선요소들 중에서 어느 것 하나 무시할 수 없지만, 우선순위로 보자면
DB 모델링 > DBMS 환경구성 > Application SQL 작성 순서로 보는 게 일반적이다.
다음은 각 요소 별 주요 튜닝항목들이다
[DB 설계 튜닝]
구분 내용
테이블의
분할/통합
- 논리적으로는 통합된 단일 테이블이지만 DBMS 가 지원하는 파티션기능 적용함으로써
액세스 효율화로 DB I/O 를 최적화 할 수 있음
- - DBMS 에 따라 파티션기능이 제약되는 경우에는 테이블의 수평분할 고려
- 액세스 패턴에 따라 단일 테이블을 1:1 로 수직분할을 고려할 수 있음
7
식별자 지정 - 본질 식별자와 인조식별자의 선택에 따라 정보의 상속과 단절에 영향을 줄 수 있음
효율적인 인덱스
전략
- 최소한의 인덱스로 최대의 효과를 얻을 수 있는 최적의 인덱스 구조 수립
- 인덱스 분포도 15% 이하 유용 유도(15% 이상이면 Full Scan 유리)
적절한 데이터타입
선택
- 조인 시 연결되는 컬럼의 데이터 타입이 다른 경우 내부적인 변형에 의해 인덱스가
있음에도 불구하고 활용을 못해 조인순서나 조인방식을 달리 선택하는 비효율 발생 할
수 있음
[DBMS 환경 튜닝]
구분 내용
CPU 튜닝 - Peak Time 시에 60~70%의 사용량 유지 권고
- CPU 증설 또는 CPU 과다 점유하는 프로세스를 찾아서 해결
Memory 튜닝 - DBMS 의 사용 메모리의 최적화 필요
- 업무를 가능하게 하기 위한 충분한 메모리 확보
- DBMS 메모리 + Session 메모리
Disk I/O 튜닝 - I/O 를 분산시킬 수 있도록 데이터 파일의 재배치
- Raid 를 이용-> I/O 가 많이 일어나는 것은 Raid 1/0 에 배치
Network 튜닝 - Ping, Ftp 를 이용하여 응답시간 분석
[SQL 튜닝]
구분 내용
옵티마이저에 대한
이해기반 SQL 작성
- RBO (Rule Based Optimizer): 통계정보가 없는 상태에서 미리 정해진 Rule 에 따라
실행계획 수립
- CBO (Cost Based Optimizer): 통계정보로부터 모든 Access Path 를 고려하여 실행계획
수립
- 옵티마이저가 선택한 실행계획을 확인하고 최적화된 실행계획 수립이 이루어지도록
Factor 부여
힌트사용 - 옵티마이저가 항상 최적화된 실행계획을 수립하는 것은 아니므로 힌트를 사용하여
원하는 실행계획으로 유도
USE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list])
IGNORE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
FORCE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
부분범위 처리 - 조건을 만족하는 전체집합이 아닌 일부분만
액세스하고도 결과를 리턴 할 수 있도록 하여 온라인 프로그램에서 응답시간(Response
Time)을 최소화 할 수 있음
인덱스 활용 - 인덱스가 있음에도 불구하고 SQL 을 잘못 기술함으로써 무용지물로 만드는 오류 제거
8
조인방식/ 조인순서 - 동일한 SQL 문이라도 조인방식과 조인순서에 따라 처리속도는 매우 큰 차이를 가져올
수 있으므로 작성한 SQL 이 어떤 실행계획으로 수립되는 지 반드시 확인 후 조정
다중처리 (Array
Processing)
- 배치작업의 경우 한번의 DBMS 호출로
여러 건을 동시에 처리할 수 있는 다중처리 활용
병렬쿼리 (Parallel
Query)
- 배치작업의 경우 하나의 SQL 을 여러 개의 CPU 가 병렬로 분할 처리하게 함으로써
처리속도 향상 가져옴(단 Parallel 지원 DBMS)
Prepared SQL 사용 - 조건절에 입력된 값을 먼저 Binding 한 후 실행계획을 수립하는 Dynamic SQL 은 파싱
부하가 커지므로 입력 값을 Binding 하기 전에 실행계획을 수립하는 Static SQL 을
가급적 사용하도록 함. 그러나 DBMS 마다 차이가 존재함.
MySQL 은 Prepared(보안측면 효과) 권고
결국
➔ 얼마나 적은 CPU, Memory, Disk IO 를 통해서 동일한 결과를 가져올 것인가?
➔ 얼마나 빨리 동일한 결과를 가져올 것인가?
➔ 대부분 Disk IO 부하에서 병목현상이 발생함으로 적은 Page Read IO 가 우선적으로 이해.
9
2.2. MySQL / MariaDB Architecture
1. MySQL Architecture
2. MariaDB Architecture
10
2.3. INNODB 구조
- System Tablespace(space ID=0), Innodb Tables-per table(space ID>0) : 32bit space ID 할당
- ibdata 정보를 확인할 때 innochecksum utility 확인 (offline 수행)
- [root@VM1 ~]# ./innochecksum --page-type-summary /usr/local/mysql/data/ibdata1
- File::/usr/local/mysql/data/ibdata1
- ================PAGE TYPE SUMMARY==============
- #PAGE_COUNT PAGE_TYPE
- ===============================================
- 39 Index page
- 427 Undo log page
- 6 Inode page
- 0 Insert buffer free list page
- 5793 Freshly allocated page
- 1 Insert buffer bitmap
- 131 System page
- 1 Transaction system page
- 2 File Space Header
- 0 Extent descriptor page
- 0 BLOB page
- 0 Compressed BLOB page
- 0 Other type of page
- ===============================================
- Additional information:
- Undo page type: 128 insert, 299 update, 0 other
- Undo page state: 0 active, 224 cached, 0 to_free, 1 to_purge, 0 prepared, 202 other
1. Tablespace
- Row > Page(16K) > Extent(64 pages) > Segment > Tablespace 구조(디렉토리= 데이터베이스)
- .frm = 테이블구조(MyISAM 과 동일)
- .ibd = 테이블 Data
Internal Data Dictionary
Replication Info
Insert Buffer,
DoubleWirte Buffer
Undo Logs
System Tablespace
ibdata[n] files
Innodb_file_per_tabl
e
.frm file
.ibd file
InnoDB Tables
---[n]
.frm+.idb (per tables)
DM
Saved Data
11
2. Pages (16Kbyte, 2^32 * 16K=64TB) – Double linked list 구조
FIL Header(38)
Row Datas (16,338)
…
Row Offset Array
FIL Trailer(8)
3. File per table 시 ibd 파일 구조
4. Index Pages
자세한 사항은 아래를 참고하세요.
참고 : https://blog.jcole.us/2013/01/03/the-basics-of-innodb-space-file-layout/
Checksum(4)
Offset(Page Number)(4)
Previous Page(4)
Next Page(4)
LSN for last page modification(4)
Page Type(2)
Flush LSN(0 except space 0 page 0)(8)
Space ID(4)
Old-style Checksum(4)
Low 32bits of LSN(4)
12
2.4. Primary Key 와 Secondary Key
MySQL/MariaDB 는 PK 를 생성하지 않아도 내부적으로 반드시 6bytes 의 ROWID 값에 대한 더미용
PK 를 생성한다. 이 값은 Secondary Index 에 포함되어 Lookup 시 활용한다.
1. Primary Key
or Internal 컬럼 ROW_ID (6byte)
2. Secondary Key
Contain Primary Key
확인) create table t (id int primary key, nm varchar(10));
create index t_idx1 on t(nm);
create index t_idx1 on t(nm, id); 두 인덱스의 차이? 결과는?
13
2.5. Join
- 모든 SQL 처리는 싱글 코어처리(병렬처리 안됨)
- 오직 Nested Loop Join 만으로 join 처리
- 연결고리가 되는 컬럼을 사용하여 바깥쪽 테이블(driving table)과 안쪽 테이블(driven table) 접근.
- 바깥쪽 테이블의 한 row 의 컬럼을 읽고 조인되는 컬럼값을 통해 안쪽 테이블의 한 row 에 대한
조인 컬럼에 대한 결과값을 만들고,
다시 바깥쪽 테이블의 다음의 한 Row 를 읽고 연결된 안쪽테이블의 컬럼을 읽어갑니다.
이렇게 바깥 테이블에 더 이상 남은 행이 없을 때 까지 연산을 수행합니다.
- 단 어떤 테이블을 먼저 읽을지에 대한 판단은 옵티마이저가 결정할 문제이며, 순차적 조인 힌트인
STRAIGHT_JOIN 통해서 사용자가 작성한 테이블이의 순서대로 JOIN 을 수행합니다.
SELECT STRAIGHT_JOIN
COUNTRY.COUNTRY_NAME
,CITY.CITY_NAME
FROM COUNTRY
INNER JOIN CITY ON CITY.COUNTRY_NAME = COUNTRY.COUNTRY_NAME
WHERE 1=1;
// COUNTRY >> CITY
이는 Oracle 의 ORDERED 와 동일하게 작동하는 HINT 이다.(SELECT /*+ORDERED */ … )
14
2-6. Limit
1. 한 테이블의 최대 컬럼 수 ?
- MySQL 5.6.9 이전 – 1000 개
- 이후는 1017 개까지 (5.7 상의 가상컬럼 포함)
2. 한 테이블의 최대 인덱스 수?
- 1 개 Primary Index + 64 개 Secondary Index = 최대 65 개 가능
그럼 MSSQL 은? 오라클은?
3. 컬럼의 인덱스 최대 키 길이(Bytes)는?
- 기본 767byte
- innodb_large_prefix=1 인 경우 3072 Byte 까지 지원
- row_format=DYNAMIC / COMPRESSED
- 멀티컬럼의 경우는 전체 키조합 길이도 3072byte 까지 지원.
- innodb_page_size=16K 면 3072byte, 8K 면 1536byte, 4K 면 768byte 로 변경됨.
(MySQL 5.7.6 부터 innodb_page_size 변경가능)
주의) master / slave 동일하게 설정.
4. 한 Row 의 최대 컬럼 합 Byte 는?
- 65,535byte
5. 단일 Tablespace 의 최대크기?
- 64TB
6. 최대 Undo(데이터 변경 trx) 가능 Transaction 수?
- undo log file 은 최대 128 개, 한 개 파일당 1023 trx 지원 : 128*1023 개
- 임시테이블 사용여부에 따라 다름.
참고: http://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html
15
3. SQL 실행계획
3.1. 쿼리 실행과정
- Query Cache : 사용 설정 시 캐시에 저장데이터 있으면 바로 결과 반환, 없으면 다음.
- Parser : 쿼리를 토큰(parse tree)으로 분리해서 SQL 엔진 인식가능형태 변환. Syntax Error 확인.
- Preprocesser : 쿼리에 구조적 문제없는지 체크. 객체 존재여부 권한확인 Filtering.
- Optimizer : 쿼리변환, 최소 Cost, 실행계획 개선작업 수행 후 Query Execution Plan 생성.
- Query Execution Engine : plan 순서대로 Storage Engine 에게 API Calls 수행함.
(ex: innodb 에게 tmp tbl 만들라, where 조건수행후 읽어와라, 저장하라, 최종 set 생성 등)
결국 실행엔진에서 비효율발생 가능성이 있음.
- Storage Engine : 실행엔진이 시키는 일(API Call)을 수행.
16
3.2. 통계정보
MySQL/MariaDB 에서는 CBO 기반 옵티마이저의 성능을 향상시키기 위해서 통계정보를 활용한다.
그러나 MySQL 의 통계정보에 대하여 몇가지 알아야 할 사항이 있다.
MySQL 5.5 이하 버전에서는 Cardinality 정보만 관리하며, 타 DBMS 와 다르게 다양한 이벤트를 통해서
상당히 동적으로 통계정보가 변경됩니다.
Cardinality 는 인덱스 컬럼 값의 Unique 값 수를 의미합니다.
유사한 정보로서 Histogram 은 인덱스 존재여부와 상관없이 원하는 컬럼의 값 분포정보입니다.
이 통계정보는 information_schema.statistics 테이블에 저장됩니다. 이를 통해 어떤 인덱스를
사용해서 데이터를 조회할 지를 결정합니다.
통계정보가 잘못된 경우는 옵티마이저가 최고의 실행계획을 찾지 못할 수 있습니다.
[MySQL/MariaDB 통계정보]
구분 내용
수집량 소량 페이지 샘플링 분석 후 예측(InnoDB), 인덱스 전체페이지(MyISAM)
Cardinality
= Total Rows / Num_Distinct
information_schema.statistics
userstat = ON (Enable)
정확도 예측기반(InnoDB), 거의 실측치(MyISAM)
통계수집 Eve
nt
동적 Event(InnoDB)
- 테이블 첫 오픈시점
- SHOW TABLE STATUS LIKE ‘~’;시
- SHOW INDEX FROM ~;
- DDL 수행
- innodb_stats_on_metadata=1 경우
information_schema Data 조회 시
- Table Data 6.25%(5.5), 10%(5.6) 변경 시
명시적 (MyISAM)
- ANALYZE TABLE ~;
영구적
통계정보
- 5.5 이하 불가능
- 5.6 이상에서부터 지원
mysql.innodb_table_stats
mysql.innodb_index_stats
innodb_stats_persistent=0/1 #위 테이블에 영구저장 여부
innodb_stats_auto_recalc=0/1 #자동수집 여부
히스토그램
- MariaDB 10.0 부터 지원, mysql 미지원
- Mysql.table_stats, mysql.column_stats, mysql.index_stats
user_stat_tables=NEVER
histogram_size=255 (0~255)
histogram_type=SINGLE_PREC_HB / DOUBLE_PREC_HB
옵티마이저의 optimizer_use_condition_selectivity=1~5
1(기본), 2(인덱스선택도), 3(모든컬럼선택도), 4(히스토그램), 5(4+레코드 샘플링)
17
3.3. 옵티마이저(Optimizer)
MySQL./MariaDB 의 옵티마이저는 비용기반옵티마이저(CBO)로 각 테이블의 데이터가 어떤 분포로
저장돼 있는지 통계정보를 참조하여 최적의 실행계획을 수립하는 작업을 담당한다.
1. 사용자의 요청 쿼리 문장을 최소비용으로 가장 빠르게 처리할 지 결정
2. 쿼리를 변환
3. 비용을 최적화
4. 실행계획을 개선
합니다.
옵티마이저는 비용예측 시 캐시의 영향은 고려하지 않습니다. 다만 모든 데이터에 대한 접근(Read)은
실제 Disk IO 가 필요하다고 가정하고 비용을 산정합니다.
또한 스토어드 루틴(프로시저, 함수)등 자신의 통제하에 있지 않는 명령에 대해서는 비용을 고려하지
않습니다.
또한 옵티마이저는 가능한 모든 실행가능한 계획의 비용을 예측하는 것이 아니므로, 선택한 실행계획이
최적의 실행계획이 아닐 수도 있습니다.
다음은 옵티마이저가 최적화를 알고 있는 일부 몇가지 경우입니다.
1. 조인순서를 조정
2. OUTER JOIN 을 INNER JOIN 으로 변경
3. 불가능/불필요 조건제거
4. COUNT(), MIN(), MAX()최적화
5. 쿼리간소화 및 상수화
6. 커버링 인덱스 선택
7. 서브쿼리 최적화
8. 조기종료처리
18
3.4. 실행계획(Explain)
MySQL 에서는 쿼리의 실행계획을 확인하는 방법으로 Explain 명령어를 이용합니다.
수행하고자 하는 SQL 문장앞에 Explain 을 추가 함으로서 SQL 에 대한 실행계획을 확인합니다.
다음의 예를 보자.
mysql> EXPLAIN SELECT HOST, USER, PASSWORD FROM mysql.user;
id select_type table type possible_keys key key_len ref rows Extra
------ ----------- ------ ------ ------------- ------ ------- ------ ------ --------
1 SIMPLE user ALL (NULL) (NULL) (NULL) (NULL) 7 (NULL)
Explain 을 통해 SQL 에 대한 실행계획 결과를 보면 id, select_type, table, type, possible_keys, key,
key_len, ref, rows, Extra 등의 정보를 볼 수 있다.
Explain 은 select 문에 사용된 각 테이블당 하나의 행을 리턴한다.
또한 나열된 순서는 MySQL 이 쿼리의 처리를 위해 수행한 순서를 가지고 출력된다.
타 DBMS 의 실행계획을 보면 단일테이블의 데이터를 찾는 순서가 Index 를 거쳐서 데이터 블록을 찾는
순서에 대해서 좀더 세부적으로 Plan 을 볼 수 있는 것에 비해 조금 아쉬운 부분이 있다.
참고로 MySQL 에서 EXPLAIN = DESCRIBE = DESC 는 동의어(synonyms) 입니다.
{EXPLAIN | DESCRIBE | DESC } tbl_name [col_name | wild];
{EXPLAIN | DESCRIBE | DESC } {EXTENDED | PARTITIONS} SELECT select_options;
Explain 을 통해서 우리는
- 옵티마이저가 어떻게 쿼리를 변환했는지
- 우리가 원하는 조인 순서가 맞는지
- 해당 테이블의 인덱스를 적절히 사용하고 있는지
- 사용하는 인덱스를 통해서 힌트를 추가하거나 SQL 을 수정해야 하는 것은 아닌지
를 확인해야 합니다.
결과셋은 하나의 테이블을 읽을 때의 정보를 보여주며 각 컬럼의 구성은 다음과 같다.
컬럼 설명
id 한 테이블의 SELECT 번호, 쿼리내의 SELECT 의 구분번호.
select_type SELECT 의 타입(SIMPLE, PRIMARY, UNION, SUBQUERY, DRIVED…)
table 해당 table 명
type 조인의 타입(system, const, eq_ref, ref, ref_or_null, index_merge…)
passible_keys 테이블의 사용가능한 인덱스들 명.
key Passible_keys 값들 중에서 실제 사용한 인덱스(key)
Key_len Where 절에 사용한 인덱스의 길이(byte)
ref 행을 추출하는데 key 와 함께 사용한 컬럼이나 상수
rows 쿼리수행을 위해 검사대상 예상행수
filtered 조건절에 의해 필터링되는 행의 비율
Extra 쿼리를 해석한 추가적인 정보
19
3.5. 프로파일링(Profiling)
프로파일은 MySQL 의 현 세션에서 수행했던 최근 SQL 에 대한 Resource 사용정보를 보여준다.
SQL 수행하는데 어느 부분에서 자원을 많이 사용하며 어디에 병목이 발생하고 있는지 확인할 수 있다.
이는 실행계획과 더불어 자원의 효율적인 사용을 위해서 참고할 만한 자료로 사용할 수 있다.
SELECT ~;
SHOW PROFILES;
SHOW PROFILE
[ALL | BLOCK TO | CONTEXT SWITCHES | CPU | IPC|MEMORY|PAGE FAULTS|SOURCE|SWAPS]
[FOR QUERY n]
[LIMIT row_count [OFFSET offset]]
mysql> show profiles;
+----------+------------+-----------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------+
| 1 | 0.00027225 | select * from test.t1 |
mysql> show profile;
+----------------+----------+
| Status | Duration |
+----------------+----------+
| starting | 0.000074 |
| query end | 0.000010 |
| closing tables | 0.000005 |
| freeing items | 0.000018 |
| cleaning up | 0.000017 |
+----------------+----------+
Mysql> show profile ALL; # 결과는?
SHOW PROFILES/PROFILE 은 5.6.7 에서 deprecated 되었습니다.
향후에는 performance_schema 를 활용하시기 바랍니다.
# performance_schema 를 시작/설정하는 방법
https://dev.mysql.com/doc/refman/5.6/en/performance-schema-startup-configuration.html
# performance_schema 로 프로파일링 하는 방법
https://dev.mysql.com/doc/refman/5.6/en/performance-schema-query-profiling.html
20
4. Explain 파해치기
MySQL 5.6.3 까지는 UPDATE, INSERT, DELETE, REPLACE 등에 대한 실행계획은 지원하지 않아서
SELECT 절로 변환해서 확인했다. 그러나 5.6.3 부터는 모두 지원한다.
Explain 수행결과에 컬럼들의 정보는 다음과 같다.
4.1. id
한 테이블의 SELECT 번호, 쿼리내의 SELECT 의 구분번호를 의미하며, 쿼리의 처리하는 테이블의 순서로
이해하면 된다.
여러 개의 테이블 조인 시에 테이블의 개수만큼 실행계획 레코드가 생기지만 ID 는 동일하게 부여.
4.2. select_type
어떤유형의 select 타입인지를 의미한다. 다음의 표를 참고하세요.
Select_type 설명
SIMPLE UNION 이나 SUBQUERY 를 사용하지 않는 단순 SELECT
PRIMARY 쿼리 제일 바깥쪽에 있는 SELECT
DERIVED SELECT 절로 추출된 테이블로 FROM 절 내부의 SUBQUERY 의미
UNION UNION 절에서 두번째 이후 SELECT 단위, 첫번째는 DRIVED
DEPENDENT UNION 내부쿼리가 외부값을 참조하는 UNION 절 테이블
UNION RESULT UNION 결과값에 대한 임시 테이블
SUBQUERY FROM 절 이외의 서브쿼리의미.
DEPENDENT SUBQUERY 서브쿼리가 바깥쪽 SELECT 절의 정의 컬럼값을 참조
UNCHACHEABLE SUBQUERY 서브쿼리결과가 외부쿼리의 변하는 값에 따라 매번 새로생성형태.
UNCHACHEABLE UNION 캐싱이 불가능한 요소를 포함한 UNION
개별 type 에 대한 예제는 다음과 같다.
mysql> EXPLAIN select * from t1 where id=1;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 N const PRIMARY PRIMARY 4 const 1 100 N
mysql>EXPLAIN
SELECT
t2.nm
,(SELECT COUNT(*) FROM t2 WHERE nm=nm) AS cnt
FROM t1
INNER JOIN t2 ON t2.nm=t1.nm
WHERE t2.del_yn='Y'
21
id select_type
tabl
e
pa
rtit
io
ns
ty
pe
possible_
keys
key key_len ref rows filtered Extra
1 PRIMARY t2

N
AL
L
PRIMAR
Y
N N N 3 33.33 Using where
1 PRIMARY t1

N
AL
L
N N N N 2 50
Using where; Using join
buffer (Block Nested
Loop)
2
SUBQUER
Y
N

N

N
N N N N N N
Select tables optimized
away
mysql> EXPLAIN
SELECT
t1.nm
,COUNT(*) AS cnt
FROM (
SELECT
'a' AS nm
,t2.del_yn
FROM t2
GROUP BY del_yn
) a
INNER JOIN t1 ON t1.nm = a.nm
WHERE a.nm='a'
GROUP BY t1.nm;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 N ALL N N N N 2 50 Using where
1 PRIMARY
<derived2
>
N ref <auto_key0>
<auto_key0
>
5 const 1 100 N
2 DERIVED t2 N ALL N N N N 3 100
Using
temporary;
Using filesort
22
mysql>EXPLAIN
SELECT t1.nm FROM t1 GROUP BY nm UNION
SELECT t2.nm FROM t2
id select_type table
partition
s
type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 N ALL N N N N 2 100
Using temporary;
Using filesort
2 UNION t2 N index N PRIMARY 302 N 3 100 Using index

N
UNION
RESULT
<union1,2> N ALL N N N N N N Using temporary
mysql> EXPLAIN
SELECT COUNT(*) FROM (
SELECT '1' AS C1, t1.nm FROM t1 GROUP BY nm UNION
SELECT '2', t2.nm FROM t2 UNION
SELECT '3', t2.nm FROM t2 WHERE del_yn='Y'
) B
1 PRIMARY <derived2> N ALL N N N N 7 100 N
2 DERIVED t1 N ALL N N N N 2 100
Using temporary; Using
filesort
3 UNION t2 N index N PRIMARY 302 N 3 100 Using index
4 UNION t2 N ALL N N N N 3 33.33 Using where
N
UNION
RESULT
<union2,3,4> N ALL N N N N N N Using temporary
mysql> EXPLAIN
SELECT nm
FROM t1
WHERE EXISTS (SELECT 1 UNION ALL SELECT 1 FROM t2 WHERE t2.nm = t1.nm )
id select_type table
partition
s
type possible_keys key
key_le
n
ref rows
filtere
d
Extra
1 PRIMARY t1 N ALL N N N N 2 100 Using where
2
UNCACHEABL
E SUBQUERY
N N N N N N N N N No tables used
3
DEPENDENT
UNION
t2 N eq_ref PRIMARY PRIMARY 302 test.t1.nm 1 100 Using index
mysql> EXPLAIN
SELECT *
23
FROM t1
WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.nm = t1.nm);
id select_type table
partition
s
type
possible_key
s
key key_len ref rows
filtere
d
Extra
1 PRIMARY t1 N ALL N N N N 2 100 Using where
2
DEPENDENT
SUBQUERY
t2 N eq_ref PRIMARY PRIMARY 302 test.t1.nm 1 100 Using index
mysql> EXPLAIN
SELECT *
FROM t1
WHERE id = (SELECT @status FROM t2 WHERE t2.nm='a');
id select_type
tabl
e
partitions type
possible_key
s
key key_len ref rows
filtere
d
Extra
1 PRIMARY t1 N ALL N N N N 2 100 Using where
2
UNCACHEABLE
SUBQUERY
t2 N const PRIMARY PRIMARY 302 const 1 100 Using index
mysql> EXPLAIN
SELECT *
FROM t1
WHERE id IN (SELECT @status FROM t2 WHERE t2.nm='a' UNION ALL SELECT 1);
id select_type table
partition
s
type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 N ALL N N N N 2 100 Using where
2
DEPENDENT
SUBQUERY
t2 N const PRIMARY PRIMARY 302 const 1 100 Using index
3
DEPENDENT
UNION
N N N N N N N N N No tables used
24
4.3. table
SELECT 절에 대한 데이터를 읽어들이는 테이블명을 말한다.
해당 테이블명 또는 테이블에 대한 Alias, 서브쿼리등으로 인한 가상의 테이블(<derived#n>을
의미한다. 단순한 쿼리의 경우는 쉽게 확인이 가능하나, 복잡한 쿼리의 경우는 이해가 다소 어렵다.
4.4. type
조인타입 또는 테이블 접근방식(Access type)으로 이해하면 쉽다.
풀스캔을 하는지, 인덱스를 사용하는지, unique 키를 사용하는지 등을 판단하여 성능판단의
주요지표이며 아래의 type 순서는 옵티마이저의 효율적인 순서(높음>중간>낮음)대로 정리합니다.
type 설명
system table 에 하나의 row 만 가지는 const 의 특별한 형태
const PK 나 Unique 키에 반드시 1 건에 대한 등가(=)조건에 대한 접근
eq_ref 조인시 첫째컬럼값으로 두번째 테이블등의 PK, Unique 등에 등가(=) 1 건 접근.
ref 조인시 인덱스에 대해 등가(=)접근하나 1 건이라는 보장이 없는 접근
fulltext 전문 검색 인덱스 사용하는 접근시(Where MATCH(?) AGAINST (~) 사용)
ref_or_null NULL 비교가 추가된 ref 접근 형태
index_merge 해당 테이블의 두개이상의 인덱스를 사용한 검색결과에 대한 병합한 형태
unique_subquery IN 절에서 서브쿼리가 중복없는 unique 값을 반환하는 형태
index_subquery IN 절에서 서브쿼리가 중복이 있으나 INDEX 를 이용해 제거할 수 있는 형태
range 대소 비교연산자를 이용해서 인덱스 걸린 컬럼 검색시.
index 인덱스 풀스캔. 인덱스를 처음부터 끝까지 조건없이 읽는 형태.
ALL 풀 테이블 스캔.
- Index Range Scan 은 type 이 const, eq_ref, ref 인경우를 의미하며 성능상 좋은 접근방법입니다.
[system]
mysql> EXPLAIN
SELECT * FROM mysql.db;
id select_type
tabl
e
partition
s
type
possible_key
s
key
key_le
n
ref
row
s
filtered Extra
"1
"
"SIMPLE" db N system "" "" "" "1" "100.00
"
- 하나의 row 만 가지는 테이블 Access 시 나타나는것으로 const 의 특별한 유형.(system table)
[const]
mysql> EXPLAIN
SELECT *
FROM t1
25
WHERE id = 1;
id select_type table
partition
s
type
possible_key
s
key key_len ref rows filtered Extra
1 SIMPLE t1 N const N N N N 1 100
[eq_ref]
mysql> EXPLAIN
SELECT t1.*
FROM t3
INNER JOIN t1 ON t1.id=t3.id;
i
d
select_type
tabl
e
partitions type
possible_key
s
key key_len ref rows filtered Extra
1 SIMPLE t3 N index PRIMARY PRIMARY 4 N 2 100 Using index
2 SIMPLE T1 N
eq_re
f
PRIMARY PRIMARY 4
test.t3.i
d
1 100
[ref]
mysql> EXPLAIN
SELECT t2.nm FROM t1
INNER JOIN t2 ON t2.nm=t1.nm
WHERE t1.nm ='a';
i
d
select_type
tabl
e
partitions type
possible_key
s
key key_len ref rows filtered Extra
1 SIMPLE T2 N const PRIMARY PRIMARY 4 N 2 100 Using index
2 SIMPLE T1 N ref nm nm 4
test.t3.i
d
1 100
[ref_or_null]
mysql> EXPLAIN
SELECT id, nm FROM t1 WHERE nm = 'a' OR nm IS NULL;
id select_type table partitions type
possible_key
s
key
key_le
n
ref rows filtered Extra
"1
"
"SIMPLE" "t1" N "ref_or_null
"
"nm" "nm" "303" "const" "2" "100.00" "Using where;
Using index"
[index_merge]
mysql> EXPLAIN
SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;
id
select_typ
e
table
partition
s
type
possible_key
s
key
key_le
n
ref
row
s
filtered Extra
"1 "SIMPLE" "tbl_name N Index_merge Key1, key2 Key1, key2 4,4 null "1" "100.00 Using
26
" " " union(~
)
[unique_subquery]
mysql> EXPLAIN
~ VALUE IN (SELECT primary_key FROM single_table WHERE some_expr);
[index_subquery]
mysql> EXPLAIN
~ VALUE IN (SELECT key_column FROM single_table WHERE some_expr);
[range]
mysql> EXPLAIN
SELECT nm FROM t1 WHERE nm>'a';
id
select_typ
e
table
partition
s
type possible_keys key
key_le
n
ref rows
filtere
d
Extra
"1
"
"SIMPLE" "t1" null " range " "nm" nm 303 cons
t
1 100" Using
where
;
Using
index
[index]
mysql> EXPLAIN
SELECT nm FROM t1 WHERE nm>='a';
id
select_typ
e
table
partition
s
type possible_keys key
key_le
n
ref rows
filtere
d
Extra
"1
"
"SIMPLE" "t1" null index "nm" nm 303 cons
t
1 100" Using
where
;
Using
index
[ALL]
mysql> EXPLAIN
SELECT * FROM t1;
id
select_typ
e
table
partition
s
type possible_keys key
key_le
n
ref rows
filtere
d
Extr
a
"1
"
"SIMPLE" "t1" ALL "" 2 100"
27
4.5. possible_keys
해당 테이블을 검색할 때 사용할 수 있는 인덱스들의 정보입니다. 이 값이 NULL 이면 사용가능
인덱스가 없다는 의미입니다.
이 값의 순서는 의미가 없으며, 사용되지 않을수도 있습니다.
이 값이 NULL 인경우는 Where 절을 검사하여 적절한 인덱스를 생성해서 쿼리의 성능향상을 도모할
필요가 있습니다.
4.6. key
possible_keys 의 인덱스들 중에서 실제 사용한 인덱스 정보를 의미합니다.
이 값을 통해서 개발자/DBA 는 사용된 index 정보를 확인할 수 있습니다.
실행계획 풀테이블 스캔인 type=ALL 이면 key=NULL 로 표시되며, index_merge 인 경우는 하나이상의
인덱스를 가질 수 있다.
만약 옵티마이저가 선택한 인덱스에 대하여 무시하거나 다른 인덱스를 사용하도록 할 경우는
FORCE INDEX / USE INDEX/ IGNORE INDEX 힌트를 통해서 해당 쿼리의 실행계획을 변경할 수
있습니다.
SELECT nm FROM t1 USE INDEX(PRIMARY);
SELECT nm FROM t1 FORCE INDEX(nm);
SELECT nm FROM t1 IGNORE INDEX(nm);
4.7. key_len
해당 테이블의 사용한 인덱스의 길이를 의미하며, key=NULL 이면 key_len=NULL 이다.
또한 실제 복수컬럼 키중 얼마나 많은 부분을 사용할 것인지 알 수 있다.
단순히 키 길이가 의미가 없다고 판단할 수 있으나, key_len 이 중요한 이유는 Where 에 대한
제약조건을 통해서 해당 인덱스에서 key 로 사용된 컬럼들의 길이의 총 합이다.
이 의미는 인덱스에 있는 컬럼 이더라도 Key조건으로 사용되는 경우는 key_len 에 포함되지만, Covered
Index 의 활용되는 사례처럼 Filter 조건으로 사용되는 경우는 해당컬럼의 길이는 포함되지 않는다.
이 값은 길이가 아니라 BYTE 를 의미합니다.
# charset = UTF8 인경우
CREATE TABLE t4 (id INT NOT NULL , nm VARCHAR(100), cmt VARCHAR(20), rdate DATETIME,
del_yn VARCHAR(1), PRIMARY KEY(id), KEY(nm,del_yn));
SELECT * FROM t4 WHERE nm ='a';
SELECT * FROM t4 WHERE nm ='a' AND del_yn=’Y’;
SELECT * FROM t4 WHERE nm ='a' AND del_yn like ’%Y%’;
# key_len 의 값은?
28
4.8. ref
ref 는 사용된 인덱스의 key 로서 사용된 컬럼이나 상수값을 나타냅니다.
만약 ref=func 이면 이는 특정 함수를 통해서 얻어진 결과값을 의미합니다.
4.9. rows
이는 쿼리를 수행하기 위해 해당테이블을 조사해야 하는 행수입니다.
InnoDB 의 경우 이값은 추정치를 나타내며, 항상 정확하지는 않습니다.
4.10. filtered
테이블의 제약조건에 의해서 필터링됩니다. 이값은 해당 테이블의 행의 예상비율을 나타냅니다.
4.9 의 rows 값(추청되는 조사할 행수)에 filtered 비율을 곱하면 이 값이 이전 테이블과 조인될 때
사용될 것으로 예상되는 rows 값을 찾을 수 있다.
4.11. Extra
이 값은 옵티마이저가 쿼리를 어떻게 해석해서 어떤 방식으로 Access 하는지에 대한 추가정보를
제공합니다. Extra 가 가지는 값들은 다음과 같다.
항목 설명
const row not found type=const 로 Access 했으나 대상테이블이 empty 테이블인 경우
Distinct 테이블 결과값이 중복없이 Unique 한 결과인 경우
Full Scan on Null key in 절 쿼리중 Outer query 에서 NULL 값이 공급되는 경우 발생함.
Index lookup 실패로 Full Table Scan 이 발생할 수 있음
Impossible Having Having 절 항상 False. Select 연산 안함.
Impossible Where Where 절 항상 False. Select 연산 안함.
Impossible WHERE noticed
after reading const tables
const/system 유형의 테이블 읽은 후 Where 절이 항상 False.
No tables used from 절 테이블 명시안됨. from dual 구문
Not existed Left join 절을 Not Exists 형태로 옵티마이저가 최적화 하는 경우
range checked for each
record (index map: N)
조인에 적절한 인덱스가 없어서, 선행테이블 컬럼값에 따라 각각
row 에 대해서 range / index merge 를 검토함을 의미
N 값은 어떤 인덱스를 검토되는지 표시
Select tables optimized
away
Select 절에 집계함수만 있는 경우, 옵티마이저는 Index Lookup 후
1 개의 결과만 리턴하고 끝.
unique row not found select ~ from table 쿼리에서 Key/PK Index 에 만족하는 row 가
없는경우
Using filesort 정렬결과를 만들기 위해 물리적인 정렬작업을 수행
29
Using index Index 내에서 모든 결과값을 리턴하는 경우(Covering Index)
인덱스 통해서 lookup 과정이 수행되면 NULL 값임.
Using index for group-by Index 내에서 group by / distinct 처리
Using join buffer join 처리를 위한 Index 가 없을경우
join 시 memory 의 join_buffer 사용함을 의미.
Using sort_union(...),
Using union(...),
Using intersect(...)
Index Merge 수행. 두개이상의 인덱스를 동시에 사용하여 처리.
Using temporary 쿼리처리를 위하여 임시테이블을 생성함.
group by, order by 등에 인덱스 사용 못할 때 발생.
Using where where 절이 다음조인에 사용될 행이나, Client 에 전송할
result 제한하는 경우.
type 이 ALL 이나 Index 값이면서 Extra 가 Using where 가 아니면
쿼리상에 문제가 있는 경우로 확인필요.
Using where with pushed
condition
InnoDB 에서는 발생안함(Only NDB)
Using MRR 테이블을 읽을 때 다중범위 읽기 최적화 전략 수행시 해당.
Secondary Index 의 랜덤 IO 를 순차 IO 로 변경해서 eq join 수행.
http://dev.mysql.com/doc/refman/5.7/en/mrr-optimization.html
Using index condition mysql 5.6 이상
Filtering 조건처리를 mysql 엔진이 아닌 InnoDB 엔진에 pushdown.
불필요 IO 줄여 빠른응답성능 제공.
Start temporary, End
temporary
mysql 5.6 에서 semi-join 최적화.
조인후에 중복제거하는 전략이용하여 semi-join(exist or in 절 select)
수행.
Skip_open_table,
Open_frm_only,
Open_trigger_only
Open_full_table
information_schema 조회시 발생.
- Extra 값에 Using filesort 나 Using temporary 면 실행계획을 보다 면밀히 확인할 필요가 있습니다.
의도된 계획이거나 인덱스 값 확인 등을 통해서 실행계획을 재검토 한다.
30
5. SQL 을 위한 인덱싱 전략
5.1 SQL 상의 컬럼가공 제거
SELECT nm FROM t1 Where id/2=1;
SELECT nm FROM t1 where substring(nm, 1,3)=’abc’;
SELECT nm FROM t1 where to_days(current_date)-to_days(dt)<=10;
제약조건이나 조인조건상에 있는 컬럼의 경우에 컬럼을 가공/병합 처리등으로 원본컬럼값을
가공한다면 인덱스를 정상적으로 사용할 수 없다.
흔히 하는 실수가 컬럼값에서 문자열을 잘라와서 비교하거나, 날짜구간 계산의 비효율적인 사용이
대표적인 경우다.
5.2 클러스터링 인덱스(테이블)
InnoDB 클러스터 인덱스는 전혀다른 의미의 인덱스가 아니다.
실제로 이는 B-Tree 구조의 인덱스정보와 Data 행값을 함께 가지고 저장한다. Root -Branch – Leaf
구조에서 Data 는 Leaf 에 저장된다.
테이블은 오로지 하나의 클러스터 인덱스만 가질 수 있으며, 만약 클러스터 인덱스를 생성하지 않으면
가상의 6byte ROW_ID 를 생성한다. 이렇게 기본 키(Primary Key)에 따라서 데이터를 묵는다.
클러스터 인덱스 구성 시 몇가지 주의사항
- 데이터 Insert 시에는 기본키 순서에 따라서 삽입하는 것이 유리.
- 클러스터 인덱스의 컬럼크기는 작을수록 좋다.
- 자주 변경되는 컬럼은 기본키로 적합하지 않다.
- 기본키만이 클러스터 인덱스로 생성된다. 이는 조금 혼돈스럽다. MySQL 은 키의 개념과
클러스터링 개념을 혼합해서 관리한다.
- 클러스터 인덱스 구조상 페이지의 분할을 고려해야 한다.
5.3 커버링 인덱스
불필요하게 여러컬럼을 읽지 않아도 되는 경우는 커버링 인덱스를 통해서 성능을 향상시킬 수 있다.
쿼리결과에 필요한 데이터가 모두 포함된 인덱스를 커버링 인덱스라고 한다.
쿼리가 커버링 인덱스를 적용하면 Explain 의 Extra 컬럼에 “Using Index”로 표시된다.
커버링 인덱스의 장점은 다음과 같다.
- 보조인덱스( NonClustered Index)만으로 데이터 결과셋을 완성할 수 있어 속도가 빠르다.
- 데이터가 정렬되어 있어서 무작위로 클러스터링 인덱스의 무작위 위치에서 가져오는것에 비해
IO 가 적게 든다.
- 최선은 Where + Select 결과절까지의 컬럼을 커버하는 경우이며, Where 조건절의 컬럼은 커버링
인덱스에 포함하는 것이 좋다.
31
5.4 압축(프리픽스) 인덱스
인덱스를 생성하고자 하는 경우 특정컬럼의 크기가 큰경우, 인덱스 컬럼의 특정길이 만큼만 인덱스
구성이 가능하다.
지원가능한 컬럼타입은 char, varchar, binary, varbinary, text
CREATE TABLE TBL(ID INT, NAME VARCHAR(1000));
CREATE INDEX TBL_IDX ON TBL ( NAME(10) );
INNODB 의 innodb_large_prefix 옵션이 꺼져있는 경우는 767byte, 켜져있는경우는 3072 byte 까지
컬럼크기 만큼 생성할 수 있다.
5.5 인덱스 개수를 최소로 구성(중복제거)
인덱스의 수가 증가하면 DML 에 따른 데이터 변경적재에 심각한 성능저하가 발생할 수 있다. 인덱스의
수와 DML 속도는 Trade-Off 관계로서 운영시스템의 SQL 조건들에 따라 적절한 인덱스의 개수를
유지하는 것이 좋다.
또한 중복된 인덱스를 구성하지 않도록 관리해야 한다.
CREATE TABLE TBL(ID INT PRIMARY KEY, NM1 VARCHAR(10), NM2 VARCHAR(10));
CREATE INDEX TBL_IDX1 ON TBL ( NM1 );
CREATE INDEX TBL_IDX2 ON TBL ( NM1, ID); #?
5.6 다중컬럼 인덱스
흔히들 단일컬럼 인덱스 만드는 것과 다중컬럼 인덱스를 만든는 것의 차이를 이야기 하곤 한다.
단일 컬럼 인덱스보다 다중컬럼 인덱스를 사용할 것을 권장한다.
5.7 복합정렬의 주의
여러컬럼에 대해서 group by 나 정렬이 필요한 경우, MySQL 에서는 ASC 만 사용하는 것이 좋다.
현재 MySQL 5.7 까지 ASC 와 DESC 를 혼합한 경우 DESC 는 지원하지 않아서 filesort 를 피할 수 없다.
이런 경우는 데이터를 ASC 화 해서 저장하거나 가상컬럼을 이용해서 DESC 방식을 우회할 수 있다.
5.8 정리
- 제일 많이 수행되는 쿼리를 확인.
- 모든 쿼리를 만족시키는 인덱스 전략은 없다. 인덱스의 구성과 속도의 타협이 필요하다.
- 등가비교컬럼이 선두컬럼이 되도록 인덱싱한다.
- 컬럼 크기는 되도록 작게 prefix 인덱스를 적절히 활용한다.
- 모든 데이터 결과처리를 MySQL 의 인덱스등으로만 수행하려는 생각은 재고해야 한다.
- 비정규화, 집계테이블, 캐싱등을 적절히 활용한다.
32
6. SQL 작성 가이드
아래내용은 절대적인 기준은 아니며, 팀/조직 내부의 표준화 정책에 따라 달라질 수 있습니다.
- SQL TEXT 에 대하여 표준화 하라.
예를 들면 table, column 등은 소문자로, 나머지는 대문자로 통일 등. 세부항목들의 정리.
- 당연히 ANSI-SQL 규약에 충실하자.
- Application_ID 형태의 주석삽입은 운영 편의성을 제공한다.
SELECT /* com.app.mem.select */ * FROM tbl~;
- TAB 과 개행에 규칙을 부여하라.
(EX: TAB=4 SPACE, ‘,’는 컬럼뒤, FROM/JOIN/WHERE 등은 개행)
SELECT
COL1,
COL2
FROM TBL_A
WHERE ID=123;
- 연산자 다음은 한칸 띄운다.
- WHERE 이나 JOIN 에 사용되는 컬럼의 데이터 타입은 같게 한다.
- JOIN 시에도 DRIVING 테이블의 컬럼은 우변으로 DRIVEN 테이블은 좌변으로 위치한다.
- 서브쿼리보다는 JOIN 을 사용한다.
SELECT ID
FROM TBL_A
INNER JOIN TBL_B
ON TBL_B.COL1 = TBL_A.COL1 (O)
ON TBL_A.COL1 = TBL_B.COL1 (X)
WHERE ~;
- 되도록이면 등가비교(=)로 처리한다.
- 부정형 보다는 긍정형으로 SQL 작성한다. (NOT IN(X))
- 되도록이면 수행되는 선행테이블 순서대로 조인한다. 가독하기 쉽다.
- ALIAS RULE 을 적용하라.(A~Z 할지, 업무규약 약칭을 사용할지)
- SELECT * 구문은 사용하지 않는다.
33
- 특별한 경우가 아니면 STORED ROUTINE 은 지양한다.
- A4 여러장 짜리의 복잡한 JOIN 을 활용한 단일쿼리는 되도록 지양한다.
가독성도 떨어지고, 운영상의 문제을 만드는 경우가 많다.
- SQL 작성 후 반드시 EXPLAIN 을 통해서 원하는 실행계획대로 수행하는지 확인한다.
- 가급적이면 불필요한 시스템 함수사용은 자제한다.
APPLICATION 영역에서 할일이지 DBMS 에서 처리할 일은 아닌것들이 더 많다.
- 전체집합처리보다 부분집합처리로 결과절을 만들려고 노력하자.

More Related Content

What's hot

The InnoDB Storage Engine for MySQL
The InnoDB Storage Engine for MySQLThe InnoDB Storage Engine for MySQL
The InnoDB Storage Engine for MySQL
Morgan Tocker
 

What's hot (20)

MySQL Advanced Administrator 2021 - 네오클로바
MySQL Advanced Administrator 2021 - 네오클로바MySQL Advanced Administrator 2021 - 네오클로바
MySQL Advanced Administrator 2021 - 네오클로바
 
MariaDB MaxScale monitor 매뉴얼
MariaDB MaxScale monitor 매뉴얼MariaDB MaxScale monitor 매뉴얼
MariaDB MaxScale monitor 매뉴얼
 
MaxScale이해와활용-2023.11
MaxScale이해와활용-2023.11MaxScale이해와활용-2023.11
MaxScale이해와활용-2023.11
 
MariaDB Administrator 교육
MariaDB Administrator 교육 MariaDB Administrator 교육
MariaDB Administrator 교육
 
Maria db 이중화구성_고민하기
Maria db 이중화구성_고민하기Maria db 이중화구성_고민하기
Maria db 이중화구성_고민하기
 
Keepalived+MaxScale+MariaDB_운영매뉴얼_1.0.docx
Keepalived+MaxScale+MariaDB_운영매뉴얼_1.0.docxKeepalived+MaxScale+MariaDB_운영매뉴얼_1.0.docx
Keepalived+MaxScale+MariaDB_운영매뉴얼_1.0.docx
 
MariaDB Optimization
MariaDB OptimizationMariaDB Optimization
MariaDB Optimization
 
InnoDB MVCC Architecture (by 권건우)
InnoDB MVCC Architecture (by 권건우)InnoDB MVCC Architecture (by 권건우)
InnoDB MVCC Architecture (by 권건우)
 
Running MariaDB in multiple data centers
Running MariaDB in multiple data centersRunning MariaDB in multiple data centers
Running MariaDB in multiple data centers
 
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
 
My sql failover test using orchestrator
My sql failover test  using orchestratorMy sql failover test  using orchestrator
My sql failover test using orchestrator
 
New optimizer features in MariaDB releases before 10.12
New optimizer features in MariaDB releases before 10.12New optimizer features in MariaDB releases before 10.12
New optimizer features in MariaDB releases before 10.12
 
The InnoDB Storage Engine for MySQL
The InnoDB Storage Engine for MySQLThe InnoDB Storage Engine for MySQL
The InnoDB Storage Engine for MySQL
 
MySQL InnoDB Cluster 소개
MySQL InnoDB Cluster 소개MySQL InnoDB Cluster 소개
MySQL InnoDB Cluster 소개
 
Maxscale switchover, failover, and auto rejoin
Maxscale switchover, failover, and auto rejoinMaxscale switchover, failover, and auto rejoin
Maxscale switchover, failover, and auto rejoin
 
Intro ProxySQL
Intro ProxySQLIntro ProxySQL
Intro ProxySQL
 
Get to know PostgreSQL!
Get to know PostgreSQL!Get to know PostgreSQL!
Get to know PostgreSQL!
 
ProxySQL High Avalability and Configuration Management Overview
ProxySQL High Avalability and Configuration Management OverviewProxySQL High Avalability and Configuration Management Overview
ProxySQL High Avalability and Configuration Management Overview
 
MySQL_MariaDB로의_전환_기술요소-202212.pptx
MySQL_MariaDB로의_전환_기술요소-202212.pptxMySQL_MariaDB로의_전환_기술요소-202212.pptx
MySQL_MariaDB로의_전환_기술요소-202212.pptx
 
PostgreSQL Deep Internal
PostgreSQL Deep InternalPostgreSQL Deep Internal
PostgreSQL Deep Internal
 

Similar to MySQL_SQL_Tunning_v0.1.3.docx

[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
Seok-joon Yun
 
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
cranbe95
 
DBMS 아키텍처
DBMS 아키텍처DBMS 아키텍처
DBMS 아키텍처
HaksunLEE6
 
Cubrid Inside 5th Session 3 Migration
Cubrid Inside 5th Session 3 MigrationCubrid Inside 5th Session 3 Migration
Cubrid Inside 5th Session 3 Migration
CUBRID
 
AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)
AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)
AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)
Amazon Web Services Korea
 
The nosql echossytem
The nosql echossytemThe nosql echossytem
The nosql echossytem
종석 박
 

Similar to MySQL_SQL_Tunning_v0.1.3.docx (20)

[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQL
 
From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQL
 
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
 
Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014
 
Pg day seoul 2016 session_02_v1.0_ff
Pg day seoul 2016 session_02_v1.0_ffPg day seoul 2016 session_02_v1.0_ff
Pg day seoul 2016 session_02_v1.0_ff
 
DBMS 아키텍처
DBMS 아키텍처DBMS 아키텍처
DBMS 아키텍처
 
Infiniflux introduction
Infiniflux introductionInfiniflux introduction
Infiniflux introduction
 
Migration to Azure Database for MySQL
Migration to Azure Database for MySQLMigration to Azure Database for MySQL
Migration to Azure Database for MySQL
 
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
[2015-06-12] Oracle 성능 최적화 및 품질 고도화 1
 
CUBRIDInside_5th_CUBRID_Migration Process_DHLee
CUBRIDInside_5th_CUBRID_Migration Process_DHLeeCUBRIDInside_5th_CUBRID_Migration Process_DHLee
CUBRIDInside_5th_CUBRID_Migration Process_DHLee
 
Cubrid Inside 5th Session 3 Migration
Cubrid Inside 5th Session 3 MigrationCubrid Inside 5th Session 3 Migration
Cubrid Inside 5th Session 3 Migration
 
MySQL Deep dive with FusionIO
MySQL Deep dive with FusionIOMySQL Deep dive with FusionIO
MySQL Deep dive with FusionIO
 
GoldenGate for MySQL 설치 시 필요한 사항
GoldenGate for MySQL 설치 시 필요한 사항GoldenGate for MySQL 설치 시 필요한 사항
GoldenGate for MySQL 설치 시 필요한 사항
 
AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)
AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)
AWS CLOUD 2018- Amazon Aurora  신규 서비스 알아보기 (최유정 솔루션즈 아키텍트)
 
The nosql echossytem
The nosql echossytemThe nosql echossytem
The nosql echossytem
 
Database 튜닝 교육 110124
Database 튜닝 교육 110124Database 튜닝 교육 110124
Database 튜닝 교육 110124
 
게임 서비스를 위한 AWS상의 고성능 SQL 데이터베이스 구성 (이정훈 솔루션즈 아키텍트, AWS) :: Gaming on AWS 2018
게임 서비스를 위한 AWS상의 고성능 SQL 데이터베이스 구성 (이정훈 솔루션즈 아키텍트, AWS) :: Gaming on AWS 2018게임 서비스를 위한 AWS상의 고성능 SQL 데이터베이스 구성 (이정훈 솔루션즈 아키텍트, AWS) :: Gaming on AWS 2018
게임 서비스를 위한 AWS상의 고성능 SQL 데이터베이스 구성 (이정훈 솔루션즈 아키텍트, AWS) :: Gaming on AWS 2018
 
Tajo and SQL-on-Hadoop in Tech Planet 2013
Tajo and SQL-on-Hadoop in Tech Planet 2013Tajo and SQL-on-Hadoop in Tech Planet 2013
Tajo and SQL-on-Hadoop in Tech Planet 2013
 
[pgday.Seoul 2022] PostgreSQL구조 - 윤성재
[pgday.Seoul 2022] PostgreSQL구조 - 윤성재[pgday.Seoul 2022] PostgreSQL구조 - 윤성재
[pgday.Seoul 2022] PostgreSQL구조 - 윤성재
 

MySQL_SQL_Tunning_v0.1.3.docx

  • 1. 1 MySQL/MariaDB SQL Tunning (For Beginner) ㈜ 네오클로바 DB 기술지원팀
  • 2. 2
  • 3. 3 목차 1. 개요 2. DBMS Tunning 을 위해 알아야 할것들. 2-1. DBMS 성능개선 (모델링 > 시스템 > SQL) 2-2. MySQL/MariaDB Architecture 2-3. InnoDB 구조 2-4. Primary Key 와 Secondary Key 2-5. Join 방식 2-6. Limit 3. SQL 실행계획 3-1. 쿼리실행과정 3-2. 통계정보 3-3. 옵티마이저 3-4. Explain 3-5. Profiling 4. Explain 파해치기 5. SQL 을 위한 인덱스전략 6. SQL 작성가이드 7. 사례로보는 SQL Tunning - 별첨
  • 4. 4 1. 개요 오픈소스의 관계형 데이터베이스 관리 시스템(RDBMS)에서 가장 많이 쓰이는 소프트웨어가 MySQL 과 MariaDB 이다. MySQL 은 MySQL AB 가 관리/지원하다 2008 년 썬마이로그시스템즈에 인수되었으며, 2010 년 오라클이 썬마이크로시스템즈를 인수하면서 현재 오라클이 MySQL 소유권을 가지고 있습니다. MariaDB 는 MySQL 과 동일한 소스코드를 기반으로 한 GPL2 라이선스를 따른다. 썬이 오르클에 인수되면서, 2009 년 Michael Monty Widenius 가 썬을 떠나서 “Monty Program AB”사를 창립합니다. 2012 년 비영리단체인 “Maria DB Foundation”을 설립해서 MariaDB 를 GPL 라이선스로 배포하기 시작했다. 이후 SkySQL 이 “Monty Program AB”를 합병하여 2014 년 “MariaDB Corporation”으로 회사명을 변경하여 오늘에 이르고 있습니다. “MariaDB Foundation”(www.mariadb.org)은 비영리단체로 GPL 라이선스를 소유하고 있으며, “MariaDB Corporation”(www.mariadb.com)은 영리단체입니다.
  • 5. 5 MySQL fork - MariaDB - Percona Server - Drizzle - WebScaleSQL - Facebook - Twitter - AliSQL - 등등등 2015’
  • 6. 6 2. DBMS Tunning 을 위해 알아야 할 것들 2.1. DBMS 의 성능개선 데이터베이스의 성능개선은 DBMS 자체, Application, OS 조정등을 통하여 최적의 자원으로 최적의 성능(시간/응답속도)를 얻을 수 있도록 개선하는 작업을 의미합니다. 그렇다면 성능개선의 관점에서 보면 다음과 같은 목적을 가질 수 있다. 관점 목적 내용 대기 분석 (Wait time) 대기시간 최소화 - 동시 세션이 데이터베이스에 접근 할 경우 - DB 자원을 취득하기 위해 대기하는 시간 분석 응답 시간 (Response Time) 응답시간 최소화 - SQL 실행 후 결과가 피드백 되기까지의 시간 분석 비율 분석 (Hit Ratio) Hit Rate 최대화 - 데이터베이스의 버퍼 캐쉬의 히트 율 분석, - DISK IO 를 최소화 하도록 메모리 히트 율 향상 이러한 성능개선의 목적을 달성하기 위해서는 크게 세가지의 개선분야(요소)로 구분할 수 있다. 개선요소 설 명 사 례 DB 설계 (모델링관점) - 데이터베이스 설계 단계에서 성능을 고려하여 설계 - 데이터 모델링 (논리모델) - 인덱스설계, 데이터파일, 테이블, 스페이스 설계(물리모델) - 데이터베이스 용량 산정 - 정규화/반정규화 - 분산파일배치 DBMS 환경 (환경 측면) - 성능을 고려하여 메모리나 블록 크기 등을 지정 - CPU, 메모리 I/O 에 관한 관점 - Buffer, Cache 크기 - 고성능 Disk OS, HW, N/W SQL 요소 (응용프로그램 측면) - SQL 작성 시 성능 고려 - join, Indexing, SQL Execution Plan - Hash / Join - Hint 성능에 영향을 미치는 세가지 개선요소들 중에서 어느 것 하나 무시할 수 없지만, 우선순위로 보자면 DB 모델링 > DBMS 환경구성 > Application SQL 작성 순서로 보는 게 일반적이다. 다음은 각 요소 별 주요 튜닝항목들이다 [DB 설계 튜닝] 구분 내용 테이블의 분할/통합 - 논리적으로는 통합된 단일 테이블이지만 DBMS 가 지원하는 파티션기능 적용함으로써 액세스 효율화로 DB I/O 를 최적화 할 수 있음 - - DBMS 에 따라 파티션기능이 제약되는 경우에는 테이블의 수평분할 고려 - 액세스 패턴에 따라 단일 테이블을 1:1 로 수직분할을 고려할 수 있음
  • 7. 7 식별자 지정 - 본질 식별자와 인조식별자의 선택에 따라 정보의 상속과 단절에 영향을 줄 수 있음 효율적인 인덱스 전략 - 최소한의 인덱스로 최대의 효과를 얻을 수 있는 최적의 인덱스 구조 수립 - 인덱스 분포도 15% 이하 유용 유도(15% 이상이면 Full Scan 유리) 적절한 데이터타입 선택 - 조인 시 연결되는 컬럼의 데이터 타입이 다른 경우 내부적인 변형에 의해 인덱스가 있음에도 불구하고 활용을 못해 조인순서나 조인방식을 달리 선택하는 비효율 발생 할 수 있음 [DBMS 환경 튜닝] 구분 내용 CPU 튜닝 - Peak Time 시에 60~70%의 사용량 유지 권고 - CPU 증설 또는 CPU 과다 점유하는 프로세스를 찾아서 해결 Memory 튜닝 - DBMS 의 사용 메모리의 최적화 필요 - 업무를 가능하게 하기 위한 충분한 메모리 확보 - DBMS 메모리 + Session 메모리 Disk I/O 튜닝 - I/O 를 분산시킬 수 있도록 데이터 파일의 재배치 - Raid 를 이용-> I/O 가 많이 일어나는 것은 Raid 1/0 에 배치 Network 튜닝 - Ping, Ftp 를 이용하여 응답시간 분석 [SQL 튜닝] 구분 내용 옵티마이저에 대한 이해기반 SQL 작성 - RBO (Rule Based Optimizer): 통계정보가 없는 상태에서 미리 정해진 Rule 에 따라 실행계획 수립 - CBO (Cost Based Optimizer): 통계정보로부터 모든 Access Path 를 고려하여 실행계획 수립 - 옵티마이저가 선택한 실행계획을 확인하고 최적화된 실행계획 수립이 이루어지도록 Factor 부여 힌트사용 - 옵티마이저가 항상 최적화된 실행계획을 수립하는 것은 아니므로 힌트를 사용하여 원하는 실행계획으로 유도 USE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list]) IGNORE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list) FORCE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list) 부분범위 처리 - 조건을 만족하는 전체집합이 아닌 일부분만 액세스하고도 결과를 리턴 할 수 있도록 하여 온라인 프로그램에서 응답시간(Response Time)을 최소화 할 수 있음 인덱스 활용 - 인덱스가 있음에도 불구하고 SQL 을 잘못 기술함으로써 무용지물로 만드는 오류 제거
  • 8. 8 조인방식/ 조인순서 - 동일한 SQL 문이라도 조인방식과 조인순서에 따라 처리속도는 매우 큰 차이를 가져올 수 있으므로 작성한 SQL 이 어떤 실행계획으로 수립되는 지 반드시 확인 후 조정 다중처리 (Array Processing) - 배치작업의 경우 한번의 DBMS 호출로 여러 건을 동시에 처리할 수 있는 다중처리 활용 병렬쿼리 (Parallel Query) - 배치작업의 경우 하나의 SQL 을 여러 개의 CPU 가 병렬로 분할 처리하게 함으로써 처리속도 향상 가져옴(단 Parallel 지원 DBMS) Prepared SQL 사용 - 조건절에 입력된 값을 먼저 Binding 한 후 실행계획을 수립하는 Dynamic SQL 은 파싱 부하가 커지므로 입력 값을 Binding 하기 전에 실행계획을 수립하는 Static SQL 을 가급적 사용하도록 함. 그러나 DBMS 마다 차이가 존재함. MySQL 은 Prepared(보안측면 효과) 권고 결국 ➔ 얼마나 적은 CPU, Memory, Disk IO 를 통해서 동일한 결과를 가져올 것인가? ➔ 얼마나 빨리 동일한 결과를 가져올 것인가? ➔ 대부분 Disk IO 부하에서 병목현상이 발생함으로 적은 Page Read IO 가 우선적으로 이해.
  • 9. 9 2.2. MySQL / MariaDB Architecture 1. MySQL Architecture 2. MariaDB Architecture
  • 10. 10 2.3. INNODB 구조 - System Tablespace(space ID=0), Innodb Tables-per table(space ID>0) : 32bit space ID 할당 - ibdata 정보를 확인할 때 innochecksum utility 확인 (offline 수행) - [root@VM1 ~]# ./innochecksum --page-type-summary /usr/local/mysql/data/ibdata1 - File::/usr/local/mysql/data/ibdata1 - ================PAGE TYPE SUMMARY============== - #PAGE_COUNT PAGE_TYPE - =============================================== - 39 Index page - 427 Undo log page - 6 Inode page - 0 Insert buffer free list page - 5793 Freshly allocated page - 1 Insert buffer bitmap - 131 System page - 1 Transaction system page - 2 File Space Header - 0 Extent descriptor page - 0 BLOB page - 0 Compressed BLOB page - 0 Other type of page - =============================================== - Additional information: - Undo page type: 128 insert, 299 update, 0 other - Undo page state: 0 active, 224 cached, 0 to_free, 1 to_purge, 0 prepared, 202 other 1. Tablespace - Row > Page(16K) > Extent(64 pages) > Segment > Tablespace 구조(디렉토리= 데이터베이스) - .frm = 테이블구조(MyISAM 과 동일) - .ibd = 테이블 Data Internal Data Dictionary Replication Info Insert Buffer, DoubleWirte Buffer Undo Logs System Tablespace ibdata[n] files Innodb_file_per_tabl e .frm file .ibd file InnoDB Tables ---[n] .frm+.idb (per tables) DM Saved Data
  • 11. 11 2. Pages (16Kbyte, 2^32 * 16K=64TB) – Double linked list 구조 FIL Header(38) Row Datas (16,338) … Row Offset Array FIL Trailer(8) 3. File per table 시 ibd 파일 구조 4. Index Pages 자세한 사항은 아래를 참고하세요. 참고 : https://blog.jcole.us/2013/01/03/the-basics-of-innodb-space-file-layout/ Checksum(4) Offset(Page Number)(4) Previous Page(4) Next Page(4) LSN for last page modification(4) Page Type(2) Flush LSN(0 except space 0 page 0)(8) Space ID(4) Old-style Checksum(4) Low 32bits of LSN(4)
  • 12. 12 2.4. Primary Key 와 Secondary Key MySQL/MariaDB 는 PK 를 생성하지 않아도 내부적으로 반드시 6bytes 의 ROWID 값에 대한 더미용 PK 를 생성한다. 이 값은 Secondary Index 에 포함되어 Lookup 시 활용한다. 1. Primary Key or Internal 컬럼 ROW_ID (6byte) 2. Secondary Key Contain Primary Key 확인) create table t (id int primary key, nm varchar(10)); create index t_idx1 on t(nm); create index t_idx1 on t(nm, id); 두 인덱스의 차이? 결과는?
  • 13. 13 2.5. Join - 모든 SQL 처리는 싱글 코어처리(병렬처리 안됨) - 오직 Nested Loop Join 만으로 join 처리 - 연결고리가 되는 컬럼을 사용하여 바깥쪽 테이블(driving table)과 안쪽 테이블(driven table) 접근. - 바깥쪽 테이블의 한 row 의 컬럼을 읽고 조인되는 컬럼값을 통해 안쪽 테이블의 한 row 에 대한 조인 컬럼에 대한 결과값을 만들고, 다시 바깥쪽 테이블의 다음의 한 Row 를 읽고 연결된 안쪽테이블의 컬럼을 읽어갑니다. 이렇게 바깥 테이블에 더 이상 남은 행이 없을 때 까지 연산을 수행합니다. - 단 어떤 테이블을 먼저 읽을지에 대한 판단은 옵티마이저가 결정할 문제이며, 순차적 조인 힌트인 STRAIGHT_JOIN 통해서 사용자가 작성한 테이블이의 순서대로 JOIN 을 수행합니다. SELECT STRAIGHT_JOIN COUNTRY.COUNTRY_NAME ,CITY.CITY_NAME FROM COUNTRY INNER JOIN CITY ON CITY.COUNTRY_NAME = COUNTRY.COUNTRY_NAME WHERE 1=1; // COUNTRY >> CITY 이는 Oracle 의 ORDERED 와 동일하게 작동하는 HINT 이다.(SELECT /*+ORDERED */ … )
  • 14. 14 2-6. Limit 1. 한 테이블의 최대 컬럼 수 ? - MySQL 5.6.9 이전 – 1000 개 - 이후는 1017 개까지 (5.7 상의 가상컬럼 포함) 2. 한 테이블의 최대 인덱스 수? - 1 개 Primary Index + 64 개 Secondary Index = 최대 65 개 가능 그럼 MSSQL 은? 오라클은? 3. 컬럼의 인덱스 최대 키 길이(Bytes)는? - 기본 767byte - innodb_large_prefix=1 인 경우 3072 Byte 까지 지원 - row_format=DYNAMIC / COMPRESSED - 멀티컬럼의 경우는 전체 키조합 길이도 3072byte 까지 지원. - innodb_page_size=16K 면 3072byte, 8K 면 1536byte, 4K 면 768byte 로 변경됨. (MySQL 5.7.6 부터 innodb_page_size 변경가능) 주의) master / slave 동일하게 설정. 4. 한 Row 의 최대 컬럼 합 Byte 는? - 65,535byte 5. 단일 Tablespace 의 최대크기? - 64TB 6. 최대 Undo(데이터 변경 trx) 가능 Transaction 수? - undo log file 은 최대 128 개, 한 개 파일당 1023 trx 지원 : 128*1023 개 - 임시테이블 사용여부에 따라 다름. 참고: http://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html
  • 15. 15 3. SQL 실행계획 3.1. 쿼리 실행과정 - Query Cache : 사용 설정 시 캐시에 저장데이터 있으면 바로 결과 반환, 없으면 다음. - Parser : 쿼리를 토큰(parse tree)으로 분리해서 SQL 엔진 인식가능형태 변환. Syntax Error 확인. - Preprocesser : 쿼리에 구조적 문제없는지 체크. 객체 존재여부 권한확인 Filtering. - Optimizer : 쿼리변환, 최소 Cost, 실행계획 개선작업 수행 후 Query Execution Plan 생성. - Query Execution Engine : plan 순서대로 Storage Engine 에게 API Calls 수행함. (ex: innodb 에게 tmp tbl 만들라, where 조건수행후 읽어와라, 저장하라, 최종 set 생성 등) 결국 실행엔진에서 비효율발생 가능성이 있음. - Storage Engine : 실행엔진이 시키는 일(API Call)을 수행.
  • 16. 16 3.2. 통계정보 MySQL/MariaDB 에서는 CBO 기반 옵티마이저의 성능을 향상시키기 위해서 통계정보를 활용한다. 그러나 MySQL 의 통계정보에 대하여 몇가지 알아야 할 사항이 있다. MySQL 5.5 이하 버전에서는 Cardinality 정보만 관리하며, 타 DBMS 와 다르게 다양한 이벤트를 통해서 상당히 동적으로 통계정보가 변경됩니다. Cardinality 는 인덱스 컬럼 값의 Unique 값 수를 의미합니다. 유사한 정보로서 Histogram 은 인덱스 존재여부와 상관없이 원하는 컬럼의 값 분포정보입니다. 이 통계정보는 information_schema.statistics 테이블에 저장됩니다. 이를 통해 어떤 인덱스를 사용해서 데이터를 조회할 지를 결정합니다. 통계정보가 잘못된 경우는 옵티마이저가 최고의 실행계획을 찾지 못할 수 있습니다. [MySQL/MariaDB 통계정보] 구분 내용 수집량 소량 페이지 샘플링 분석 후 예측(InnoDB), 인덱스 전체페이지(MyISAM) Cardinality = Total Rows / Num_Distinct information_schema.statistics userstat = ON (Enable) 정확도 예측기반(InnoDB), 거의 실측치(MyISAM) 통계수집 Eve nt 동적 Event(InnoDB) - 테이블 첫 오픈시점 - SHOW TABLE STATUS LIKE ‘~’;시 - SHOW INDEX FROM ~; - DDL 수행 - innodb_stats_on_metadata=1 경우 information_schema Data 조회 시 - Table Data 6.25%(5.5), 10%(5.6) 변경 시 명시적 (MyISAM) - ANALYZE TABLE ~; 영구적 통계정보 - 5.5 이하 불가능 - 5.6 이상에서부터 지원 mysql.innodb_table_stats mysql.innodb_index_stats innodb_stats_persistent=0/1 #위 테이블에 영구저장 여부 innodb_stats_auto_recalc=0/1 #자동수집 여부 히스토그램 - MariaDB 10.0 부터 지원, mysql 미지원 - Mysql.table_stats, mysql.column_stats, mysql.index_stats user_stat_tables=NEVER histogram_size=255 (0~255) histogram_type=SINGLE_PREC_HB / DOUBLE_PREC_HB 옵티마이저의 optimizer_use_condition_selectivity=1~5 1(기본), 2(인덱스선택도), 3(모든컬럼선택도), 4(히스토그램), 5(4+레코드 샘플링)
  • 17. 17 3.3. 옵티마이저(Optimizer) MySQL./MariaDB 의 옵티마이저는 비용기반옵티마이저(CBO)로 각 테이블의 데이터가 어떤 분포로 저장돼 있는지 통계정보를 참조하여 최적의 실행계획을 수립하는 작업을 담당한다. 1. 사용자의 요청 쿼리 문장을 최소비용으로 가장 빠르게 처리할 지 결정 2. 쿼리를 변환 3. 비용을 최적화 4. 실행계획을 개선 합니다. 옵티마이저는 비용예측 시 캐시의 영향은 고려하지 않습니다. 다만 모든 데이터에 대한 접근(Read)은 실제 Disk IO 가 필요하다고 가정하고 비용을 산정합니다. 또한 스토어드 루틴(프로시저, 함수)등 자신의 통제하에 있지 않는 명령에 대해서는 비용을 고려하지 않습니다. 또한 옵티마이저는 가능한 모든 실행가능한 계획의 비용을 예측하는 것이 아니므로, 선택한 실행계획이 최적의 실행계획이 아닐 수도 있습니다. 다음은 옵티마이저가 최적화를 알고 있는 일부 몇가지 경우입니다. 1. 조인순서를 조정 2. OUTER JOIN 을 INNER JOIN 으로 변경 3. 불가능/불필요 조건제거 4. COUNT(), MIN(), MAX()최적화 5. 쿼리간소화 및 상수화 6. 커버링 인덱스 선택 7. 서브쿼리 최적화 8. 조기종료처리
  • 18. 18 3.4. 실행계획(Explain) MySQL 에서는 쿼리의 실행계획을 확인하는 방법으로 Explain 명령어를 이용합니다. 수행하고자 하는 SQL 문장앞에 Explain 을 추가 함으로서 SQL 에 대한 실행계획을 확인합니다. 다음의 예를 보자. mysql> EXPLAIN SELECT HOST, USER, PASSWORD FROM mysql.user; id select_type table type possible_keys key key_len ref rows Extra ------ ----------- ------ ------ ------------- ------ ------- ------ ------ -------- 1 SIMPLE user ALL (NULL) (NULL) (NULL) (NULL) 7 (NULL) Explain 을 통해 SQL 에 대한 실행계획 결과를 보면 id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra 등의 정보를 볼 수 있다. Explain 은 select 문에 사용된 각 테이블당 하나의 행을 리턴한다. 또한 나열된 순서는 MySQL 이 쿼리의 처리를 위해 수행한 순서를 가지고 출력된다. 타 DBMS 의 실행계획을 보면 단일테이블의 데이터를 찾는 순서가 Index 를 거쳐서 데이터 블록을 찾는 순서에 대해서 좀더 세부적으로 Plan 을 볼 수 있는 것에 비해 조금 아쉬운 부분이 있다. 참고로 MySQL 에서 EXPLAIN = DESCRIBE = DESC 는 동의어(synonyms) 입니다. {EXPLAIN | DESCRIBE | DESC } tbl_name [col_name | wild]; {EXPLAIN | DESCRIBE | DESC } {EXTENDED | PARTITIONS} SELECT select_options; Explain 을 통해서 우리는 - 옵티마이저가 어떻게 쿼리를 변환했는지 - 우리가 원하는 조인 순서가 맞는지 - 해당 테이블의 인덱스를 적절히 사용하고 있는지 - 사용하는 인덱스를 통해서 힌트를 추가하거나 SQL 을 수정해야 하는 것은 아닌지 를 확인해야 합니다. 결과셋은 하나의 테이블을 읽을 때의 정보를 보여주며 각 컬럼의 구성은 다음과 같다. 컬럼 설명 id 한 테이블의 SELECT 번호, 쿼리내의 SELECT 의 구분번호. select_type SELECT 의 타입(SIMPLE, PRIMARY, UNION, SUBQUERY, DRIVED…) table 해당 table 명 type 조인의 타입(system, const, eq_ref, ref, ref_or_null, index_merge…) passible_keys 테이블의 사용가능한 인덱스들 명. key Passible_keys 값들 중에서 실제 사용한 인덱스(key) Key_len Where 절에 사용한 인덱스의 길이(byte) ref 행을 추출하는데 key 와 함께 사용한 컬럼이나 상수 rows 쿼리수행을 위해 검사대상 예상행수 filtered 조건절에 의해 필터링되는 행의 비율 Extra 쿼리를 해석한 추가적인 정보
  • 19. 19 3.5. 프로파일링(Profiling) 프로파일은 MySQL 의 현 세션에서 수행했던 최근 SQL 에 대한 Resource 사용정보를 보여준다. SQL 수행하는데 어느 부분에서 자원을 많이 사용하며 어디에 병목이 발생하고 있는지 확인할 수 있다. 이는 실행계획과 더불어 자원의 효율적인 사용을 위해서 참고할 만한 자료로 사용할 수 있다. SELECT ~; SHOW PROFILES; SHOW PROFILE [ALL | BLOCK TO | CONTEXT SWITCHES | CPU | IPC|MEMORY|PAGE FAULTS|SOURCE|SWAPS] [FOR QUERY n] [LIMIT row_count [OFFSET offset]] mysql> show profiles; +----------+------------+-----------------------+ | Query_ID | Duration | Query | +----------+------------+-----------------------+ | 1 | 0.00027225 | select * from test.t1 | mysql> show profile; +----------------+----------+ | Status | Duration | +----------------+----------+ | starting | 0.000074 | | query end | 0.000010 | | closing tables | 0.000005 | | freeing items | 0.000018 | | cleaning up | 0.000017 | +----------------+----------+ Mysql> show profile ALL; # 결과는? SHOW PROFILES/PROFILE 은 5.6.7 에서 deprecated 되었습니다. 향후에는 performance_schema 를 활용하시기 바랍니다. # performance_schema 를 시작/설정하는 방법 https://dev.mysql.com/doc/refman/5.6/en/performance-schema-startup-configuration.html # performance_schema 로 프로파일링 하는 방법 https://dev.mysql.com/doc/refman/5.6/en/performance-schema-query-profiling.html
  • 20. 20 4. Explain 파해치기 MySQL 5.6.3 까지는 UPDATE, INSERT, DELETE, REPLACE 등에 대한 실행계획은 지원하지 않아서 SELECT 절로 변환해서 확인했다. 그러나 5.6.3 부터는 모두 지원한다. Explain 수행결과에 컬럼들의 정보는 다음과 같다. 4.1. id 한 테이블의 SELECT 번호, 쿼리내의 SELECT 의 구분번호를 의미하며, 쿼리의 처리하는 테이블의 순서로 이해하면 된다. 여러 개의 테이블 조인 시에 테이블의 개수만큼 실행계획 레코드가 생기지만 ID 는 동일하게 부여. 4.2. select_type 어떤유형의 select 타입인지를 의미한다. 다음의 표를 참고하세요. Select_type 설명 SIMPLE UNION 이나 SUBQUERY 를 사용하지 않는 단순 SELECT PRIMARY 쿼리 제일 바깥쪽에 있는 SELECT DERIVED SELECT 절로 추출된 테이블로 FROM 절 내부의 SUBQUERY 의미 UNION UNION 절에서 두번째 이후 SELECT 단위, 첫번째는 DRIVED DEPENDENT UNION 내부쿼리가 외부값을 참조하는 UNION 절 테이블 UNION RESULT UNION 결과값에 대한 임시 테이블 SUBQUERY FROM 절 이외의 서브쿼리의미. DEPENDENT SUBQUERY 서브쿼리가 바깥쪽 SELECT 절의 정의 컬럼값을 참조 UNCHACHEABLE SUBQUERY 서브쿼리결과가 외부쿼리의 변하는 값에 따라 매번 새로생성형태. UNCHACHEABLE UNION 캐싱이 불가능한 요소를 포함한 UNION 개별 type 에 대한 예제는 다음과 같다. mysql> EXPLAIN select * from t1 where id=1; id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 N const PRIMARY PRIMARY 4 const 1 100 N mysql>EXPLAIN SELECT t2.nm ,(SELECT COUNT(*) FROM t2 WHERE nm=nm) AS cnt FROM t1 INNER JOIN t2 ON t2.nm=t1.nm WHERE t2.del_yn='Y'
  • 21. 21 id select_type tabl e pa rtit io ns ty pe possible_ keys key key_len ref rows filtered Extra 1 PRIMARY t2 N AL L PRIMAR Y N N N 3 33.33 Using where 1 PRIMARY t1 N AL L N N N N 2 50 Using where; Using join buffer (Block Nested Loop) 2 SUBQUER Y N N N N N N N N N Select tables optimized away mysql> EXPLAIN SELECT t1.nm ,COUNT(*) AS cnt FROM ( SELECT 'a' AS nm ,t2.del_yn FROM t2 GROUP BY del_yn ) a INNER JOIN t1 ON t1.nm = a.nm WHERE a.nm='a' GROUP BY t1.nm; id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 N ALL N N N N 2 50 Using where 1 PRIMARY <derived2 > N ref <auto_key0> <auto_key0 > 5 const 1 100 N 2 DERIVED t2 N ALL N N N N 3 100 Using temporary; Using filesort
  • 22. 22 mysql>EXPLAIN SELECT t1.nm FROM t1 GROUP BY nm UNION SELECT t2.nm FROM t2 id select_type table partition s type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 N ALL N N N N 2 100 Using temporary; Using filesort 2 UNION t2 N index N PRIMARY 302 N 3 100 Using index N UNION RESULT <union1,2> N ALL N N N N N N Using temporary mysql> EXPLAIN SELECT COUNT(*) FROM ( SELECT '1' AS C1, t1.nm FROM t1 GROUP BY nm UNION SELECT '2', t2.nm FROM t2 UNION SELECT '3', t2.nm FROM t2 WHERE del_yn='Y' ) B 1 PRIMARY <derived2> N ALL N N N N 7 100 N 2 DERIVED t1 N ALL N N N N 2 100 Using temporary; Using filesort 3 UNION t2 N index N PRIMARY 302 N 3 100 Using index 4 UNION t2 N ALL N N N N 3 33.33 Using where N UNION RESULT <union2,3,4> N ALL N N N N N N Using temporary mysql> EXPLAIN SELECT nm FROM t1 WHERE EXISTS (SELECT 1 UNION ALL SELECT 1 FROM t2 WHERE t2.nm = t1.nm ) id select_type table partition s type possible_keys key key_le n ref rows filtere d Extra 1 PRIMARY t1 N ALL N N N N 2 100 Using where 2 UNCACHEABL E SUBQUERY N N N N N N N N N No tables used 3 DEPENDENT UNION t2 N eq_ref PRIMARY PRIMARY 302 test.t1.nm 1 100 Using index mysql> EXPLAIN SELECT *
  • 23. 23 FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.nm = t1.nm); id select_type table partition s type possible_key s key key_len ref rows filtere d Extra 1 PRIMARY t1 N ALL N N N N 2 100 Using where 2 DEPENDENT SUBQUERY t2 N eq_ref PRIMARY PRIMARY 302 test.t1.nm 1 100 Using index mysql> EXPLAIN SELECT * FROM t1 WHERE id = (SELECT @status FROM t2 WHERE t2.nm='a'); id select_type tabl e partitions type possible_key s key key_len ref rows filtere d Extra 1 PRIMARY t1 N ALL N N N N 2 100 Using where 2 UNCACHEABLE SUBQUERY t2 N const PRIMARY PRIMARY 302 const 1 100 Using index mysql> EXPLAIN SELECT * FROM t1 WHERE id IN (SELECT @status FROM t2 WHERE t2.nm='a' UNION ALL SELECT 1); id select_type table partition s type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 N ALL N N N N 2 100 Using where 2 DEPENDENT SUBQUERY t2 N const PRIMARY PRIMARY 302 const 1 100 Using index 3 DEPENDENT UNION N N N N N N N N N No tables used
  • 24. 24 4.3. table SELECT 절에 대한 데이터를 읽어들이는 테이블명을 말한다. 해당 테이블명 또는 테이블에 대한 Alias, 서브쿼리등으로 인한 가상의 테이블(<derived#n>을 의미한다. 단순한 쿼리의 경우는 쉽게 확인이 가능하나, 복잡한 쿼리의 경우는 이해가 다소 어렵다. 4.4. type 조인타입 또는 테이블 접근방식(Access type)으로 이해하면 쉽다. 풀스캔을 하는지, 인덱스를 사용하는지, unique 키를 사용하는지 등을 판단하여 성능판단의 주요지표이며 아래의 type 순서는 옵티마이저의 효율적인 순서(높음>중간>낮음)대로 정리합니다. type 설명 system table 에 하나의 row 만 가지는 const 의 특별한 형태 const PK 나 Unique 키에 반드시 1 건에 대한 등가(=)조건에 대한 접근 eq_ref 조인시 첫째컬럼값으로 두번째 테이블등의 PK, Unique 등에 등가(=) 1 건 접근. ref 조인시 인덱스에 대해 등가(=)접근하나 1 건이라는 보장이 없는 접근 fulltext 전문 검색 인덱스 사용하는 접근시(Where MATCH(?) AGAINST (~) 사용) ref_or_null NULL 비교가 추가된 ref 접근 형태 index_merge 해당 테이블의 두개이상의 인덱스를 사용한 검색결과에 대한 병합한 형태 unique_subquery IN 절에서 서브쿼리가 중복없는 unique 값을 반환하는 형태 index_subquery IN 절에서 서브쿼리가 중복이 있으나 INDEX 를 이용해 제거할 수 있는 형태 range 대소 비교연산자를 이용해서 인덱스 걸린 컬럼 검색시. index 인덱스 풀스캔. 인덱스를 처음부터 끝까지 조건없이 읽는 형태. ALL 풀 테이블 스캔. - Index Range Scan 은 type 이 const, eq_ref, ref 인경우를 의미하며 성능상 좋은 접근방법입니다. [system] mysql> EXPLAIN SELECT * FROM mysql.db; id select_type tabl e partition s type possible_key s key key_le n ref row s filtered Extra "1 " "SIMPLE" db N system "" "" "" "1" "100.00 " - 하나의 row 만 가지는 테이블 Access 시 나타나는것으로 const 의 특별한 유형.(system table) [const] mysql> EXPLAIN SELECT * FROM t1
  • 25. 25 WHERE id = 1; id select_type table partition s type possible_key s key key_len ref rows filtered Extra 1 SIMPLE t1 N const N N N N 1 100 [eq_ref] mysql> EXPLAIN SELECT t1.* FROM t3 INNER JOIN t1 ON t1.id=t3.id; i d select_type tabl e partitions type possible_key s key key_len ref rows filtered Extra 1 SIMPLE t3 N index PRIMARY PRIMARY 4 N 2 100 Using index 2 SIMPLE T1 N eq_re f PRIMARY PRIMARY 4 test.t3.i d 1 100 [ref] mysql> EXPLAIN SELECT t2.nm FROM t1 INNER JOIN t2 ON t2.nm=t1.nm WHERE t1.nm ='a'; i d select_type tabl e partitions type possible_key s key key_len ref rows filtered Extra 1 SIMPLE T2 N const PRIMARY PRIMARY 4 N 2 100 Using index 2 SIMPLE T1 N ref nm nm 4 test.t3.i d 1 100 [ref_or_null] mysql> EXPLAIN SELECT id, nm FROM t1 WHERE nm = 'a' OR nm IS NULL; id select_type table partitions type possible_key s key key_le n ref rows filtered Extra "1 " "SIMPLE" "t1" N "ref_or_null " "nm" "nm" "303" "const" "2" "100.00" "Using where; Using index" [index_merge] mysql> EXPLAIN SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20; id select_typ e table partition s type possible_key s key key_le n ref row s filtered Extra "1 "SIMPLE" "tbl_name N Index_merge Key1, key2 Key1, key2 4,4 null "1" "100.00 Using
  • 26. 26 " " " union(~ ) [unique_subquery] mysql> EXPLAIN ~ VALUE IN (SELECT primary_key FROM single_table WHERE some_expr); [index_subquery] mysql> EXPLAIN ~ VALUE IN (SELECT key_column FROM single_table WHERE some_expr); [range] mysql> EXPLAIN SELECT nm FROM t1 WHERE nm>'a'; id select_typ e table partition s type possible_keys key key_le n ref rows filtere d Extra "1 " "SIMPLE" "t1" null " range " "nm" nm 303 cons t 1 100" Using where ; Using index [index] mysql> EXPLAIN SELECT nm FROM t1 WHERE nm>='a'; id select_typ e table partition s type possible_keys key key_le n ref rows filtere d Extra "1 " "SIMPLE" "t1" null index "nm" nm 303 cons t 1 100" Using where ; Using index [ALL] mysql> EXPLAIN SELECT * FROM t1; id select_typ e table partition s type possible_keys key key_le n ref rows filtere d Extr a "1 " "SIMPLE" "t1" ALL "" 2 100"
  • 27. 27 4.5. possible_keys 해당 테이블을 검색할 때 사용할 수 있는 인덱스들의 정보입니다. 이 값이 NULL 이면 사용가능 인덱스가 없다는 의미입니다. 이 값의 순서는 의미가 없으며, 사용되지 않을수도 있습니다. 이 값이 NULL 인경우는 Where 절을 검사하여 적절한 인덱스를 생성해서 쿼리의 성능향상을 도모할 필요가 있습니다. 4.6. key possible_keys 의 인덱스들 중에서 실제 사용한 인덱스 정보를 의미합니다. 이 값을 통해서 개발자/DBA 는 사용된 index 정보를 확인할 수 있습니다. 실행계획 풀테이블 스캔인 type=ALL 이면 key=NULL 로 표시되며, index_merge 인 경우는 하나이상의 인덱스를 가질 수 있다. 만약 옵티마이저가 선택한 인덱스에 대하여 무시하거나 다른 인덱스를 사용하도록 할 경우는 FORCE INDEX / USE INDEX/ IGNORE INDEX 힌트를 통해서 해당 쿼리의 실행계획을 변경할 수 있습니다. SELECT nm FROM t1 USE INDEX(PRIMARY); SELECT nm FROM t1 FORCE INDEX(nm); SELECT nm FROM t1 IGNORE INDEX(nm); 4.7. key_len 해당 테이블의 사용한 인덱스의 길이를 의미하며, key=NULL 이면 key_len=NULL 이다. 또한 실제 복수컬럼 키중 얼마나 많은 부분을 사용할 것인지 알 수 있다. 단순히 키 길이가 의미가 없다고 판단할 수 있으나, key_len 이 중요한 이유는 Where 에 대한 제약조건을 통해서 해당 인덱스에서 key 로 사용된 컬럼들의 길이의 총 합이다. 이 의미는 인덱스에 있는 컬럼 이더라도 Key조건으로 사용되는 경우는 key_len 에 포함되지만, Covered Index 의 활용되는 사례처럼 Filter 조건으로 사용되는 경우는 해당컬럼의 길이는 포함되지 않는다. 이 값은 길이가 아니라 BYTE 를 의미합니다. # charset = UTF8 인경우 CREATE TABLE t4 (id INT NOT NULL , nm VARCHAR(100), cmt VARCHAR(20), rdate DATETIME, del_yn VARCHAR(1), PRIMARY KEY(id), KEY(nm,del_yn)); SELECT * FROM t4 WHERE nm ='a'; SELECT * FROM t4 WHERE nm ='a' AND del_yn=’Y’; SELECT * FROM t4 WHERE nm ='a' AND del_yn like ’%Y%’; # key_len 의 값은?
  • 28. 28 4.8. ref ref 는 사용된 인덱스의 key 로서 사용된 컬럼이나 상수값을 나타냅니다. 만약 ref=func 이면 이는 특정 함수를 통해서 얻어진 결과값을 의미합니다. 4.9. rows 이는 쿼리를 수행하기 위해 해당테이블을 조사해야 하는 행수입니다. InnoDB 의 경우 이값은 추정치를 나타내며, 항상 정확하지는 않습니다. 4.10. filtered 테이블의 제약조건에 의해서 필터링됩니다. 이값은 해당 테이블의 행의 예상비율을 나타냅니다. 4.9 의 rows 값(추청되는 조사할 행수)에 filtered 비율을 곱하면 이 값이 이전 테이블과 조인될 때 사용될 것으로 예상되는 rows 값을 찾을 수 있다. 4.11. Extra 이 값은 옵티마이저가 쿼리를 어떻게 해석해서 어떤 방식으로 Access 하는지에 대한 추가정보를 제공합니다. Extra 가 가지는 값들은 다음과 같다. 항목 설명 const row not found type=const 로 Access 했으나 대상테이블이 empty 테이블인 경우 Distinct 테이블 결과값이 중복없이 Unique 한 결과인 경우 Full Scan on Null key in 절 쿼리중 Outer query 에서 NULL 값이 공급되는 경우 발생함. Index lookup 실패로 Full Table Scan 이 발생할 수 있음 Impossible Having Having 절 항상 False. Select 연산 안함. Impossible Where Where 절 항상 False. Select 연산 안함. Impossible WHERE noticed after reading const tables const/system 유형의 테이블 읽은 후 Where 절이 항상 False. No tables used from 절 테이블 명시안됨. from dual 구문 Not existed Left join 절을 Not Exists 형태로 옵티마이저가 최적화 하는 경우 range checked for each record (index map: N) 조인에 적절한 인덱스가 없어서, 선행테이블 컬럼값에 따라 각각 row 에 대해서 range / index merge 를 검토함을 의미 N 값은 어떤 인덱스를 검토되는지 표시 Select tables optimized away Select 절에 집계함수만 있는 경우, 옵티마이저는 Index Lookup 후 1 개의 결과만 리턴하고 끝. unique row not found select ~ from table 쿼리에서 Key/PK Index 에 만족하는 row 가 없는경우 Using filesort 정렬결과를 만들기 위해 물리적인 정렬작업을 수행
  • 29. 29 Using index Index 내에서 모든 결과값을 리턴하는 경우(Covering Index) 인덱스 통해서 lookup 과정이 수행되면 NULL 값임. Using index for group-by Index 내에서 group by / distinct 처리 Using join buffer join 처리를 위한 Index 가 없을경우 join 시 memory 의 join_buffer 사용함을 의미. Using sort_union(...), Using union(...), Using intersect(...) Index Merge 수행. 두개이상의 인덱스를 동시에 사용하여 처리. Using temporary 쿼리처리를 위하여 임시테이블을 생성함. group by, order by 등에 인덱스 사용 못할 때 발생. Using where where 절이 다음조인에 사용될 행이나, Client 에 전송할 result 제한하는 경우. type 이 ALL 이나 Index 값이면서 Extra 가 Using where 가 아니면 쿼리상에 문제가 있는 경우로 확인필요. Using where with pushed condition InnoDB 에서는 발생안함(Only NDB) Using MRR 테이블을 읽을 때 다중범위 읽기 최적화 전략 수행시 해당. Secondary Index 의 랜덤 IO 를 순차 IO 로 변경해서 eq join 수행. http://dev.mysql.com/doc/refman/5.7/en/mrr-optimization.html Using index condition mysql 5.6 이상 Filtering 조건처리를 mysql 엔진이 아닌 InnoDB 엔진에 pushdown. 불필요 IO 줄여 빠른응답성능 제공. Start temporary, End temporary mysql 5.6 에서 semi-join 최적화. 조인후에 중복제거하는 전략이용하여 semi-join(exist or in 절 select) 수행. Skip_open_table, Open_frm_only, Open_trigger_only Open_full_table information_schema 조회시 발생. - Extra 값에 Using filesort 나 Using temporary 면 실행계획을 보다 면밀히 확인할 필요가 있습니다. 의도된 계획이거나 인덱스 값 확인 등을 통해서 실행계획을 재검토 한다.
  • 30. 30 5. SQL 을 위한 인덱싱 전략 5.1 SQL 상의 컬럼가공 제거 SELECT nm FROM t1 Where id/2=1; SELECT nm FROM t1 where substring(nm, 1,3)=’abc’; SELECT nm FROM t1 where to_days(current_date)-to_days(dt)<=10; 제약조건이나 조인조건상에 있는 컬럼의 경우에 컬럼을 가공/병합 처리등으로 원본컬럼값을 가공한다면 인덱스를 정상적으로 사용할 수 없다. 흔히 하는 실수가 컬럼값에서 문자열을 잘라와서 비교하거나, 날짜구간 계산의 비효율적인 사용이 대표적인 경우다. 5.2 클러스터링 인덱스(테이블) InnoDB 클러스터 인덱스는 전혀다른 의미의 인덱스가 아니다. 실제로 이는 B-Tree 구조의 인덱스정보와 Data 행값을 함께 가지고 저장한다. Root -Branch – Leaf 구조에서 Data 는 Leaf 에 저장된다. 테이블은 오로지 하나의 클러스터 인덱스만 가질 수 있으며, 만약 클러스터 인덱스를 생성하지 않으면 가상의 6byte ROW_ID 를 생성한다. 이렇게 기본 키(Primary Key)에 따라서 데이터를 묵는다. 클러스터 인덱스 구성 시 몇가지 주의사항 - 데이터 Insert 시에는 기본키 순서에 따라서 삽입하는 것이 유리. - 클러스터 인덱스의 컬럼크기는 작을수록 좋다. - 자주 변경되는 컬럼은 기본키로 적합하지 않다. - 기본키만이 클러스터 인덱스로 생성된다. 이는 조금 혼돈스럽다. MySQL 은 키의 개념과 클러스터링 개념을 혼합해서 관리한다. - 클러스터 인덱스 구조상 페이지의 분할을 고려해야 한다. 5.3 커버링 인덱스 불필요하게 여러컬럼을 읽지 않아도 되는 경우는 커버링 인덱스를 통해서 성능을 향상시킬 수 있다. 쿼리결과에 필요한 데이터가 모두 포함된 인덱스를 커버링 인덱스라고 한다. 쿼리가 커버링 인덱스를 적용하면 Explain 의 Extra 컬럼에 “Using Index”로 표시된다. 커버링 인덱스의 장점은 다음과 같다. - 보조인덱스( NonClustered Index)만으로 데이터 결과셋을 완성할 수 있어 속도가 빠르다. - 데이터가 정렬되어 있어서 무작위로 클러스터링 인덱스의 무작위 위치에서 가져오는것에 비해 IO 가 적게 든다. - 최선은 Where + Select 결과절까지의 컬럼을 커버하는 경우이며, Where 조건절의 컬럼은 커버링 인덱스에 포함하는 것이 좋다.
  • 31. 31 5.4 압축(프리픽스) 인덱스 인덱스를 생성하고자 하는 경우 특정컬럼의 크기가 큰경우, 인덱스 컬럼의 특정길이 만큼만 인덱스 구성이 가능하다. 지원가능한 컬럼타입은 char, varchar, binary, varbinary, text CREATE TABLE TBL(ID INT, NAME VARCHAR(1000)); CREATE INDEX TBL_IDX ON TBL ( NAME(10) ); INNODB 의 innodb_large_prefix 옵션이 꺼져있는 경우는 767byte, 켜져있는경우는 3072 byte 까지 컬럼크기 만큼 생성할 수 있다. 5.5 인덱스 개수를 최소로 구성(중복제거) 인덱스의 수가 증가하면 DML 에 따른 데이터 변경적재에 심각한 성능저하가 발생할 수 있다. 인덱스의 수와 DML 속도는 Trade-Off 관계로서 운영시스템의 SQL 조건들에 따라 적절한 인덱스의 개수를 유지하는 것이 좋다. 또한 중복된 인덱스를 구성하지 않도록 관리해야 한다. CREATE TABLE TBL(ID INT PRIMARY KEY, NM1 VARCHAR(10), NM2 VARCHAR(10)); CREATE INDEX TBL_IDX1 ON TBL ( NM1 ); CREATE INDEX TBL_IDX2 ON TBL ( NM1, ID); #? 5.6 다중컬럼 인덱스 흔히들 단일컬럼 인덱스 만드는 것과 다중컬럼 인덱스를 만든는 것의 차이를 이야기 하곤 한다. 단일 컬럼 인덱스보다 다중컬럼 인덱스를 사용할 것을 권장한다. 5.7 복합정렬의 주의 여러컬럼에 대해서 group by 나 정렬이 필요한 경우, MySQL 에서는 ASC 만 사용하는 것이 좋다. 현재 MySQL 5.7 까지 ASC 와 DESC 를 혼합한 경우 DESC 는 지원하지 않아서 filesort 를 피할 수 없다. 이런 경우는 데이터를 ASC 화 해서 저장하거나 가상컬럼을 이용해서 DESC 방식을 우회할 수 있다. 5.8 정리 - 제일 많이 수행되는 쿼리를 확인. - 모든 쿼리를 만족시키는 인덱스 전략은 없다. 인덱스의 구성과 속도의 타협이 필요하다. - 등가비교컬럼이 선두컬럼이 되도록 인덱싱한다. - 컬럼 크기는 되도록 작게 prefix 인덱스를 적절히 활용한다. - 모든 데이터 결과처리를 MySQL 의 인덱스등으로만 수행하려는 생각은 재고해야 한다. - 비정규화, 집계테이블, 캐싱등을 적절히 활용한다.
  • 32. 32 6. SQL 작성 가이드 아래내용은 절대적인 기준은 아니며, 팀/조직 내부의 표준화 정책에 따라 달라질 수 있습니다. - SQL TEXT 에 대하여 표준화 하라. 예를 들면 table, column 등은 소문자로, 나머지는 대문자로 통일 등. 세부항목들의 정리. - 당연히 ANSI-SQL 규약에 충실하자. - Application_ID 형태의 주석삽입은 운영 편의성을 제공한다. SELECT /* com.app.mem.select */ * FROM tbl~; - TAB 과 개행에 규칙을 부여하라. (EX: TAB=4 SPACE, ‘,’는 컬럼뒤, FROM/JOIN/WHERE 등은 개행) SELECT COL1, COL2 FROM TBL_A WHERE ID=123; - 연산자 다음은 한칸 띄운다. - WHERE 이나 JOIN 에 사용되는 컬럼의 데이터 타입은 같게 한다. - JOIN 시에도 DRIVING 테이블의 컬럼은 우변으로 DRIVEN 테이블은 좌변으로 위치한다. - 서브쿼리보다는 JOIN 을 사용한다. SELECT ID FROM TBL_A INNER JOIN TBL_B ON TBL_B.COL1 = TBL_A.COL1 (O) ON TBL_A.COL1 = TBL_B.COL1 (X) WHERE ~; - 되도록이면 등가비교(=)로 처리한다. - 부정형 보다는 긍정형으로 SQL 작성한다. (NOT IN(X)) - 되도록이면 수행되는 선행테이블 순서대로 조인한다. 가독하기 쉽다. - ALIAS RULE 을 적용하라.(A~Z 할지, 업무규약 약칭을 사용할지) - SELECT * 구문은 사용하지 않는다.
  • 33. 33 - 특별한 경우가 아니면 STORED ROUTINE 은 지양한다. - A4 여러장 짜리의 복잡한 JOIN 을 활용한 단일쿼리는 되도록 지양한다. 가독성도 떨어지고, 운영상의 문제을 만드는 경우가 많다. - SQL 작성 후 반드시 EXPLAIN 을 통해서 원하는 실행계획대로 수행하는지 확인한다. - 가급적이면 불필요한 시스템 함수사용은 자제한다. APPLICATION 영역에서 할일이지 DBMS 에서 처리할 일은 아닌것들이 더 많다. - 전체집합처리보다 부분집합처리로 결과절을 만들려고 노력하자.