Single Block I/O vs Mulit Block I/O
Single Block I/O
- 디스크 I/O 한번에 한 블록씩 요청해서 메모리에 적재하는 방식
- 인덱스와 테이블 블록 모두 이 방식을 사용
- 인덱스는 소량의 데이터를 읽어서 이 방식이 효율적임.
Mulit Block I/O
- 한번에 여러 블록을 요청해서 메모리에 적재하는 방식
- 대상 블록과 인접한 블록들을 한꺼번에 읽어 캐시에 미리 적재하는 방식
- 테이블 전체를 스캔(Full Scan)할때 좋은 방식 (I/O 할때 프로세스 Sleep을 줄여줌)
Mulit Block Read Count
DBMS가 Mulit Block I/O 수행하면서 한번에 가져올수 있는 블록의 총 갯수이다. (책에서는 손수레로 표현)
- 오라클의 블록 단위 : 8kb
- 최대로 설정할 수 있는 Mulit Block Read Count : 128
- 8kb * 128 = 1024kb = 1Mb
- 즉, Mulit Block I/O 수행하며 가져올 수 있는 최대 크기 : 1Mb
Mulit Block I/O 는 익스텐트를 벗어나지 못한다.
예시
- 1개의 익스텐트의 블록 수 : 20개로 가정
- Mulit Block Read Count : 8 이라고 가정
- 한개의 익스텐트 처음부터 읽었을때 읽는 블록
- 1번째 Mulit Block I/O : 1 ~ 8
- 2번째 Mulit Block I/O : 9 ~ 16
- 3번째 Mulit Block I/O는? : 16 ~ 20 (24가 아님)
즉, MulitBlock I/O는 익스텐트 경계를 넘을 수 없다.
Single Block I/O와 Mulit Block I/O는 섞어 쓴다.
익스텐트 안의 블록을 전체 읽어야 하는데, 만약 일부가 버퍼캐시에 있다면?
버퍼캐시에서 읽을 블록을 확인하고, 듬성듬성 읽게 되는 블록이 있다면 Single Block I/O를 사용하는게 적절하다.
- 아래는 익스텐트이다. 아래의 1~10까지의 번호는 블록의 번호이다.
- 괄호친 블록의 번호는 버퍼캐시에 있다.
- M : MulitBlock I/O
- B : 버퍼캐시
- S : Single Block I/O
1 | 2 | 3 | 4 | (5) | 6 | (7) | 8 | 9 | 10 |
---|
M | M | M | M | B | S | B | M | M | M |
- 1 ~ 4번까지: Mulit Block I/O
- 5번: 버퍼캐시로 메모리 I/O
- 6번: Single Block I/O
- 7번: 버퍼캐시로 메모리 I/O
- 8~10번: Mulit Block I/O
Table Full Scan vs Index Range Scan
Table Full Scan
- 테이블 전체를 스캔해서 읽는 방식
- 다량의 데이터를 읽을 때 좋은 방식
- 시퀀셜 액세스와 Mulit Block I/O 방식을 이용하여 디스크 블록을 읽는다.
Index Range Scan
- 인덱스를 이용해서 읽는 방식
- 소량의 특정 데이터를 검색할 때 좋은 방식
- 인덱스에서 일정량을 스캔해서 얻은 ROWID로 테이블 레코드를 찾아가는 방식
- 랜덤 액세스와 Single Block I/O 방식으로 디스크 블록을 읽는다.
ROWID는 테이블 레코드가 디스크상에 어디에 저장 되어있는지 가리키는 위치정보
Table Full Scan이 오히려 Index Range Scan보다 좋은 경우가 있을 수 있다.
한번에 많은 데이터를 처리하는 집계용 SQL과 배치 프로그램은 Table Full Scan이 좋을 수 있음.
조인을 포함한 SQL이면 '해시조인' 선택해주면 된다.
왜 인덱스가 느릴까?
- Single Block I/O와 Mulit Block I/O
- Index Range Scan와 Table Full Scan
많은 데이터를 읽으려고 할때 인덱스를 이용하면 Table Full Scan이 유리함. => 읽으려는 데이터 양의 차이.
많은 양의 데이터를 Index Scan하면 Single Block I/O를 엄청 많이 하기 때문에 I/O가 많아지는 것은 속도가 느려지는 것이다.
캐시 탐색 메커니즘
Direct Path I/O를 제외한 모든 블록 I/O는 메모리 버퍼캐시를 경유함.
버퍼캐시 탐색과정
아래 오퍼레이션은 모두 버퍼캐시 탐색과정을 거침
- 인덱스 루트 블록을 읽을 때
- 인덱스 루트 블록에서 얻은 주소 정보로 브랜치 블록을 읽을 때
- 인덱스 브랜치 블록에서 얻은 주소 정보로 리프 블록을 읽을 때
- 인덱스 리프 블록에서 얻은 주소정보로 테이블 블록을 읽을 때
- 테이블 블록을 Full Scan 할 때
버퍼캐시에서 블록을 찾을 때, 해시 알고리즘으로 버퍼 헤더를 찾음. 헤더에서 얻은 포인터로 버퍼 블록을 액세스 함.
해시 구조의 특징 (Hash를 알면 패스)
- 같은 입력값은 항상 동일한 해시 체인에 연결됨.
- 다른 입력값이 동일한 해시 체인에 연결될 수 있음.
- 해시 체인 내에는 정렬이 보장되지 않음.
메모리 공유 자원에 대한 액세스 직렬화
- 버퍼캐시는 SGA라서 버퍼캐시에 캐싱된 버퍼블록은 공유 자원임.
- 버퍼캐시는 누구나 접근 가능
- 버퍼캐시 내 버퍼블록을 2개 이상 프로세스가 동시 접근하려고 하면 블록 정합성에 문제가 생김.
- 직렬화(serialization) 매커니즘을 사용하여 순차 접근하도록 구현
- 직렬화가 가능하도록 지원하는 매커니즘이 래치(Latch)이다.
캐시버퍼 체인 래치
- 해시체인을 스캔하는 동안 다른 프로세스가 체인 구조를 변경하는걸 막기 위해 체인래치가 존재함.
- 체인에 접근하기 위해 프로세스가 래치 Key를 획득해야만 해시 체인에 접근할 수 있다.
- 캐시 히트율을 올려도 이 체인 래치의 경합 때문에 생각보다 빠르지 않을 수 있다.
버퍼캐시에 작동하는 래치
- 캐시버퍼 체인 래치
- 캐시버퍼 LRU 체인 래치
버퍼 Lock
- 버퍼 블록에 존재하는 직렬화 매커니즘이다.
- 래치 해제한 상태로 버퍼블록 사용 도중, 후행 프로세스가 같은 블록에 접근할 경우 정합성의 문제가 생김. 이를 방지하기 위한 매커니즘이다.
- 캐시버퍼 체인래치를 해제하기 전에 버퍼 헤더에 Lock을 설정함으로써 버퍼 블록 자체에 대한 직렬화 문제 해결
직렬화 매커니즘에 의한 캐시 경합을 줄이려면 SQL 튜님을 통해서 쿼리의 "논리적 I/O" 자체를 줄여야 한다.