DB 버퍼 캐시 내에서 버퍼 블록을 찾았으면 빨리 래치를 해제해야한다. 아니면 cache buffers chains 래치에 여러 개의 해시 체인이 달렸으므로 래치에 대한 경합 발생 가능성이 증가한다. 그럼 선행 프로세스가 아직 버퍼를 사용 중이라면 데이터 정합성이 문제다.

두 개 이상의 프로세스가 동시에 버퍼 내용을 읽고 쓰면 문제가 생길수 있다.

 

버퍼 Lock이란?

위의 문제를 막기위해 캐시된 버퍼 블록을 읽거나 변경하려는 프로세스는 먼저 버퍼 헤더로부터 버퍼 Lock을 획득해야한다. 획득 후 래치를 곧바로 해제한다. 버퍼 내용을 읽기만 할때는 Share 모드, 변경할때는 Exclusive 모드로 Lock을 설정한다. 액세스 직렬화하기 위한 매커니즘이므로 당연히 Exclusive Lock은 한 시점에 하나의 프로세스만 얻을 수 있다. select문이더라도 블록 클린아웃이 필요할때는 버퍼 내용을 변경하는 작업이므로 Exclusive 모드 Lock을 요구한다. 뭔소린지 모르겠다.

해시 체인 래치를 획득하고 목적한 버퍼를 찾았는데 다른 프로세스가 버퍼 Lock을 Exclusive 모드로 점유한 채 내용을 갱신 중이라면 래치를 쥔채 기다릴수없으니 버퍼 헤더에 있는 버퍼 Lock 대기자 목록에 자신을 등록하고 래치를 해제한다. Lock 대기자 목록에 등록돼있는 동안 buffer busy waits 대기가 발생한다. 버퍼 Lock이 해제되면 버퍼 Lock을 획득하고 작업을 시작한다.목적한 읽기쓰기가 완료되면 버퍼 헤더에서 버퍼 Lock을 해제해야 하는데, 이때도 버퍼 헤더를 액세스하려는 다른 프로세스와 충돌이 생기므로 해당 버퍼가 속한 체인 래치를 다시 한번 획득한다. 버퍼 Lock을 해제하고 래치를 해제해야 비로소 버퍼 블록 읽기가 완료된다.

 

버퍼 핸들이란?

버퍼 헤더에 Pin을 설정하고 사용하려는 오브젝트를 '버퍼 헤더'라고 부른다. 버퍼 핸들을 얻어 버퍼 헤더에 있는 소유자 목록에 연결시키는 방식으로 Pin을 설정한다. 버퍼 핸들도 공유된 리소스 이므로 버퍼 핸들을 얻으려면 또 다른 래치가 필요해지는데 이를 cache buffer handles 래치다. 버퍼를 Pin하는 오퍼레이션이 많을 수록 cache buffer handles 래치가 경함지점이 될것이므로 각 프로세스마다 _db_handles_cached 개수많큼 버퍼 핸들을 미리 할당해주며 기본값은 5개다. 각 세션은 이를 캐싱하고 있다가 버퍼 Pin 할 때마다 사용하며, 그 이상 버퍼 핸들이 필요할때만 cache buffer handles 래치를 얻고 추가로 버퍼 핸들을 할당받는다.

시스템 전체적으로 사용할 수 있는 총 버퍼 핸들 개수는 _db_handles 파라미터(=processes 파라미터 x _db_handles_cached)에 결정된다 

 

버퍼 Lock의 필요성

사용자 데이터를 변경할 때는 DML Lock을 통해 보호하도록 돼 있는데, 그것을 담는 블록에 또 다른 Lock을 획득해야하는 이유는?

-> 오라클이 하나의 레코드를 갱신하더라도 블록 단위로 I/O을 수행한다. 블록안에 저장된 10개의 레코드를 읽는 짧은 순간 동안 다른 프로세스에 의해 변경이 발생하면 잘못된 결과를 얻는다.

 그리고 값을 변경하기 전에 레코드에 로우 단위 Lock을 설정하는 일 자체도 레코드의 속성을 변경하는 작업이므로 두 개의 프로세스가 동시에 로우 단위 Lock을 설정하려고 시도한다면 문제된다. 블록 SCN을 변경하거나 ITL 슬록에 변경을 가하는 등 블록 헤더 내용을 변경하는 작업도 동시에 일어날 수 있는데. 이런 동시 액세스가 실제로 발생한다면 Lost Update 문제가 생겨 블록 자체의 정합성이 깨진다.

-> 블록 자체로의 진입을 직렬화해야한다.

 Pin된 버퍼 블록은 버퍼 캐시 전체를 비우려고 아래 시스템 명령어를 날리더라도 밀려나지 않는다.

 

버퍼 Pinning

버퍼를 읽고 나서 버퍼 Pin을 즉각 해제하지 않고 데이터베이스 Call이 진행되는 동안 유지하는 기능. 같은 블록을 반복적으로 읽을 때 버퍼 Pinning을 통해 래치 획득 과정을 생략한다면 논리적인 블록 읽기 횟수를 획기적으로 줄일 수 있다. 같은 블록을 재방문할 가능성이 큰 몇몇 오퍼레이션을 수행할 때만 사용

v$sysstat, v$sesstat, v$mystat 등을 조회하면 래치 획득 과정을 통해 블록을 액세스할때는 session logical reads항목이 증가하고, 래치 획득 과정 없이 버퍼 Pinning을 통해 블록을 곧바로 액세스할때는 buffer is pinned count 항목의 수치가 증가한다.

버퍼 Pinning은 하나의 데이터베이스 Call내에서만 유효하다. 즉 Call이 끝나고 사용자에게 결과를 반환하고 나면 Pin은 해제되어야한다. 따라서 첫 번째 Fetch Call에서 Pin된 블록은 두번째 Fetch Call에서 다시 래치 획득 과정을 거쳐 Pin 되야한다.

전통적으로 버퍼 Pinning이 적용되던 지점은 인덱스를 스캔하면서 테이블을 액세스할 때의 인덱스 리프 블록이다. Index Range Scan하면서 인덱스와 테이블 블록을 교차 방문할때 블록 I/O를 체크해보면 테이블 블록에 대한 I/O만 계속 증가하는 이유다.

 

+ Recent posts