01 | 오라클 아키텍처
03 ) 버퍼 Lock
(1) 버퍼 Lock ?
DB 버퍼 캐시 내에서 버퍼 블록을 찾고, 래치를 빠르게 해제하지 않으면 cache buffers chains 래치에 여러 개의 해시 체인이 달렸으므로 래치에 대한 경합 발생 가능성이 증가하게 된다.
캐시된 버퍼 블록을 읽거나 변경하려는 프로세스는 먼저 버퍼 헤더로부터 버퍼 Lock(buffer pin) 을 흭득해야 한다.
버퍼 Lock (buffer pin)을 흭득햇다면 래치를 곧바로 해제해야한다.
버퍼 내용을 읽기만 할 때는 Share 모드, 변경할 때는 Exclusive 모드로 Lock을 설정한다.
액세스를 직렬화하기 위한 메커니즘이므로 당연히 Exclusive 모드 Lock은 한 시점에 하나의 프로세스만 얻을 수 있다. Select 문이더라도 Block Cleanout이 필요할 때는 버퍼 내용을 변경하는 작업이므로 Exclusive 모드 Lock을 요구한다.
💡 Block Cleanout → 성능 최적화를 위해 사용
- 트랜잭션 완료정보를 지연해서 처리하는 최적화기법
- 트랜잭션 완료여부를 블록 내에 표시(clean)하지 않고 남겨뒀다가, 이후 누군가 그 블록을 읽을 때 처리(cleanout)하는 메커니즘
- 성능을 위해서 트랜잭션이 커밋되었을때 모든 블록을 즉시 수정하지 않는다. 대신 해당 트랜잭션이 어떤 블록을 변경했는지, undo, redo 로그에만 저장한다.
- 블록내의 트랜잭션 메타 데이터(Interested Transaction List)는 "나중에" 정리한다. 나중에 정리하는 것이 Block Cleanout이다.
📌 Why 성능 최적화?
▶️ 트랜잭션이 커밋될때마다 모든 관련 블록을 다시 찾아가서 "커밋됨"을 표시하면 디스크 I/O 비용과 latch 경합이 매우 커진다.
ex) 트랜잭션 A가 1000개의 데이터 블록을 변경 → COMMIT → 1000개의 데이터 블록을 모두 찾아가 "COMMIT됨 표시" ▶️ COST 가 큼
트랜잭션 A가 1000개의 데이터 블록을 변경 (ITL 기록; 이 트랜잭션이 수정 중 표시) + UNDO 생성 → COMMIT(redo 기록, SCN 부여) → 트랜잭션 B가 트랜잭션 A가 변경한 데이터블록 접근(ITL확인) → UNDO에 연결된 트랜잭션 상태를 확인한 후 Block Cleanout 처리
📌 버퍼 Lock 대기모드
- 찾아낸 버퍼 캐시상의 대상 버퍼가 타 프로세스에 의해 exclusive lock이 흭득된 상태인 경우 버퍼 헤더에 있는 버퍼 Lock 대기자 목록에 등록 후 대기 (해당 요청을 했던 프로세스에 할당된 cache buffers chain latch는 이 시정에 해지된다.)
- 이렇게 동작해야 또 다른 프로세스가 해당 버퍼 캐시에 요청을 했을때 latch wait이 발생하지 않고 버퍼 lock 대기자 목록에 자신의 정보를 기록할 수 있다.
- buffer busy waits 대기 이벤트 발생
- 목적한 읽기/쓰기 작업을 완료하면 버퍼 헤더에서 버퍼 Lock 을 해제해야 하는데, 이때도 버퍼 헤더를 액세스하려는 다른 프로세스와 충돌이 생길 수 있으므로 해당 버퍼가 속한 체인 래치를 다시 한번 흭득한다. ▶ 버퍼 Lock 해제의 경우 버퍼헤더에서 해당 버퍼 블록에 대한 상태를 수정하는 작업임(pin count 수정), 다른 프로세스가 버퍼 체인 스캔을 하며 버퍼 헤더에 접근하게 되면 충돌이 발생할 수 있다. 그렇기 때문에 Lock 해지할 때에도 Buffer Chains Latch를 재흭득해야 함.
⭐️ 버퍼 블록을 읽을때 대부분 두번의 래치 흭득을 요구한다. (버퍼 헤더 검색 후 pin / Un-pin)
일부 작업의 경우 래치를 흭득한 상태로 버퍼 블록을 읽기 때문에 래치 흭득이 한번만 일어난다. (v$sysstat 뷰에서 consistent gets - examination 통계항목으로 측정)
버퍼 Lock을 해제하고 Latch Lock을 해제해야 비로소 버퍼 블록 읽기가 완료된다.
(2) 버퍼 핸들
버퍼 Lock을 설정하는 것은 자신이 현재 그 버퍼를 사용중임을 표시해 두는 것으로서, 그 버퍼 헤더에 Pin을 걸었다고도 표현한다.
읽기 작업 : 여러 프로세스가 Pin 설정 가능
변경 작업 : 하나의 프로세스만 Pin 설정 가능
버퍼 헤더에 Pin을 설정하려고 사용하는 오브젝트를 버퍼 핸들(Buffer Handle)이라고 부르며, 버퍼 핸들을 얻어 버퍼 헤더에 있는 소유자 목록(Holder Lost)에 연결시키는 방식으로 Pin을 설정한다.
버퍼 핸들도 공유된 리소스이므로 버퍼 핸들을 얻으려면 또 다른 래치가 필요해지는데, 바로 cache buffer handles 래치이다.
버퍼를 Pin하는 작업이 많을수록 cache buffer handles 래치가 경합 지점이 될것이므로, 오라클은 각 프로세스마다 _db_handles_cached 개수만큼의 버퍼 핸들을 미리 할당해 주며, 기본값은 5이다.
각 세션은 이를 캐싱하고 있다가 버퍼를 Pin할때마다 사용하며, 그 이상의 버퍼 핸들이 필요할 때만 cache buffer handles 래치를 얻고 추가로 버퍼 핸들을 할당받는다.
시스템 전체 버퍼 핸들 개수 (_db_handles) = processes * _db_handles_cached
(3) 버퍼 Lock의 필요성
- 단일 레코드 갱신시의 버퍼 Lock 필요성
- 단일 레코드를 갱신 → 사용자 데이터 변경 시 DML Lock 을 통해서 정합성을 유지
- DML Lock 만으로는 정합성 유지에는 불충분하다. → 하나의 레코드를 갱신하더라도 블록 단위로 I/O를 수행하기 때문에 , 블록 안에 저장된 10개의 레코드를 읽는 짧은 순간 동안 다른 프로세스에 의해 변경이 발생하면 잘못된 결과를 얻게 된다.
- 버퍼 Lock 필요
- Row-Level Lock에서의 버퍼 Lock 필요성
- row-level lock 설정 자체도 레코드의 속성을 변경한다.
- 두 프로세스가 동시에 row-level lock을 설정할 경우 문제가 발생한다. ( 대상 로우가 다르더라도 )
- 블록 SCN 변경 / ITL 슬롯 변경도 블록 헤더 내용을 변경하는 작업으로 동시 액세스가 발생하면 Lost Update 문제가 생겨 블록 자체의 정합성이 깨질 수 있다.
- 블록 진입 자체를 직렬화 해야한다.
(4) 버퍼 Pinning
"버퍼 Pinning" 버퍼를 읽고 나서 버퍼 Pin을 즉각 해제하지 않고 데이터베이스 Call이 진행되는 동안 유지하는 기능
같은 블록을 반복적으로 읽을 때 버퍼 Pinning을 통해 래치 흭득 과정을 생략하면 논리적인 블록 읽기(Logical Reads) 횟수를 획기적으로 줄일 수 있다.
같은 블록을 재방문할 가능성이 큰 몇몇 작업들을 수행할 때만 사용한다.
v$sysstat, v$sesstat, v$mystat 등을 조회해 보면, 래치 흭득 과정을 통해 블록을 액세스 할 때는 session logical reads 항목이 증가하고, 래치 흭득 과정 없이 버퍼 Pinning 을 통해 블록을 곧바로 액세스 할 때는 buffer is pinned count 항목의 수치가 증가한다.
버퍼 pinning은 하나의 데이터베이스 Call(Parse call, execute call, fetch call) 내에서만 유효하다. 즉 Call이 끝나고 사용자에게 결과를 반환하고 나면 Pin 은 해제되어야 한다.
전통적으로 버퍼 Pinning이 적용되던 지점은 인덱스를 스캔하면서 테이블을 액세스할 때의 인덱스 리프 블록이다.
Index Range Scan하면서 인덱스와 테이블 블록을 교차 방문할 때 블록 I/O를 보면, 테이블 블록에 대한 I/O만 증가하는 이유가 여기있다. → 클러스터링 팩터가 좋은 인덱스의 경우 효과는 극대화 된다. (= 인덱스 레코드가 가리키는 테이블 rowid 정렬 순서가 인덱스 키 값 정렬 순서와 거의 일치한다면)
이외에도 DML 수행시 Undo 레코드를 기록하는 Undo 블록도 Pinning 적용
'Database > Oracle' 카테고리의 다른 글
| [Oralce] 오라클 성능 최적화 (2) (0) | 2026.03.18 |
|---|---|
| [Oracle] 오라클 성능 고도화 (1) (0) | 2026.03.18 |
