Free buffer waits
EXEM Knowledge Base
목차 |
[편집] Basic Info
free buffer waits 대기이벤트는 버퍼캐쉬 내부에 데이터블록을 읽어 들이거나, CR(consistent read) 이미지를 생성하기 위한 프리 버퍼(free buffer)를 찾지 못할 때 발생한다. 이것은 버퍼캐쉬가 너무 작거나, 더티(dirty) 블록들을 디스크로 기록하는 작업이 충분히 빠르지 못하다는 것을 의미한다. 버퍼캐쉬내의 프리버퍼를 찾지 못한 프로세스는 DBWR에게 요청신호(더티버퍼를 디스크에 기록해달라는 신호)를 보낸 후 free buffer waits 대기이벤트를 대기한다.
Free buffer가 필요한 포그라운드 프로세스는 정의된 임계치까지 LRU 리스트를 스캔한다. 임계치는 LRU 리스트의 특정 퍼센트만큼 지정되며, 오라클 9i에서의 임계치는 40%이다. 이 수치는 X$KVIT(kernel performance information transitory instance parameters) 뷰의 "Max percentage of LRU list foreground can scan for free” 로 표시된다. 만일 임계치에 도달할 때까지 프리 버퍼를 찾지 못하면, 포그라운드 프로세스는 DBWR 프로세스에게 버퍼를 정리하라고 요청한다. DBWR 프로세스가 해당 작업을 수행하는 동안, 오라클 세션은 free buffer waits 대기이벤트를 대기한다.
오라클은 모든 프리 버퍼 요청에 대한 횟수를 기록한다. 통계정보 명은 V$SYSSTAT 뷰의 free buffer requested 이다. 오라클은 프리 버퍼 요청에 대한 실패 횟수도 기록한다. 이것은 free buffer waits 이벤트의 TOTAL_WAITS에서 찾아볼 수 있다. 프리 버퍼 요청은 기술적으로는 buffer gets이고, 프리 버퍼 요청 실패는buffer misses라 할 수 있다. V$SYSSTAT의 free buffer inspected 통계정보는 오라클 프로세스가 요청된 프리 버퍼를 획득하기 위해서 얼마나 많은 버퍼를 검색했는지를 알려준다. 만일free buffer inspected 수치가 free buffer requested 보다 월등히 높은 경우는, 프로세스들이 가용한 버퍼를 획득하기 위해 LRU 리스트를 그 만큼 더 검색했다는 것을 의미한다. 아래의 쿼리는 시스템 레벨의 free buffer requested, free buffer inspected 및 free buffer waits의 통계정보를 보여준다.
select * from v$sysstat where name in ('free buffer requested', 'free buffer inspected'); STATISTIC# NAME CLASS VALUE --------------- ------------------------- ----- ----------------- 75 free buffer requested 8 3,311,443,932 79 free buffer inspected 8 108,685,547 select * from v$system_event where event = 'free buffer waits'; EVENT TOTAL_WAITS TOTAL_TIMEOUTS TIME_WAITED AVERAGE_WAIT -------------------- ----------------- -------------------- ---------------- ------------------- free buffer waits 30369 15795 2187602 72.0340479
[편집] Parameter & Wait Time
[편집] Wait Parameters
free buffer waits 대기이벤트의 대기 파라미터는 다음과 같다.
- P1 : 오라클이 버퍼캐쉬로 읽어 들이려는 블록에 해당되는 File#
- P2 : 오라클이 버퍼캐쉬로 읽어 들이려는 Block#
- P3 : 오라클 10g 이전에서는 사용되지 않았다. 오라클 10g부터 LRU, LRUW 리스트의 SET_ID#를 나타낸다
[편집] Wait Time
가용한 프리버퍼를 위해 1초까지 대기하며, 타임아웃 발생 후 또다시 1초를 대기한다.
[편집] Check Point & Solution
[편집] 비효율적인 SQL문
비 효율적인 SQL문들이 이벤트 발생의 주요 원인으로 작용한다. V$SQL 뷰에서 대량의 디스크 읽기 I/O (DISK_READS)를 수행하는 SQL문을 찾아보자. 해당 SQL문들은 full table scan, index fast full scan 또는 선택도가 좋지 않은 인덱스를 사용하여 테이블을 접근할 것이다. 디스크 읽기 I/O 요청을 줄이도록 SQL문을 튜닝 해야 한다. 가장 효율적인 조인방법과 액세스경로를 사용해야 한다. 또한, 가능하다면 어플리케이션 레벨에서 direct loads, direct inserts, parallel queries와 같은 direct reads/writes를 사용하는 것이 바람직하다. Direct read / write오퍼레이션은 SGA를 경유하지 않는다.
[편집] 불충분한 DBWR 프로세스 수
LGWR 프로세스가 로그 버퍼의 공간을 확보하는 역할을 수행하는 것처럼, DBWR 프로세스는 LRU 리스트상의 프리 버퍼를 확보하는 역할을 수행한다. DBWR 프로세스는 버퍼 캐시의 “working set” 과 관계가 있다. “working set” 은 LRU 와 LRUW 리스트로 구성된다. 만일 하나의 DBWR 프로세스만 존재한다면, 모든 “working set” 에 대한 서비스를 하나의 DBWR 프로세스가 수행해야 한다. 다수의 DBWR 프로세스가 존재한다면, 오라클은 ”working set” 을 균등하게 분배하게 된다. 당연히, 더 많은 DBWR 프로세스는 더욱 효율적으로 “working set” 을 서비스 할 수 있고 더 높은 처리량을 보장할 수 있다. 아래의 X$KCBWDS (kernel cache buffer working set descriptors) 뷰의 결과는 8개의 “working set” 과 2개의 DBWR 프로세스가 있음을 나타낸다.
select set_id, dbwr_num from x$kcbwds order by set_id; SET_ID DBWR_NUM ---------- -------------- 1 0 2 0 3 0 4 0 5 1 6 1 7 1 8 1
많은 사람들이 비동기식 I/O 환경에서는 하나의 DBWR 프로세스를 사용하라는 이야기를 들은 적이 있을 것이다. 이것은 단지 가이드라인에 불과하며, free buffer waits 대기이벤트가 발생하는 경우에는 비동기식 I/O 환경에서도 다수의 DBWR 프로세스를 사용할 수 있다. CPU_COUNT에 따라서 DB_WRITER_PROCESSES 파라미터를 조정하여 DBWR 프로세스의 개수를 증가 시킬 수 있다. 하지만, SQL문이 최적화 되지 않아서 다수의 버퍼를 사용할 경우에는 별다른 효과가 없게 된다.
게다가 다수의 DBWR 프로세스를 사용하면, DBWR 체크포인트 횟수가 증가함에 따라 free buffer waits 발생 횟수를 감소 시킬 수 있다. 오라클 9i에서, FAST_START_MTTR_TARGET 파라미터를 이용하여 MTTR(Mean Time To Recovery)을 단축시키는 방법으로도 동일한 효과를 낼 수 있다. 더 짧은 MTTR은 DBWR 프로세스의 활동성을 높이게 하여, 결과적으로 클린 버퍼(clean buffer)의 공급을 더 원활하게 만든다. 하지만, DBWR 프로세스의 활동성을 높이는 것은 디스크로 전송되는 블록을 변경하려는 프로세스들이 write complete waits 대기이벤트를 대기할 가능성을 높이게 된다. 따라서 어플리케이션의 유형에 따라 적절한 균형점을 찾아야 한다.
[편집] 느린 I/O 서브 시스템
만일 DBWR에서 db file parallel write 대기시간이 길게 나타난다면 I/O 시스템에 문제가 있다고 판단할 수 있다. DBWR에서 db file paralle write 대기시간이 길어지면, 서버 프로세스는 연쇄적으로 free buffer waits 대기나 write complete waits 대기를 겪게 된다. 로디바이스(Raw device)와 AIO(Asychronous IO. 비동기 I/O)를 조합해서 사용하는 것이 I/O 성능을 개선시키는 가장 좋은 방법으로 알려져 있다. 로디바이스가 아닌 경우라도 AIO를 지원하는 시스템이 있으므로 이를 활용할 수 있다. AIO를 사용하는 경우, DBWR의 개수를 늘리는 것은 의미가 없다는 의견이 많다. 복수개의 DBWR을 사용하는 목적이 결국 AIO를 소프트웨어적으로 흉내내는 것이기 때문이다. 하지만 Write가 매우 많은 시스템이라면 AIO와 복수개의 DBWR을 동시에 구성하는 것도 가능하다. OS차원에서 Direct I/O를 사용하는 것도 성능에 큰 도움이 된다. Direct I/O를 사용하는 경우에는 로디바이스를 사용할 필요가 없다는 의견도 있다. Direct I/O를 사용하는 경우에는 CPU 개수가 충분하다면, DB_WRITER_PROCESSES 값을 조정해서 DBWR의 개수를 증가시키는 것을 병행할 수 있다. 오라클의 기본 DBWR 개수는 CPU_COUNT / 8 이다.
I/O시스템의 성능을 높이는 방법 (IO_enhance 링크)
[편집] Delayed Block Cleanouts
테이블이 버퍼 캐쉬로 적재된 후에, 어플리케이션에서 해당 테이블을 사용하기 전에 테이블에 대한 full table scan 오퍼레이션을 수행할 필요가 있다. 이러한 이유는 해당 데이터 블록을 처음으로 읽는 프로세스가 delayed block cleanout을 수행해야 하며, 이것은 free buffer waits 대기이벤트를 야기 시킨다. 따라서, 어플리케이션에서 해당 테이블을 사용하기 전에 full table scan을 수행함으로써 어플리케이션에서 발생할 free buffer waits 대기이벤트를 제거할 수 있게 된다. 아래의 예제 테스트를 이용하여 오라클 9i에서의 처리 방법을 알 수 있을 것이다.
- 버퍼 캐쉬를 20,000 블록으로 설정한다.
- FAST_START_MTTR_TARGET을 5초로 낮게 설정한다. 이것은 DBWR가 활발하게 더티 버퍼를 디스크에 기록하게 하여 향후에 해당 블록에 대한 쿼리를 수행할 경우 반드시 delayed block cleanout을 수행하도록 한다.
- 인덱스 없는 테이블에 많은 양의 레코드(약 150만 건 정도)를 insert한 후 커밋을 수행한다.
- 데이터베이스를 셧다운(shutdown)한 후 재 구동 한다.
- 테이블의 레코드를 카운트 해보자. 오라클 프로세스는 해당 테이블에 대한 full table scan 오퍼레이션을 수행할 것이다. V$SESSION_EVENT 뷰를 조회하여 이 세션을 찾아보자. delayed block cleanout으로 인해 free buffer waits 대기이벤트를 대기하고 있음을 확인할 수 있을 것이다.
- 4번과 5번의 과정을 반복해 보자. 하지만, 이번에는 old ITL들이 정리되어 free buffer waits 대기이벤트가 발생하지 않는다
| Delayed block cleanout 과 commit cleanout - 트랜잭션은 버퍼 캐쉬 내의 많은 블록들을 더티(dirty)로 만들 수 있다. DBWR 프로세스는 다양한 간격으로 커밋 블록과 커밋 되지 않은 블록들을 데이터파일에 기록한다. 트랜잭션이 커밋 되었을 때, 오라클 프로세스는 DBWR 프로세스에 의해 디스크로 기록되지 않은 블록에 대해서 commit cleanout을 수행한다. 이미 디스크에 기록된 블록들은 해당 블록을 처음 읽는 프로세스에 의해 정리(cleaned)된다. 이것을 delayed block cleanout이라고 한다. 더 많은 정보를 원할 경우 메타링크 Note #40689.1을 참조하라. |
[편집] 작은 버퍼 캐시
버퍼 캐쉬가 너무나 작아서 프리 버퍼 요청을 수용할 수 없을 경우에도 free buffer waits 경합이 발생하게 된다. 이것을 마지막 원인으로 다룬 이유는 요즘은 버퍼 캐쉬를 이렇게 작게 설정하는 것은 일반적이지 않기 때문이다. 오늘날의 데이터베이스 서버들은 충분한 메모리를 장착하므로, DBA들은 버퍼 캐쉬를 크게 설정하는 경향이 있다. 많은 DBA들은 SGA를 가용한 메모리의 50%로 할당할 필요가 있다고 생각하고 있다. 버퍼 캐쉬의 크기를 증가시키기 전에, DBWR 프로세스의 개수를 증가시킨 후 free buffer waits 대기이벤트의 대기횟수가 감소하는지 확인해야 한다. 그 이후에 버퍼 캐쉬의 크기를 증가시켜야 한다.
[편집] Event Tip
[편집] Buffer 적재 과정
- 사용자가 요청한 블록의 DBA에 대해 해시 함수를 적용해서 적절한 해시 버킷(Hash Bucket)을 찾는다.
- 해시 버킷에 딸려있는 해시 체인(Hash chain)을 탐색해서 블록에 해당하는 버퍼 헤더가 존재하는지 확인한다. 버퍼 헤더가 이미 존재하고 해당 블록이 버퍼 캐시에 올라와 있는 상태라면 해당 블록을 사용한다.
- 아직 버퍼 캐시에 존재하지 않으면 우선 LRU 리스트를 가장 덜 사용된 순서로 프리 버퍼를 찾는다. 이 과정에서 더티 버퍼가 발견되면 LRUW 리스트로 이동시킨다. 프리 버퍼를 찾게 되면 데이터 파일로부터 블록을 해당 버퍼로 읽어들인다.
- LRU 리스트에서 프리 버퍼를 찾을 때 _DB_BLOCK_SCAN_MAX_PCT(기본값은 40) 파라미터 값만큼 스캔을 하고도 프리 버퍼를 찾지 못하면 오라클은 스캔을 멈추고 DBWR에게 더티 블록을 디스크에 기록하고 프리 버퍼를 확보할 것을 요청한다. 쓰기가 완료되면 해당 버퍼를 사용한다.
[편집] Analysis Case
[편집] Free Buffer Waits 분석 사례
분석 대상 인스턴스는 Active Session이 급증한 구간이 존재하며, 이 시점의 Stat/Event/Active Session List 정보를 통해 Free Buffer Waits 이벤트 대기가 발생함을 알 수 있다.
특히, Active Session List에 나타나 있는 Logical Reads/Physical Reads 값이 모두 0이며, 이는 세션들이 Free Buffer Waits 이벤트의 대기로 인하여 전혀 I/O 작업을 하지 못하고 있다는 것이다.
읽으려는 블록이 버퍼캐시에 존재하지 않는 경우, LRU 리스트에서 프리버퍼를 찾아 데이터 파일로부터 블록을 해당 버퍼로 읽어들여야 한다. 그런데, LRU 리스트의 일정 영역을 스캔하고도 프리 버퍼를 얻지 못한 경우, LRU 리스트의 스캔을 멈추고, DBWR에게 더티블록을 디스크에 기록하여 프리버퍼를 확보하도록 요청한다. 이때, 서버 프로세스는 DBWR의 쓰기작업이 완료될 때까지 Free Buffer Waits 이벤트를 대기한다.
따라서, 스캔해야 하는 블록이 많은 비효율적인 SQL문과 DBWR의 느린 성능, 작은 버퍼캐시의 크기가 Free Buffer Waits 이벤트의 발생 원인이 될 수 있다. 특히, DBWR의 느린 성능은 Free Buffer Waits 이벤트와 함께 Write Complete Waits 이벤트의 원인이 된다. Write Complete Waits 이벤트는 DBWR가 기록하고 있는 더티버퍼를 서버프로세스가 요청할 때 발생하는데, 디스크에 기록중인 버퍼를 읽을 확률이 실제로 높지 않으므로 자주 발생한다면, DBWR가 더티버퍼를 기록하는 시간이 지나치게 길다는 것을 의미한다.
문제 구간에서 Write Complete Waits 이벤트가 발생하는지, DBWR가 DB file Parallel Write 이벤트를 대기하는지 다음의 그래프를 통해 확인해 본다.
문제가 발생한 구간에서 Write Complete Waits 이벤트와 DB File Parallel Write 이벤트에 대한 대기는 거의 나타나지 않는다. DBWR의 느린 성능 때문에 Free Buffer Waits가 발생한 것은 아닌 것으로 보인다.
Active Session의 추이를 Free Buffer Waits, Free Buffer Inspected, Free Buffer requested, Physical Reads 지표의 추이와 함께 비교해 본다.
Active Session과 Free Buffer Waits 이벤트의 추이가 일치하며, Free Buffer Inspected 지표를 보고 대기 시점에 오라클 프로세스들은 요청된 프리 버퍼를 획득하기 위해서 500개 이상의 버퍼를 검 색했음을 알 수 있다.
또한, Free Buffer requested와 physical reads 지표의 추이를 통해 문제가 발생한 Active Session Peak 이전 시점에 프리버퍼 요청이 증가했음을 알 수 있다. 즉, 프리버퍼 요청이 증가한, Physical Reads Peak 시점(성능저하 전 시점)으로 이동할 필요가 있다.
성능저하 시점 직전으로 이동하여 Physical reads를 기준으로 Active Session들을 정렬해 보면, 1분 동안 13만 블록 이상을 디스크에서 스캔한 SQL문이 확인된다. 이 SQL문의 Detail 정보를 보자.
9시 10~20분 대에 해당 SQL문은 총 249번 수행되었으며, 수행하는 동안 메모리에서는 1678만 블록 이상을 스캔하였고, 디스크에서는 118만 블록 이상을 읽었다. 1번 수행 시, 약 67000블록 이상을 스캔한 것으로, 이 SQL문의 수행으로 인하여 버퍼캐시를 사용하려는 다른 세션들은 Free Buffer Waits 이벤트를 대기한 것으로 추측된다. 해당 SQL문의 블록 스캔 양을 줄일 수 있도록 튜닝이 필요할 것으로 생각된다. 또한, CPU의 개수가 충분하다면, Parallel Query로 변경하여 direct I/O를 이용할 수는 없는지 생각해 볼 필요가 있다.