3.1 실습 환경 구성하기
• 실 습 데 이 터 다 운 로 드 U R L : https://github.com/7ieon/sqLtune
3.2 실행 계획 수행
124page
실행 계획이란 말 그대로 SQL 문으로 요청한 데이터를 어떻게 불러올 것인지에 대한 계획, 즉 경로를 의미한다.
지름길로 가서 빠르게 데이터 찾을 것인지, 지름길 있어도 멀리 돌아가서 찾을 것인지 미리 확인이 가능하다.
3.2.1 기본 실행 계획 수행
EXPLAIN, DESCRIBE, DESC 로 실행 계획 조회 가능하다.
MySQL의 실행 계획 수행
mariaDB에 비해 filtered 열이 추가되면서 더 많은 정보를 보여준다.
MariaDB 실행 계획 수행
10.0.5 이상에서는 UPDATE, DELETE 문에서도 실행 계획 확인이 가능하다.
3.2.2 기본 실행 계획 항목 분석
실행 계획에 있는 id, select_type 등 키워드를 명확히 알아야된다. 그래야 비효율적이 아닌지, 어떻게 튜닝을 해야할 지에 관한 방향을 잡을 수 있기 때문이다.
id
실행 순서를 표시하는 숫자. 수행되는 차례를 ID로 표시한 것으로, 조인할 때는 똑같은 ID 가 표시된다. 즉, id가 작을수록 먼저 수행된 것이고 id가 같다면 두 개 테이블의 조인이 이루어졌다고 해석할 수 있다.
select_type
SELECT 문의 유형을 출력하는 항목. select가 단순히 FROM에 위치한건지, 서브쿼리인지, UNION으로 묶인 select 인지 등의 정보를 제공한다. 다음을 통해 자주 출력되는 select_type 정보를 알아보자.
SIMPLE
union 또는 내부 쿼리가 없는 select란 의미. 단순한 select 구문만으로 작성된 경우를 가리킨다.
PRIMARY
서브쿼리가 포함된 SQL 문이 있을때 첫 번째 SELECT 문에 해당하는 구문에 표시되는 유형. 즉, 서브쿼리를 감싸는 외부 쿼리거나, UNION이 포함된 SQL에서 첫 번째로 SELECT 키워드가 작성된 구문에 표시된다. 128 페이지 참고!
SUBQUERY
독립적으로 수행되는 서브쿼리를 의미.
DRIVED
FROM 절에 작성된 서브쿼리라는 의미. 즉, FROM 절의 별도 임시 테이블인 인라인 뷰를 말한다.
UNION
UNION 및 UNION ALL 구문으로 합쳐진 SELECT 문에서 첫 SELECT 구문 제외한 이후의 select 구문에 해당하는걸 나타낸다. 이때 union 구문의 첫 select 절은 PRIMARY 유형으로 출력된다.
UNION RESULT
UNION ALL 아닌 UNION 구문으로 SELECT 절을 결합했을 때 출력된다. union은 출력 결과에 중복이 없는 유일한 속성을 가지므로 각 select 절에서 데이터 가져와 정렬하여 중복 체크하는 과정을 거친다. 따라서 union result는 별도의 메모리 또는 디스크에서 임시 테이블을 만들어 중복을 제거하겠다는 의미로 해석할 수 있다.
한편 union 으로 결합되지 전의 각 select 문이 중복되지 않는 결과가 보장될때는 union 보다 union all 구문으로 변경하는 SQL 튜닝을 수행한다.
DEPENDENT SUBQUERY
union or union all을 사용하는 서브쿼리가 메인 테이블의 영향을 받는 경우. UNION 으로 연결된 단위 쿼리들 중에서 처음으로 작성한 단위 쿼리에 해당되는 경우이다. 즉, UNION으로 연결되는 첫 번째 단위 쿼리가 독립적으로 수행되지 못하고 메인 테이블로 부터 값을 하나씩 공급받는 구조(AND 사원1.사원번호 = 관리자.사원번호)이므로 성능적으로 불리하여 SQL이 튜닝대상이 된다.
DEPENDENT UNION
UNION or UNION ALL 시용하는 서브쿼리가 메인 테이블의 영향을 받는 경우. UNION으로 연결된 단위 쿼리 중 첫 번째 단위 쿼리를 제외하고 두 번째 단위 쿼리에 해당하는 경우. 즉, UNION으로 연결되는 두 번째 이후의 단위 쿼리가 독립적으로 수행되지 못하고 메인 테이블로부터 값을 하나씩 공급받는 구조이므로 이 또한 위와 같이 성능적으로 불리하여 튜닝의 대상이 된다.
UNCACHEABLE SUBQUERY
말 그대로 메모리에 상주하여 재활용되어야할 서브쿼리가 재사용되지 못할 때 출력되는 유형. 이런 유형은
- 해당 서브쿼리 안에 사용자 정의 함수나 사용자 변수 포함
- RAND(), UUID() 함수 등을 사용하여 매번 조회 시마다 결과가 달라지는 경우 에 해당한다.
만약 자주 호출되는 쿼리라면 메모리에 서브쿼리 결과가 상주할 수 있도록 변경하는 방향으로 튜닝을 고려해볼 수 있다. 135페이지 참고.
MATERIALIZED
IN 절 구문에 연결된 서브쿼리가 임시 테이블을 생성한 뒤, 조인이나 가공 작업을 수행할 때 출력되는 유형. 즉, IN 절의 서브쿼리를 임시 테이블로 만들어서 조인 작업을 수행하는 것이다. 136페이지 참고!
table
말 그대로 테이블명을 표시하는 항목. 실행 계획 정보에서 테이블명이나 테이블 별칭(alias)를 출력하며, 서브쿼리나 임시 테이블을 만들어서 별도의 작업을 수행할 때는 <subquery#> 나 <derived#>라고 출력된다.
예제를 보자. 쿼리 수행 결과 첫 번째 행의 table 열에는 <derived2>라고 출력되었고 ID 값은 1이다. 두 번째 행의 ID 값 역시 마찬가지로 1인 것으로 보아, <derived2>와 사원 테이블이 조인했으리라 해석이 된다. 여기서 <derived2>는 ID가 2인 테이블이라는 뜻으로 사실상 급여 테이블을 의미한다. 즉, FROM 절의 서브쿼리 구문으로 작성된 급여 테이블과 사원 테이블이 조인했다고 이해할 수 있다.
partitions
실행계획의 부가 정보. 데이터가 저장된 논리적인 영역을 표시하는 항목이다. 사전에 정의한 전체 파티션 중 특정 파티션에 선택적으로 접근하는 것이 쿼리 성능 측면에서 유리하다. 만약 너무 많은 영역의 파티션에 접근하는 것으로 출력된다면 파티션 정의를 튜닝해야 할 것이다.
type
테이블의 데이터를 어떻게 찾을지에 대한 정보를 제공하는 항목. 테이블을 처음 부터 끝까지 전부 확인할 지 아니면 인덱스로 바로 데이터를 찾아갈지 등을 해석할 수 있다.
SYSTEM
테이블에 데이터가 없거나 한 개만 있는 경우로, 성늘상 최상의 type.
CONST
조회되는 데이터가 단 1건일 때 출력되는 유형. 성능상 매우 유리한 방식. 고유 인덱스나 기본 키를 사용하여 단 1건의 데이터에만 접근하면 되니 속도나 리소스 측면에서 지양해야 할 타입.
eq_ref
조인이 수행될 때 드리븐 테이블의 데이터에 접근하며 고유 인덱스 또는 기본 키로 단 1건의 데이터를 조회하는 방식. 드라이빙 테이블과 조인 키가 드리븐 테이블에 유일하므로 조인이 수행될 때 성능상 가장 유리한 경우라고 할 수 있다.
ref
앞서 설명한 eq_ref 유형과 유사한 방식으로, 조인을 수행할 때 드리븐 테이블의 데이터 접근 범위가 2개 이상일 경우를 의미한다. 즉, 드라이빙 테이블과 드리븐 테이블이 조인을 수행하면 일대다 관계가 되므로, 드라이빙 테이블 1개 값이 드리븐 테이블에서는 2개 이상의 데이터로 존재한다. 이때 기본 키나 고유 인덱스를 활용하면 2개 이상의 데이터가 검색되거나, 유일성이 없는 비고유 인덱스를 사용하게 된다. 드리븐 테이블의 양이 적을때는 성능 저하를 크게 우려하지 않아도 되지만, 데이터양이 많다면 접근해야 할 데이터 범위가 넓어져 성능 저하의 원인이 되는지 확인해야 한다. 한편으로는 =, <, > 등의 연산자를 사용해 인덱스로 생성된 영을 비교할 때도 출력된다.
ref_or_null
ref 유형과 유사하지만 IS NULL 구문에 대해 인덱스를 활용하도록 최적화된 방식이다. MySQL, MariaDB는 NULL에 대해서도 인덱스를 활용하여 검색할 수 있으며, 이때 NULL은 가장 앞에 정렬된다. 테이블에서 검색할 NULL 데이터양이 적다면 ref_of_null 방식을 활용했을때 효율적인 쿼리문이 될 것이나, 검색할 NULL 데이터양이 많다면 튜닝의 대상이 될 것이다.
range
테이블 내의 연속된 데이터 범위를 조회하는 유형. =, <>, >, >=, IS NULL, BETWEEN 또는 IN 으로 범위 스캔을 수행하는 방식. 주어진 데이터 범위 내에서 행 단위 스캔을 하지만, 스캔 범위가 넓으면 성능 저하 요인이 될 수 있느니 튜닝의 대상이 된다.
fulltext
텍스트 검색을 빠르게 처리하기 위한 전문 인덱스(full text index)를 사용하여 데이터에 접근하는 방식
index_merge
말 그대로 결합된 인덱스들이 동시에 사용되는 유형. 즉, 특정 테이블에 생성된 두 개 이상의 인덱스가 병합되어 동시에 적용된다. 이때 전문 인덱스는 제외된다.
index
type항목의 index 유형은 인덱스 풀 스캔을 의미한다. 즉, 물리적인 인덱스 블록을 처음부터 끝까지 훓는 방식. 이때 데이터를 스캔하는 대상이 인덱스라는 점이 다를 뿐, 이어서 설명할 ALL 유형과 유사하다.
ALL
테이블을 처음부터 끝까지 읽는 테이블 풀 스캔 유형. ALL 유형은 활용할 수 있는 인덱스가 없거나, 인덱스를 활용하는게 오히려 비효율적이라고 옵티마이저가 판단했을 때 선택된다.
ALL 일때는 인덱스 추가하거나 기존 인덱스를 변경하여 인덱스를 활용하는 방식으로 튜닝이 가능하나, 전체 테이블 중 10~20% 이상 분량의 데이터를 조회할 때는 ALL 이 오히려 성능상 유리할 수 있다.
possible_keys
옵티마이저가 쿼리문을 최적화하고자 사용할 수 있는 인덱스 목록을 출력한다. 다만 실제 사용한 인덱스가 아닌, 사용할 수 있는 후보군의 기본 키와 인덱스 목록만 보여줄 뿐이다.
key
옵티마이저가 쿼리를 최적화 하고자 사용한 PK 또는 인덱스명을 의미한다. 어느 인덱스로 데이터를 검색했는지 알 수 있으므로, 비효율적인 인덱스를 사용했거나 인덱스 자체를 사용하지 않았다면 튜닝의 대상이 된다.
key_len
인덱스 쓸 때는 인덱스 전체를 쓰거나 일부 인덱스만 쓴다. key_len은 이렇게 사용한 인덱스의 바이트 수를 의미한다. UTF-8 캐릭터셋 기준으로 INT 데이터 유형은 단위당 4바이트, VARCHAR은 단위당 3바이트다.
ref
reference 의 약자. 조인을 수행할 때 어떤 조인 조건으로 해당 테이블에 액세스되었는지 알려주는 정보.
rows
쿼리문을 수행하고자 접근하는 데이터의 모든 행 수를 나타내는 예측 항목. 즉, 디스크에서 데이터 파일을 읽고 메모리에서 처리해야 할 행수를 예상하는 값이고, 수시로 변동되는 MySQL의 통계정보를 참고하여 산출되는 값이므로 정확하진 않다. 그리고 최종 출력될 행 수가 아니라는 점에 유의하자.
filtered
SQL 문을 통해 DB 엔진으로 가져온 데이터 대상으로 필터 조건에 따라 어느 정도의 비율로 데이터를 제거했는지 의미하는 항목이다. 예를 들어 DB 엔진으로 100건의 데이터를 가져왔다 가정하면, 이후 WHERE 절의 사원번호 BETWEEN 1 AND 10 조건으로 100거의 데이터가 10건으로 필터링 된다. 이처럼 100 → 10 으로 필터링 되었으므로 filtered 에는 10 이라는 정보가 출력될 것. 단위는 %

extra
SQL 문을 어떻게 수행할 것인지에 관한 추가 정보를 보여주는 항목. 이러한 부가적인 정보들은 세미콜론(;)으로 구분하여 여러 정보 나열할 수 있으며 30여 가지 항목으로 정리할 수 있다.
이후 현업에서 자주 만나게 될 extra 정보 몇가지를 설명한다. MySQL에서는 extra에서 수행되는 정보가 모두 출력이 안되니 어디까지나 참고하는 수준에서 해석하는것이 좋다. Distinct
중복이 제거되어 유일한 값을 찾을 때 출력되는 정보. 중복 제거가 포함되는 distinct 나 union 이 포함된 경우 출력된다.
Using where
자주보이는 정보. WHERE 절의 필터 조건을 사용해 MySQL 엔진으로 가져온 데이터를 추출할 것이라는 의미로 이해 가능하다.
Using temporary
데이터의 중간 결과를 저장하고자 임시 테이블을 생성하겠다는 의미. 데이터를 가져와 저장한 뒤 정렬 작업을 수행하거나 중복을 제거하는 작업 등을 수행한다. 보통 DISTINCT, GROUP BY, ORDER BY 구문이 포함된 경우 Using temporary 정보가 출력된다.
임시 테이블을 메모리에 생성하거나, 메모리 영역을 초과하여 디스크에 임시 테이블을 생성하면 Using temporary는 성능 저하의 원인이 될 수 있다. 따라서 이 항목이 나오면 튜닝 대상이 될 수도 있다.
Using index
물리적인 데이터 파일을 읽지 않고 인덱스만을 읽어서 쿼리 요청사항을 처리할 수 있는 경우. 일명 커버링 인덱스 방식이라 부르며, 인덱스로 구성된 열만 쿼리에서 사용할 경우 이 방식을 활용한다. 물리적으로도 테이블보다 인덱스가 작고 정렬되어 있으므로 적은 양의 데이터에 접근할 때 성능 측면에서도 효율적이다.
Using filesort
정렬이 필요한 데이터를 메모리에 올리고 정렬 작업을 수행한다는 의미. 보통 이미 정렬된 인덱스를 쓴다면 추가적인 정렬 작업이 필요 없지만, 인덱스를 사용하지 못할 때는 정렬을 위해 메모리에 데이터를 올리게 된다. Using filesort 는 추가적인 정렬 작업이므로 인덱스를 활용하도록 튜닝 대상이 될 수 있다.
Using join buffer
조인을 수행하기 위한 중간 데이터 결과를 저장하는 조인 버퍼를 쓴다는 의미. 즉, 드라이빙 테이블의 데이터에 먼저 접근한 결과를 조인 버퍼에 담은 후, 조인 버퍼와 드리븐 테이블 간에 서로 일치하는 조인 키 값을 찾는 과정을 수행한다. 이런 조인 버퍼를 활용하는 일련의 과정이 존재하면 Using join buffer 가 출력된다.
Using union / Using intersect / Using sort_union
앞서 실행 계획의 type 항목에서는 두 개 이상의 인덱스가 병합되어 데이터에 접근하는 경우를 나타내는 index_merge 유형을 설명했습니다. 이처럼 인덱스가 병합되어 실행되는 SQL의 extra 항목에는 인덱스를 어떻게 병합했는지에 관한 상세 정보가 출력된다. 그 상세 정보가 Using union / Using intersect / Using sort_union 인 것.
Using union 은 인덱스들을 합집합처럼 모두 결합하여 데이터에 접근한다는 의미. 보통 쿼리 문이 OR 로 작성된 경우에 해당한다.
Using intersect 는 인덱스들을 교집합 처럼 추출하는 방식. 쿼리문이 AND 로 작성된 경우 확인할 수 있는 extra 정보.
Using sort_union 는 기본적으로 Using union과 유사하지만, WHERE 의 OR 구문이 동등조건이 아닐 때 확인할 수 있는 extra 정보.
Using index condition
MySQL 엔진에서 인덱스로 생성된 열의 필터 조건에 따라 요청된 데이터만 필터링하는 Using where 과 달리, 필터 조건을 스토리지 엔진까지 전달하여 필터링 작업에 대한 MySQL 엔진의 부하를 줄이는 방식. 이는 스토리지 엔진의 결과를 MySQL 엔진으로 전송하는 양을 줄여 성능 효율을 높일 수 있는 옵티마이저의 최적화 방식.
Using index condition(BKA)
type 정보의 Using index condition 유형과 비슷하나, 데이터 검색하기 위해 배치 키 액세스를 사용하는 방식.
Using index for group-by
쿼리문에 Group by 나 Distinct 가 포함될 때는 인덱스로 정렬 작업을 수행하여 최적화한다. 이때 Using index for group-by는 인덱스로 정렬 작업을 수행하는 인덱스 루스 스캔일 때 출력되는 부가 정보.
Not exists
하나의 일치하는 행 찾으면 추가로 행 탐색 하지않아도 될 때 출력. 해당 메커니즘은 레프트 조인 또는 라이트 조인에서 테이블에 존재하지 않는 데이터를 명시적으로 검색할 때 발생한다.
예를 들어 SELECT * FROM t1 LEFT JOIN t2 on (...) WHERE t2.not_null_column IS NULL; 이라는 쿼리에서 t1, t2 테이블 조건에 일치하는 데이터 없는 경우 그 값이 NULL이 될 수 있으므로, 일치하는 행 하나를 찾았으니 검색을 중지한다. 이 상황일때 Not exists 가 발생.
3.2.3 좋고 나쁨을 판단하는 기준
실행 계획을 보더라도, 튜닝 대상인지 아닌지 판단하는 것은 어렵다. 하지만 그간의 튜닝 경험을 바탕으로 나름의 기준을 수립하고 각 상황에 맞게 검토 대상을 추출할 수는 있다. 단언하기는 어렵지만 튜닝 대상을 검토할 때 다음과 같은 기준을 참조할 수는 있다. 검토 대상인 열은 select_type, type, extra 이다.

3.2.4 확장된 실행 계획 수행
EXPLAIN 을 실행 계획을 확인할 때 쓰지만, 추가 정보를 확인하고자 한다면 DB에서 각각 지원하는 키워드를 입력할 수 있다. MySQL과 MariaDB가 수행하는 확장 실행 명령어가 서로 다르니 다믕과 같이 구분해서 실행해보자.
MySQL의 확장된 실행 계획 수행
EXPLAIN FORMAT = TRADITIONAL
기본적인 실행 계획은 EXPLAIN 키워드로 입력하며 기본 포맷은 TRADITIONAL이다. 항상 보던 실행 계획 형태가 조회된다.
EXPLAIN FORMAT = TREE
포맷 값에 TREE를 입력하면 트리 형태로 추가된 실행 계획을 확인할 수 있다.
EXPLAIN FORMAT = JSON
JSON 형태로 실행 계획을 확인할 수 있다.
EXPLAIN ANALYZE
그동안 출력된 실행 계획은 예측된 실핼 계왹에 관한 정보. 만약 실제 측정한 실행 계획을 출력하고 싶다면 ANALYZE 를 쓴다. 실제 수행된 소요 시간과 비용을 측정하여 실측 실행 계획과 예측 실행 계획 모두를 확인하려면 EXPLAIN ANALYZE 키워드를 활용하면 된다. MySQL 8.0.18 이상에서 SELECT 문 대상으로 수행 가능하다.

MariaDB의 확장된 실행 계획 수행
EXPLAIN PARTITIONS
파티션으로 설정된 테이블에 대해 접근 대상인 파티션 정보를 출력한다.
EXPLAIN EXTENEDE
스토리지 엔진에서 가져온 데이터를 다시 MySQL 엔진에서 추출한 비율일 filtered 열의 값을 추가로 출력한다.
ANALYZE
MariaDB 10.1 이상에서 ANALYZE 키워드만으로 실제 측정한 실행 계획 정보가 출력된다. 실제 액세스한 데이터 건수(r_rows)와 MySQL 엔진에서 가져온 데이터에서 추가루 추출한 데이터의 비율(r_filtered)을 확인할 수 있다.

3.3 프로파일링
프로파일링은 마치 범죄수사에서 실마리를 찾기위해 분석하는 것 처럼 쿼리문에서도 문제가 되는 병목 지점을 찾고자 사용되는 수단이나 틀을 가리킨다. slow query 나 문제가 있다 의심되는 SQL 문의 원인을 확인할 수 있다.
프로파일을 확인하는 과정은 툴이 아닌 명령줄에서 수행한다. 툴은 사용자의 의도와 무관하게 백그라운드에서 호출되는 sql이 있으므로, 예상치 않은 쿼리가 프로파일링이 되지 않도록 CLI를 여기서는 쓴다.
3.3.1 SQL 프로파일링 실행하기
실습 대상 디비에 접속하고 프로파일링 설정값을 확인한다. MySQL은 기본적으로 비활성화 되어있어서 활성화 해줘야한다.
show variables like 'profiling%'
set profiling = 'ON'
아무 select 실행후
show profiles

특정 쿼리 ID에 대해서만 프로파일링된 상세 내용을 확인하고자 한다면, 쿼리 ID를 입력하여 다음과 같은 문법으로 결과를확인한다.
show profile for query #
Status, Duration 컬럼을 가진 결과로 나오는데 Duration이 길면 문제가 될 소지가 높은 구간으로 볼 수 있다.
…
3.3.2 프로파일링 결과 해석하기
일반적인 프로파일링 항목
| 항목 | 설명 |
|---|---|
| starting | 쿼리 시작 |
| checking permissions | 필요 권한 확인 |
| Opening tables | 테이블 열기 |
| After opening tables | 테이블 연 후 |
| System lock | 시스템 잠금 |
| Table lock | 테이블 잠금 |
| init | 초기화 |
| optimizing | 최적화 |
| statistics | 통계 |
| perparing | 준비 |
| exeuting | 실행 |
| Sending data | 데이터 보내기 |
| end | 끝 |
| query end | 질의 끝 |
| closing tables | 테이블 닫기 |
| Unlocking tables | 잠금 해제 테이블 |
| freeing iterms | 항목 해방 |
| updating status | 상태 업데이트 |
| cleaning up | 청소 |
| 프로파일링의 추가 정보 확인하려면 다음 표에 기재된 키워드 작성하여 구체적으로 분석이 가능하다. show profile 구문에 해당 키워드 작성하여 Block I/O, CPU, SWAP 횟수 등에 대한 OS 수준의 확장된 정보 제공받을 수 있다. |
| 옵션 | 설명 |
|---|---|
| ALL | 모든 정보 표시 |
| BLOCK ID | 블록 입력 및 출력 작업의 횟수 표시 |
| CONTEXT SWITCHES | 자발적 및 비자발적인 컨텍스트 스위치 수 표시 |
| CPU | 사용자 및 시스템 CPU 사용 기간 표시 |
| IPC | 보내고 받은 메시지의 수 표시 |
| PAGE FAULTS | 주 페이지 오류 및 부 페이지 오류 수 표시 |
| SOURCE | 함수가 발생하는 파일 이름과 행 번호와 함께 소스 코드의 함수 이름을 표시 |
| SWAPS | 스왑 카운트 표시 |
show profile all for query 1 같이 사용하면 된다. |
확장된 프로파일링 출력 항복에 관한 설명은 다음 표와 같다. 운영체제 관점에서 발생하는 지표를 상세한 수준으로 제공한다.
| 항목 | 설명 |
|---|---|
| QUERY_ID | 쿼리 아이디 |
| SEQ | 동일한 QUERY_ID를 갖는 행의 표시 순서를 보여주는 일련번호 |
| STATE | 프로파일링 상태 |
| DURATION | 명령문이 현재 상태에 있었던 시간(초) |
| CPU_USER | 사용자 cpu 사용량(초) |
| CPU_SYSTEM | 시스템 cpu 사용량(초) |
| CONTEXT_VOLUNTARY | 자발적 컨텍스트 전환의 수 |
| CONTEXT_INVOLUNTARY | 무의식적 컨텍스트 전환의 수 |
| BLOCK_OPS_IN | 블록 입력 조작의 수 |
| BLOCK_OPS_OUT | 블록 출력 조작의 수 |
| MESSAGES_SENT | 전송된 통신 수 |
| MESSAGES_RECEIVED | 수신된 통신 수 |
| PAGE_FAULTS_MAJOR | 메이저 페이지 폴트의 수 |
| PAGE_FAULTS_MINOR | 마이너 페이지 폴트의 수 |
| SWAPS | 스왑 수 |
| SOURCE_FUNCTION | 프로파일링된 상태로 실행되는 소스 코드의 기능 |
| SOURCE_FILE | 프로파일링된 상태로 실행된 소스 코드의 파일 |
| SOURCE_LINE | 프로파일링된 상태로 실행된 소스 코드의 행 |
3.4 마치며
다양한 툴(예측 실행 계획, 실측 실행 계획, 프로파일링)들이 있지만, 보통 제한된 업무 조건에서 튜닝을 수행하므로 예측 실행 계획 정보를 기준으로 이 책에서는 튜닝을 진행할 예정이다.