Skip to content

Commit

Permalink
캐시 탐색 및 소소하게 내용 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
binary-ho authored Jun 29, 2024
1 parent 4a3a247 commit d936b73
Showing 1 changed file with 42 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,46 @@ DB 버퍼 캐시도 라이브러리 캐시와 마찬가지로 SGA의 가장 중

### Signle-Blocking I/O와 Multi-Blocking I/O
일단 Signle-Blocking I/O와 Multi-Blocking I/O의 차이를 이해할 수 있어야 한다. <Br>
연산에 따라 DB는 데이터를 가져올 때 딱 필요한 하나의 블록을 가져오거나 (Single), 혹은 쭉 스캔하여 여러 블록의 데이터를 가져온다. <Br>
마치 인부가 벽돌을 나를 때, 하나만 나를지 여러개를 나를지의 차이이다. 많은 블럭을 가져와야 하는 경우 어떤 때는 가져올 블럭을 식별하는 시간 (Scan)이 더 걸리더라도 한번에 가져오는 것이 나을 수도 있다. <Br>
메모리 캐시에 원하는 데이터 블록 없다면, 결국 디스크에서 불러올 수 밖에 없다. 이때, 연산에 따라 DB는 필요한 하나의 블록을 가져오거나 (Single Block), 혹은 쭉 스캔하여 여러 블록의 데이터를 가져온다. (Multiblock) <Br>
마치 인부가 벽돌을 나를 때, 하나만 나를지 여러개를 나를지의 차이이다. 많은 블럭을 가져와야 하는 경우 어떤 때는 가져올 블럭을 식별하는 시간 (Scan)이 더 걸리더라도 한번에 가져오는 것이 나을 수도 있다. <Br> <br>

Oracle DB가 Index Range Scan 통해 데이터를 가져올 때, 데이터를 Single-Blocking으로 가져온다. 그리고 Table Full Scan은 데이터를 가져올 때, 필요한 데이터를 모두 스캔해 "한번에" 가지고 온다. 물론 스캔하는 데에 시간이 걸릴 수 있지만, 레인지 스캔을 하는 경우 보다는 낫다. <br>
예를 들어 레인지 스캔을 통해 500개의 레코드를 가져와야한다고 가정하자. 그리고 하나의 블록에는 500개의 레코드가 있다. <Br>
인덱스를 통한 방식은 이때 500번의 중복 작업을 한다! 필요한 데이터들을 쭉 긁어오지 않고, 같은 블록을 500번 가져오며 하나의 데이터만을 꺼내버린다. <Br>
이러한 상황에서는 Full Table Scan이 더 나은 선택임이 자명하다.
어떤 경우에 Single Block I/O가 발생할까? 주로 소량의 데이터를 가져올 때이다.
1. `인덱스 루트 블록`을 읽을 때
2. `인덱스 루트 블록`에서 얻은 주소 정보로 -> `브랜치 블록`을 읽을 때
3. `인덱스 브랜치 블록`에서 얻은 주소 정보로 -> `리프 블록`을 읽을 때
4. `인덱스 리프 블록`에서 얻은 주소 정보로 -> `테이블 블록`을 읽을 때

<br>

결국 한번의 인덱스를 통한 Range Scan 이용하는 과정에서 여러번의 Single Block I/O가 발생하게 된다. <br> 많은 데이터 블록을 읽을 때는 Multiblock 방식이 유리한데, Full Scan의 경우 당연히 이쪽이 유리하다. 이때 Multiblock I/O 단위를 크게 설정하면 한번에 데이터를 많이 가져올 수 있어 성능이 좋아진다. <br> <br>

이제 왜 인덱스를 통해 데이터를 가져오는 것이 항상 빠르다고 할 수 없는지 알 수 있다. 어떤 레코드를 500개 가져오는 상황을 생각해보자. 그리고 하나의 블록엔 레코드가 500개 있다고 생각해보자. <br>

**인덱스를 사용하면 총 500번의 중복 작업을 하게 된다!** 하나 하나 데이터는 빠르게 찾을 수 있겠지만, 무려 500번의 I/O가 발생하게 된다. <br>
반대로 Full Scan을 하게 되면, 데이터 자체를 찾는데엔 오랜 시간이 걸리고, 스캔하는데 시간이 걸리겠지만 단 한번만의 I/O만에 데이터를 가져올 수 있다! <br>
DBMS의 주요 병목은 I/O에 의해 발생하는 것이므로, 인덱스를 사용하는 경우가 더 느릴 수도 있다! **Table Full Scan이 Index Range Scan 보다 유리한 경우는 분명 있다는 것이다!** <br>
따라서, Full Scan이면 웬만하면 인덱스를 사용하는 방향으로 튜닝하겠다는 생각은 버려야 한다.


## 1.3.8 캐시 탐색 매커니즘

Direct Path I/O 외의 모든 블록 I/O는 메모리 버퍼 캐시를 경유하게 된다.
1. 인덱스를 사용하는 동안
2. 테이블 블록을 Full Scan 하는 동안
모두 메모리 버퍼 캐시를 경유한다.

<Br>

버퍼 캐시는 HashMap이 버퍼 블록 래퍼런스를 가리키는 형태로 구현되어 있다. <Br>
버킷에 각 버킷별로 링크드 리스트 형태와 같은 해시 체인을 가지고 있다. 보통의 해시 탐색과 같은 순서로 탐색이 진행된다.

1. Data Block Adress를 해시 함수에 넣어 버킷을 식별한다.
2. 해당 해시 체인을 순회하면서 올바른 노드를 찾으면, 그곳에 "버퍼 헤더"가 있다.
3. 이 버퍼 헤더에서 포인터를 얻을 수 있고, 이 포인터는 데이터가 캐싱된 버퍼 블록을 가리키고 있다.

### 버퍼 캐시 접근 직렬화
버퍼 캐시 또한 SGA의 구성요소로 메모리에 올라간 공유 자원에 해당한다. 따라서 동시에 접근하면 정합성에 문제가 생길 수 있다. **따라서 요청들을 직렬화하는데, 이때 Latch를 활용한다.** 이를 "Hash Chain Latch"라고 부른다. <br>

**이 랫치가 잠그는 범위는 하나의 Hash Chain이다.** 즉, 체인마다 Latch가 있다. 랫치를 통해 한번에 하나의 프로세스만 체인에 진입할 수 있도록 돕는다. 체인에서 노드를 찾으면, 즉시 랫치는 해제되고, 다른 프로세스가 진입할 수 있다. <br>

이때, 노드를 찾자마자 반환되는 거지, 버퍼 블록을 방문했을 때 반환되는 것이 아니므로, 다른 프로세스가 같은 버퍼 블록에 접근할 수도 있다. **그래서 버퍼 블록 자체에도 "Buffer Lock"이 존재한다.** **Chain Latch를 해제하기 전에 먼저 Buffer Lock을 걸어 직렬화 문제를 해결한다.**

0 comments on commit d936b73

Please sign in to comment.