작년에 면접 준비를 하면서 DB 인덱스에 대해서 정리한 내용이 있다. https://minnseong.tistory.com/19
아래 내용보다는 "친절한 SQL 튜닝"을 읽으면서 실무적인 내용과 새로운 개념을 배울 수 있어 책을 읽어보는 것도 추천한다!
제 3장. 인덱스 튜닝은 내용이 많아 두 파트로 나눠 정리를 했다.
인덱스 튜닝 (1) : 테이블 액세스 최소화, 부분범위 처리 활용
인덱스 튜닝 (2) : 인덱스 스캔 효율화, 인덱스 설계
01. SQL 튜닝은 랜덤 I/O와의 전쟁
- 인덱스를 이용하는 경우, 테이블 풀 스캔보다 오히려 느릴 때가 있다.
- SQL에서 조회하려는 칼럼을 인덱스가 모두 포함하는 경우가 아니라면, 인덱스 스캔 이후에 반드시 테이블을 액세스 해야 한다.
- 실행계획에서 "TABLE ACCESS BY INDEX ROWID"로 테이블을 액세스 하는 경우를 확인할 수 있다.
ROWID는 물리적 주소일까? 논리적 주소일까?
- 데이터파일 번호, 오브젝트 번호, 블록 번호 같은 물리적 요소로 구성되어 있어, 물리적 주소라고 설명해도 틀리다고 할 수 없다.
- 하지만 물리적 주소보다는 논리적 주소에 가깝다.
- 물리적으로 직접 연결되어 있지 않고, 테이블 레코드를 찾아가기 위한 논리적인 주소 정보를 담고 있기 때문이다.
I/O 메커니즘 복습
- DBA(데이터파일 번호 + 블록번호) : 디스크 상에서 블록을 찾기 위한 주소 정보
- 블록을 읽을 때 디스크로 가기 전에 버퍼 캐시부터 찾아보고, 읽고자 하는 DBA를 해시 함수에 입력해서 해시 체인을 찾고 거기서 버퍼 헤더를 찾는다.
- 캐시에 적재할 때와 읽을 때 같은 해시 함수를 사용하므로 버퍼 헤더는 항상 같은 해시 체인에 연결된다. 반면, 실제 데이터가 담긴 버퍼 블록은 매번 다른 위치에 캐싱되는데, 그 메모리 주소값을 버퍼 헤더가 가지고 있다.
- 인덱스로 테이블 블록을 액세스 할 때는 리프 블록에서 읽은 ROWID를 분해해서 DBA 정보를 얻고, 테이블을 Full Scan 할 때는 익스텐트 맵을 통해 읽은 블록들의 DBA 정보를 얻는다.
(비용) ROWID가 가리키는 테이블 블록을 버퍼캐시에서 먼저 찾아보고, 못 찾을 때만 디스크에서 블록을 읽는다. 모든 데이터가 캐싱되어 있더라도 테이블 레코드를 찾기 위해 매번 DBA 해싱과 래치 획득 과정을 반복해야 한다. 동시 액세스가 심할 때는 캐시버퍼 체인 래치와 버퍼 Lock에 대한 경합까지 일어난다.
인덱스 ROWID를 이용한 테이블 액세스는 생각보다 고비용 구조라는 것을 인지하고 있어야 하며, 앞으로 실행계획에서 "TABLE ACCESS BY INDEX ROWID" 오퍼레이션을 볼 때면, 위와 같이 복잡한 처리과정을 떠올리면 좋을 것이라 필자는 말한다.
02. 인덱스 클러스터링 팩터 (CF)
- 특정 칼럼을 기준으로 같은 값을 갖는 데이터가 서로 모여있는 정도를 의미
- 클러스터링 팩터가 높은 칼럼에 생성한 인덱스는 검색 효율이 매우 좋다. = 테이블 액세스량에 비해 블록 I/O가 적게 발생
* 테이블 액세스량에 비해 블록 I/O가 적게 발생의 의미 (인덱스 클러스터링 팩터 효과)
- 인덱스 레코드마다 테이블 레코드를 건건이 블록 단위로 I/O 한다면, CF가 다르더라도 블록 I/O 발생량에 차이는 크게 없다고 생각할 수 있다.
- 하지만, 인덱스 ROWID로 테이블을 액세스 할 때, 오라클은 래치 회득과 해시 체인 스캔 과정을 거쳐 어렵게 찾아간 테이블 블록에 대한 포인터를 바로 해제하지 않고 일단 유지한다. = 버퍼 Pinning
- 이 상태에서 다음 인덱스 레코드를 읽었는데, 마침 '직전과 같은' 테이블 블록을 가리킨다면 래치 획득과 해시 체인 스캔 과정을 생략하고 테이블 블록을 읽을 수 있다. (논리적인 블록 I/O 과정을 생략할 수 있다.)
이미지 출처 : https://oracle-online-help.blogspot.com/2006/11/clustering-factor_28.html
03. 인덱스 손익분기점
- 인덱스 ROWID를 이용한 테이블 액세스는 생각보다 고비용으로, 읽어야 할 데이터가 일정량을 넘는 순간 테이블 풀 스캔보다 오히려 성능이 안 좋아진다. 그 지점을 '인덱스 손익분기점'이라고 한다.
- 핵심적인 차이
Table Full Scan | 인덱스 ROWID를 이용한 Table Access | |
Scan 방식 | Sequential Access | Random Access |
I/O 방식 | Multiblock I/O | Single Block I/O |
- 인덱스 클러스터링 팩터가 나블 경우, 같은 테이블 블록을 여러 번 반복 액게스하면서 논리적 I/O와 물리적 I/O 횟수가 늘어난다.
- 항상 인덱스 스캔이 좋고, 테이블 풀 스캔이 나쁘다고 할 수 없다.
04. 온라인 프로그램 튜닝 vs 배치 프로그램 튜닝
온라인 프로그램 튜닝
- 보통 소량 데이터를 읽고 갱신하므로 인덱스를 효과적으로 활용하는 것이 무엇보다 중요하다.
- 조인도 NL 방식을 사용하며, 인덱스를 이용한 소트 연산 생략으로 성능을 높일 수 있다.
배치 프로그램 튜닝
- 대량 데이터를 읽고 갱신하므로 항상 전체범위 처리 기준으로 튜닝해야 한다.
- 대량 데이터를 빠르게 처리하려면, 인덱스와 NL 조인보다는 Full Scan과 해시 조인이 유리하다.
- 파티션 활용 전략, 병렬 처리가 중요한 튜닝 요소이다.
05. 인덱스 칼럼 추가
- 운영 환경에서 인덱스 구성을 변경하는 것은 쉽지 않다. 또한 새로 인덱스를 만들 수 있지만, 인덱스가 많아질 경우 인덱스 관리 비용이 증가하고 DML 부하에 따른 트랜잭션 성능 저하가 생긴다.
- 기존 인덱스에 인덱스 칼럼을 추가하는 것만으로도 큰 효과를 얻을 수 있다. 인덱스 스캔량은 줄지 않겠지만 테이블 랜덤 액세스 횟수를 줄여줄 수 있기 때문이다.
- Covered 쿼리 : 쿼리에서 조회되는 칼럼을 모두 인덱스에 추가해서 테이블 액세스가 아예 발생하지 않게 하는 방법이다. 테이블을 액세스 하지 않아 성능은 획기적으로 좋아질 수 있지만, 추가해야 할 칼럼이 많은 경우 실제 적용하기 어려운 경우도 많다.
함께 보면 좋을 내용 : https://jojoldu.tistory.com/476
06. 인덱스 구조 테이블
- 인덱스를 이용한 테이블 액세스가 고비용이기 때문에 랜덤 액세스가 아예 발생하지 않도록 테이블을 인덱스 구조로 생성하면 어떨까?라는 생각으로부터 나온 개념이다.
- 오라클 (IOT : Index-Organized Table) : 테이블을 찾아가기 위한 ROWID를 갖는 일반 인덱스와 달리 IOT는 테이블 데이터를 갖는다. 즉, 테이블 블록에 있어야 할 데이터를 인덱스 리프 블록에 모두 저장하고 있다.
-- 테이블을 인덱스 구조로 만드는 구문
create table index_org_t (a number, b varchar(10), constraint index_org_t_pk primary key (a))
organization index;
- IOT는 인위적으로 클러스터링 팩터를 좋게 만드는 방법 중 하나이다.
- 같은 값을 가진 레코드들이 100% 정렬된 상태로 모여 있으므로 랜덤 액세스가 아닌 시퀀셜 방식으로 데이터를 액세스 한다.
07. 클러스터 테이블
인덱스 클러스터 테이블
- 클러스터 키 값이 같은 레코드를 한 블록에 모아저 저장하는 구조
- 한 블록에 모두 담을 수 없는 경우에는 새로운 블록을 할당해서 클러스터 체인으로 연결한다.
- 여러 테이블 레코드를 같은 블록에 저장할 수도 있는데, 이를 '다중 테이블 클러스터'라고 부른다.
- 클러스터 인덱스를 스캔하면서 값을 찾을 때는 랜덤 액세스가 클러스터 체인을 스캔하면서 발생하는 랜덤 액세스를 제외하고 값 하나당 한 번씩밖에 발생하지 않는다.
-- 클러스터 생성
create cluster c_dept# (deptno number(2)) index;
-- 클러스터 인덱스 정의
create index c_dept#_idx on cluster c_dept#;
-- 클러스터 테이블 생성
create table dept (
deptno number(2) not null,
dname varchar2(14) not null,
loc varchar2(13))
cluster c_dept#(deptno);
해시 클러스터 테이블
- 인덱스 클러스터 테이블과 인덱스를 사용하지 않고 해시 알고리즘을 사용해 클러스터를 찾아간다는 점만 다르다.
-- 클러스터 생성
create cluster c_dept# (deptno number(2)) hashkeys 4;
-- 클러스터 테이블 생성
create table dept (
deptno number(2) not null,
dname varchar2(14) not null,
loc varchar2(13))
cluster c_dept# (deptno);
08. 부분 범위 처리 활용
- DBMS가 데이터를 모두 읽어 한 번에 전송하지 않고 먼저 읽은 데이터부터 일정량을 전송한다. 데이터를 전송하고 나면 서버 프로세스는 CPU를 OS에 반환하고 대기 큐에서 잠을 잔다. 다음 Fetch Call을 받으면 대기 큐에서 나와 그다음 데이터부터 일정량을 읽어서 전송하고 또다시 잠을 잔다.
- 이처럼 전체 쿼리 결과집합을 쉼 없이 연속적으로 전송하지 않고 사용자로부터 Fetch Call이 있을 때마다 일정량씩 나누어 전송하는 것을 이른바 '부분범위 처리'라고 한다.
정렬조건이 있을 때 부분범위 처리
- DB 서버는 모든 데이터를 다 읽어 정렬을 마치고서야 클라이언트에게 데이터 전송을 시작할 수 있다. (전체범위처리)
- Sort Area와 Temp 테이블스페이스까지 이용해 데이터 정렬을 마치고 나면 그때부터 일정량씩 나눠 클라이언트에게 데이터를 전송한다.
- 만약 정렬 칼럼이 선두인 인덱스가 있으면, 부분범위 처리가 가능하다. 인덱스는 항상 정렬된 상태를 유지하므로 전체 데이터를 정렬하지 않고도 정렬된 상태의 결과집합을 바로 전송할 수 있기 때문이다.
ArraySize 조정을 통한 Fetch Call 최소화
- 대량의 데이터를 파일로 내려받는다면 어차피 데이터를 모두 전송해야 하므로 가급적 Array Size 값을 크게 설정해야 한다.
- Array Size를 조정한다고 해서 전송해야 할 총량이 변하진 않지만, Fetch Call 횟수를 그만큼 줄일 수 있다.
- 반대로, 일부 테이만 Fetch 하다가 멈추는 프로그램이라면 Array Size를 작게 설정하는 것이 유리하다. 불필요하게 많은 데이터를 전송하고 버리는 비효율을 줄일 수 있기 때문이다.
08-1. OLTP 환경에서 부분범위 처리에 의한 성능 개선 원리
- OLTP(Online Transaction Processing) 시스템에서 대부분 소량의 데이터를 조회하는 경우가 많지만, 수천수만 건을 조회하는 경우도 있다.
- 그 경우, 인덱스를 이용해 수천수만 건을 조회하려면 많은 테이블 랜덤 액세스가 발생하기 때문에 만족할만한 성능을 내기 어렵다.
- 특정한 정렬 순서로 상위 일부 데이터만 확인하는 경우 (입출금, 게시판 조회등), 인덱스와 부분범위 처리 원리를 활용하여 극적인 성능 개선효과를 낼 수 있다.
select 게시글ID, 제목, 작성자, 등록일시
from 게시판
where 게시판구분코드 = 'A'
order by 등록일시 desc
만약, 인덱스 선두 칼럼을 [게시판구분코드 + 구분일시] 순으로 구성되어 있지 않다면, Sort 연산은 생략할 수 없을 것이다.
모든 레코드를 인덱스에서 읽어야 하고, 그만큼 많은 테이블 랜덤 액세스가 발생할 것이다. 또한 모든 데이터를 읽은 이후에 등록일시 역순으로 정렬할 수 있다.
하지만, 인덱스 선두 칼럼을 [게시판구분코드 + 구분일시] 순으로 구성한다면, Sort Order By 연산을 생략할 수 있기 때문에 게시판구분코드 = 'A' 조건을 만족하는 전체 로우를 읽지 않고도 바로 결과집합을 일부 출력할 수 있다.
책 읽고 정리하는 과정
1. '친절한 SQL 튜닝' 책을 정독하며, 중요한 부분 바로바로 타이핑
2. 한 챕터가 끝나면, 타이핑한 부분을 복습하며 관련 구글링 및 재정리
SQL 튜닝은 랜덤I/O와의 전쟁
인덱스를 이용하니 테이블 전체 스캔하는 것 보다 오히려 느릴 때가 있다.
SQL이 참조하는 컬럼을 인덱스가 모두 포함하는 경우가 아니면, 인덱스를 스캔한 후에는 반드시 테이블을 엑세스해야 한다.
실행계획에서 "TABLE ACCESS BY INDEX ROWID" 라고 표시된 부분이 여기에 해당한다.
인덱스를 사용하는 이유, 검색 조건을 만족하는 소량의 데이터를 인덱스에서 빨리 찾고 거기서 테이블 레코드를 찾아가기 위한 주소값, ROWID 를 얻기 위해서.
ROWID는 물리적 주소일까? 논리적 주소일까?
- 데이터파일 번호, 오브젝트 번호, 블록 번호 같은 물리적 요소로 구성되어 있어, 그런 의미에서는 물리적 주소라고 설명해도 틀리다고 말할 수 없다.
- 하지만 ROWID는 물리적 주소보다 논리적 주소에 가깝다.
- 물리적으로 직접 연결되지 않고, 테이블 레코드를 찾아가기 위한 논리적 주소 정보를 담고 있기 때문이다.
- 포인터라고 생각할 수 있다. 메모리에 있는 데이터를 포인터로 액세스할 때 매우 빠르며 그 비용은 0에 가깝다.
- 하지만 포인터가 아니다.
- 정리하면 ROWID는 논리적 주소이며, 디스크 상에서 테이블 레코드를 찾아가기 위한 위치 정보를 담는다. 포인터도 아니고 테이블 레코드와 물리적으로 직접 연결된 구조는 더더욱 아니다.
I/O 메커니즘 복습
- DBA(데이터파일번호 + 블록번호)는 디스크 상에서 블록을 찾기 위한 주소 정보이다.
매번 디스크에서 블록을 읽을 수 없기 때문에, I/O 성능을 높이려면 버퍼캐시를 활용해야 한다.
- 블록을 읽을 떄는 디스크로 가기 전에 버퍼 캐시부터 찾아본다. 읽고자 하는 DBA를 해시 함수에 입력해서 해시 체인을 찾고 거기서 버퍼 헤더를 찾는다.
- 해싱 알고리즘으로 버퍼 헤더를 찾고 거기서 얻은 포인터로 버퍼 블록을 찾아간다. (P134)
- 인덱스로 테이블 블록을 액세스할 때는 리프 블록에서 읽은 ROWID를 분해해서 DBA 정보를 얻고, 테이블을 Full Scan 할 때는 익스텐트 맵을 통해 읽을 블록들의 DBA 정보를 얻는다.
인덱스 클러스터링 팩터
- 특정 컬럼을 기준으로 같은 값을 갖는 데이터가 서로 모여있는 정도를 의미
- 클러스터 팩터가 좋은 칼럼에 생성한 인덱스는 검색 효율이 매우 좋다. (분가한 열 명의 자녀가 모두 한 동네에 모여 살면 CF가 좋아 부모가 자녀들 집을 모두 방문하는데 하루면 충분)
인덱스 손익분기점
- 인덱스 ROWID를 이용한 테이블 엑스는 생각보다 고비용으로, 읽어야할 데이터가 일정량이 넘는 순간 테이블 전체를 스캔하는 것보다 오히려 느려진다. 그 지점을 '인덱스 손익분기점' 이라고 한다.
- 핵심적인 요소
- Table Full Scan은 시퀀셜 엑세스인 반면, 인덱스 ROWID를 이용한 테이블 엑세스는 랜덤 엑세스 방식
- Table Full Scan은 Mulitiblock I/O 인 반면, 인덱스 ROWID를 이용한 테이블 엑세스는 Single Block I/O 방식이다.
- 인덱스 CF가 나쁠 경우 같은 테이블 블록을 여러번 반복 액세스하면서 논리적 I/O 횟구가 늘고, 물리적 I/O 횟수도 늘기 때문이다.
- 테이블 스캔도 인덱스 스캔도 항상 좋은 것은 아니다.
온라인 프로그램 튜닝 vs
- 보통 소량 데이터를 읽고 갱신하므로 인덱스를 효과적으로 활용하느 것은 무엇보다 중요하다.
- 조인도 NL 방식을 사용한다. (인덱스를 이용하는 조인 방식)
- 인덱스를 이용한 소트 연산 생략
배치 프로그래밍 튜닝
- 대량 데이터를 읽고 갱신하는 배치 프로그램은 항상 전체범위 처리 기준으로 튜닝해야 한다.
- 처리 대상 집합 중 일부를 빠르게 처리하는 것이 아니라 전체를 빠르게 처리하는 것을 목표로 삼아야 한다.
- 대량 데이터를 빠르게 처리하려면, 인덱스와 NL 조인보다 Full Scan과 해시 조인이 유리하다.
- 인덱스보다는 Full Scan이 효과적이지만, 초대용량 테이블은 Full Scan 하면 상당히 오래 기다려야하기 때문에, 파티션 활용 전략이 매우 중요한 튜닝 요소이고, 병렬 처리까지 더할 수 있으면 금상첨화다.
인덱스 칼럼 추가
- 실 운영 환경에서 인덱스 구성을 변경하기가 쉽지 않다.
- 새로 인덱스를 만들 수 있지만, 인덱스가 수십 개씩 달려 배보다 배꼽이 더 커지게 되며, 인덱스 관리 비용이 증가함은 물론 DML 부하에 따른 트랜잭션 성능 저하가 생긴다.
- 기존 인덱스에 인덱스 칼럼을 추가하는 것만으로 큰 효과를 얻을 수 있다. 인덱스 스캔량은 줄지 않겠지만, 테이블 랜덤 액세스 횟수를 줄여줄 수 있다.
- Covered 쿼리 : 쿼리에 사용된 칼럼을 모두 인덱스에 추가해서 테이블 액세스가 아예 발생하지 않게 하는 방법, 성능은 획기적으로 좋아질 수 있지만, 추가해야 할 칼럼이 많아 실제 적용하기 곤란한 경우도 있다.
인덱스 구조 테이블
- 인덱스를 이용한 테이블 액세스가 고비용 구조라고 하니, 랜덤 액세스가 아예 발생하지 않도록 테이블을 인덱스 구로조 생성하면 어떨까?
- 오라클 (IOT, index-organized table), 테이블을 찾아가기 위한 ROWID를 갖는 일반 인덱스와 달리 IOT는 그 자리에 테이블 데이터를 갖는다.
- 즉, 테이블 블록에 있어야 할 데이터를 인덱스 리프 블록에 모두 저장하고 있다.
create table index_org_t (a number, b varchar(10), constraint index_org_t_pk primary key (a))
organization index;
- IOT는 인위적으로 클러스터링 팩터를 좋게 만드는 방법 중 하나이다. 같은 값을 가진 레코드들이 100% 정렬된 상태로 모여 있으므로 랜덤 액세스가 아닌 시퀀셜 방식으로 데이터를 액세스 한다.
클러스터 테이블
- 인덱스 클러스터
-- 클러스터 키 값이 같은 레코드를 한 블록에 모아서 저장하는 구조
-- 한 블록에 모두 담을 수 없을 떄는 새로운 블록을 할당해서 클러스터 체인으로 연결한다.
-- 여러 테이블 레크드를 같은 블록에 저장할 수도 있는데, 이를 '다중 테이블 클러스터'라고 부른다.
-- 클러스터 인덱스를 스캔하면서 값을 찾을 때는 랜덤 액세스가 (클러스터 체인을 스캔하면서 발생하는 랜덤 액세스를 제외하고) 값 하나당 한번 씩 밖에 발생하지 않은다.
create cluster c_dept# (deptno number(2)) index; // 클러스터 생성
create index c_dept#_idx on cluster c_dept#; // 클러스터 인덱스 정의
create table dept (
deptno number(2) not null,
dname varchar2(14) not null,
loc varchar2(13))
cluster c_dept# (deptno); // 클러스터 테이블 생성
- 해시 틀러스터
-- 인덱스를 사용하지 않고 해시 알고리즘을 사용해 클러스터를 찾아간다는 점만 다르다.
create cluster c_dept# (deptno number(2)) hashkeys 4; // 클러스터 생성
create table dept (
deptno number(2) not null,
dname varchar2(14) not null,
loc varchar2(13))
cluster c_dept# (deptno); // 클러스터 테이블 생성
부분 범위 처리 활용
- DBMS가 클라이언트에게 데이터를 전송할 때도 일정량씩 나누어 전송한다. 전체 결과집합 중 아직 전송하지 않은 분량이 많이 남아있어도 서버 프로세스는 클라이언트로부터 추가 Fetch Call을 받기 전까지 그대로 멈춰 서서 기다린다.
- DBMS가 데이터를 모두 읽어 한 번에 전송하지 않고 먼저 읽은 데이터부터 일정량을 전송하고 멈추기 때문이다. 데이터를 전송하고 나면 서버 프로세스는 CPU를 OS에 반환하고 대기 큐에서 잠을 잔다. 다음 Fetch Call을 받으면 대기 큐에서 나와 그다음 데이터부터 일정량을 읽어서 전송하고 또다시 잠을 잔다.
- 이처럼 전체 쿼리 결과집합을 쉼 없이 연속적으로 전송하지 않고 사용자로부터 Fetch Call이 있을 때마다 일정량씩 나누어 전송하는 것을 이른바 '부분범위 처리'라고 한다.
정렬조건이 있을때 부분범위 처리
- DB 서버는 모든 데이터를 다 읽어 정렬을 마치고서야 클라이언트에게 데이터 전송을 시작할 수 있다.
- 전체범위처리다.
- Sort Area와 Temp 테이블스페이스까지 이용해 데이터 정렬을 마치고 나면 그때부터 일정량씩 나눠 클라이언트에게 데이터를 전송한다.
- 만약 정렬 컬럼이 선두인 인덱스가 있으면, 부분범위 처리가 가능하다. 인덱스는 항상 정렬된 상태를 유지하므로 전체 데이터를 정렬하지 않고도 정렬된 상태의 결과집합을 바로 전송할 수 있기 때문에.
ArraySize 조정을 통한 Fetch Call 최소화
- 대량의 데이터를 파일로 내려받는다면 어차피 데이터를 모두 전송해야 하므로 가급적 그 값을 크게 설정해야 한다.
Array Size를 조정한다고 해서 전송해야 할 총량이 변하진 않지만, Fetch Call 횟수를 그만큼 줄일 수 있다.
- 반대로, 일부 테이만 Fetch 하다가 멈추는 프로그램이라면 Array Size를 작게 설정하는 것이 유리하다. 불필요하게 많은 데이터를 전송하고 버리는 비효율을 줄일 수 있기 때문에.
OLTP (Online Transaction Processing) 시스템에서 수천수만 건을 조회하는 경우가 있다.
- 인덱스를 이용해 수천수만 건을 조회하려면 만족할만한 성능을 내기 어려울 수 있다. 많은 테이블 랜덤 액세스가 발생하기 때문에
- 특정한 정렬 순서로 상위 일부 데이터만 확인하는 경우가 많다.
-- 인덱스와 부분범위 처리 원리를 활용하면 OLTP 환경에서 극적인 성능개선 효과를 얻을 수 있다.
select 게시글ID, 제목, 작성자, 등록일시
from 게시판
where 게시판구분코드 = 'A'
order by 등록일시 desc
인덱스 선두칼럼을 [게시판구분코드 + 구분일시] 순으로 구성하지 않으면, 소트 연산을 생략할 수 없다. 모든 레코드를 인덱스에서 읽어야 하고, 그만큼 많은 테이블 랜덤 액세스가 발생한다. 모든 데이터를 읽은 이후에 등록일시 역순으로 정렬해야한다.
하지만, 인덱스를 [게시판구분코드 + 구분일시] 순으로 구성하면, Sort Order By 연산을 생략할 수 있다. 따라서 게시판구분코드 = 'A' 조건을 만족하는 전체 로우를 읽지 않고도 바로 결과집합 출력을 시작할 수 있다.
'정리' 카테고리의 다른 글
[친절한 SQL 튜닝] NL 조인, 소트 머지 조인, 해시 조인 (2) | 2024.06.16 |
---|---|
[친절한 SQL 튜닝] 인덱스 튜닝 (2) : 인덱스 스캔 효율화, 인덱스 설계 (0) | 2024.06.13 |
[친절한 SQL 튜닝] 인덱스 기본 (1) | 2024.04.21 |
[친절한 SQL 튜닝] 데이터 저장 구조 및 I/O 메커니즘 (1) | 2024.02.03 |
[Java] JVM 정리 (JVM의 개념, 실행과정, 구조) (0) | 2023.07.12 |