1절의 기본 아키텍쳐에서 잠깐 알아봤던 시스템들에 대해 좀 더 디테일하게 정리한 내용입니다.
2. DB 버퍼 캐시
오라클은 빠른 데이터입출력을 위해 SGA공유 메모리를 이용하는데, 구체적으로 사용자가 입력한 데이터를 데이터파일에 저장하고 이를 다시 읽는 과정에서 거쳐가는 캐시 영역은 DB 버퍼캐시입니다.
1)블록 단위 I/O
오라클은 블록단위 I/O 단위로 이루어 집니다.
메모리 버퍼 캐시에서 버퍼 블록을 액세스할 때뿐만 아니라, 데이터파일에 저장된 데이터 블록을 DB버퍼 캐시로 적재하거나 캐시에서 변경된 블록을 다시 데이터파일에 저장할때도 블록단위로 처리합니다. 데이터파일에서 버퍼 캐시로 블록을 적재할 때, 인덱스를 경유한 테이블 액세스 시에는 한번에 한 블록씩 읽는 single read block으로 읽고, Full Table Scan을 할 시에는 성능 향상을 위해 한 번에 여러 블록을 읽는 multiblock read로 읽게 됩니다.(파라미터인 db_file_multiblock_read_count[MBRC]의 개수가 한번에 몇개의 블록을 읽는지 설정하는 파라미터입니다)
블록 단위로 읽는다는 것은 하나의 레코드에서 하나의 컬럼만을 읽고자 하더라도 레코드가 속한 블록 전체를 읽게 됨을 의미합니다. SQL성능을 좌우하는 가장 중요한 성능지표는 액세스하는 블록 개수이며, 옵티마이저가 인덱스를 이용해 테이블을 액세스할지 아니면 Full Table Scan을 할지 결정하는것에 있어서도 가장 중요한 기준은 레코드 수가 아니라 블록 개수인 것입니다.
2)버퍼 캐시 구조
DB버퍼 캐시를 설명할 때 바둑판 모양에 무작위로 데이터가 차 있는 블록들의 모양으로 많이 표현합니다. 바둑판처럼 생긴 버퍼 캐시에서 읽고자 하는 데이터 블록이 담긴 버퍼 블록을 어떻게 찾아내는 것일까요? DB버퍼캐시는 해시 테이블 구조로 관리됩니다. 해싱알고리즘을 설명할 때 흔히 주소록에 비유하곤 하는데, 고객의 주소와 전화번호를 관리하는 주소록에서 성씨가 같은 고객은 같은 페이지인 해시 버킷에 묶어서 관리합니다. 고객정보가 입수되는 시점이 다르므로 같은 성씨 안에서는 대개 정렬된 상태를 유지하지 않습니다. 이 때문에 해시 버킷내에서는 스캔 방식으로 값을 찾습니다. 그렇다하더라도 키 값을 해시 값으로 변환하여 해시 버킷을 찾아가는 속도가 워낙 빨라서 각 버킷에 기록되는 엔트리 개수를 일정 수준으로 유지할 수만 있다면 해싱에 의한 검색 속도가 가장 빠르다고 알려져 있습니다.
DB 버퍼 캐시 내에서 데이터 블록을 해싱하기 위해 사용되는 키 값은 데이터 블록 주소(DBA, Data Block Address)입니다. 해시 함수에 데이터 블록 주소를 입력해 리턴받은 해시 값(hash value)이 같은 블록들을 같은 해시 버킷에 연결 리스트구조로 연결하는 것입니다. 각각의 연결리스트를 해시 체인이라고 합니다.
찾고자 하는 데이터 블록주소를 해시 값으로 변환해서 해당 해시 버킷에서 체인을 따라 스캔하다가 거기서 찾아지면 바로 읽고, 찾지 못하면 디스크에서 읽어 해시 체인에 연결한 후 읽으면 됩니다. 자신만 읽고 버리는게 아니라 다른 사용자들도 사용할 수 있도록 캐싱해 두는 것입니다.
좀 더 정확히 이야기하면 버퍼 헤더만 해시 체인에 연결되며, 실제의 데이터 값이 필요해지면 버퍼헤더에 있는 포인터를 이용해 다시 버퍼 블록을 찾아가는 구조입니다.
3) 캐시 버퍼 체인
각각의 해시 체인(그림에서 점선화살표)들은 래치(latch)에 의해 보호됩니다. DB버퍼 캐시는 공유 메모리 영역인 SGA영역에 존재하므로 여러 프로세스에 의한 동시 엑세스가 일어날 가능성이 크고 같은 리소스에 대한 액세스를 반드시 직렬화(순서화)해야 하고 이를 위해 구현된 lock 매커니즘이 래치입니다.
래치를 획득한 프로세스만이 그 래치에 의해 보호되는 자료구조로의 진입이 허용됩니다. 두 개이상의 프로세스가 같은 해시 체인으로 진입해 새로운 버퍼 블록을 연결하고 해제하는 작업을 동시에 진행한다면 문제가 발생할 수 있으며 이를 방지하기 위해 사용하는 것이 cache buffer chains 래치입니다.
(v$latchname 딕셔너리 뷰로 확인할 수 있습니다) 하나의 cache buffer chains래치가 여러 개 해시 체인을 동시에 관리한다는 사실도 기억할 필요가 있습니다. _db_block_hash_buckets과 _db_block_hash_latches 파라미터 값을 통해 확인 가능합니다.
래치는 데이터 차제를 보호하는 게 아니라 SGA에 공유되어 있는 자료구조를 보호하는 것입니다. 따라서 cache buffer chains버퍼 캐시에 연결된 체인구조를 보호합니다. 해시 체인을 스캔하거나 거기에 블록을 추가, 제거 할때 래치가 요구됩니다.
4)캐시 버퍼 LRU 체인
버퍼 헤더는 해시 체인 뿐 아니라 LRU(least recently used)체인에 의해서도 연결되어 있습니다. DB 버퍼 캐시는 한번 읽은 데이터 블록을 캐싱해 두는 메모리 공간이지만 메모리는 유한한 자원이어서 모든 데이터를 캐싱할 수는 없습니다. 따라서 사용빈도가 높은 데이터 블록 위주로 구성될 수 있도록 LRU 알고리즘을 사용해 관리됩니다. 사용빈도가 낮은 데이터 블록들을 우선하여 밀어냄으로써 자주 액세스되는 블록들이 캐시에 더 오래 남아 있도록 관리하는 것입니다.
LRU리스트에는 내부적으로 두개의 리스트를 갖습니다.
Dirty 리스트 : 캐시 내에서 변경되었지만, 아직 디스크에 기록되지 않은 Dirty 버퍼 블록들을 관리하며, LRUW 리스트라고도 합니다.
LRU 리스트 : 아직 Dirty 리스트로 옮겨지지 않은 나머지 버퍼 블록들을 관리합니다.
참고로, LRU 리스트를 보호하기 위해 사용하는 래치는 cache buffers lru chain 입니다.
버퍼 상태 - 모든 캐시 버퍼는 세 가지 중 하나의 상태에 놓이게 됩니다.
FREE버퍼 : 인스턴스 기동 후 아직 데이터가 읽히지 않아 비어 있는 상태이거나, 데이터가 담겼지만 동기화되어 언제든지 덮어 써도 무방한 버퍼 블록입니다. 오라클이 데이터 파일로부터 새로운 데이터 블록을 로딩하려면 먼저 이 버퍼를 획득하여야 합니다. FREE버퍼에 변경이 발생하면 DIRTY 버퍼상태로 변경 됩니다.
DIRTY버퍼: 버퍼에 캐시된 이후 변경이 발생했지만, 아직 디스크에 기록되지 않아 데이터 파일 블록과 동기화가 필요한 버퍼 블록을 말합니다. 이 버퍼 블록이 재사용될려면 디스크에 먼저 기록되어야 하며 디스크에 기록되는 순간 FREE버퍼로 상태가 바뀝니다.
PINNED버퍼: 읽기 또는 쓰기 작업을 위해 현재 액세스되고 있는 버퍼블록을 말합니다.
'스터디 > 오라클 성능고도화 원리와 해법1' 카테고리의 다른 글
CH1.오라클 아키텍처 - 8.블록 클린아웃, 9.Snapshot too old (0) | 2019.12.21 |
---|---|
CH1.오라클 아키텍처 - 6.문장수준 읽기 일관성 7.Consistent vs. Current 모드 읽기 (0) | 2019.12.20 |
CH1.오라클 아키텍처 - 4.Redo , 5. Undo (0) | 2019.12.19 |
CH1.오라클 아키텍처 - 3.버퍼 LOCK (0) | 2019.12.18 |
CH1.오라클 아키텍쳐 - 1.기본 아키텍쳐 (0) | 2019.12.16 |
댓글