데이터 검색

해당 블록이 메모리에 존재하는지 확인 메커니즘 탐구

  • 오라클은 많은 수의 버킷을 가진 해시 테이블 사용
  • 각 버킷에 연결된 버퍼 헤더의 링크드 리스트는 매우 짧음
  • 버킷들을 작은 그룹으로 그룹핑 한 후, 각 그룹을 cache buffer chains 래치로 보호

매우 작은 오라클 버퍼 캐시

!데이터 검색.버퍼 캐시.PNG|vspace=4!

하나의 버퍼 헤더에 대한 덤프 내용


BH (66BEAF5C) file#: 1 rdba: 0x004096b0 (1/38576) class: 1 ba: 668D8000
  set: 3 blksize: 8192 bsi: 0 set-flg: 0 pwbcntg: 0
  dbwrid: 0 obj: 18 objn: 18 tsn: 0 afn: 1
  hash: [6ffa0870,6ffa0870] lru: [66bfab94,66bed2f0] -- 하나의 버퍼 헤더만 존재 (forward, backward 포인터 동일) 
  lru-flags: moved_to_tail on_auxiliary_list
  ckptq: [NULL] fileq: [NULL] objq: [66bed348,66bfabec] -- 더티 상태가 아님 (ckptq, fileq NULL)
  st: XCURRENT md: NULL tch:0
  flags: only_sequential_access
  LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]

DB CACHE HASH BUCKETS

  • 오라클 8.0 까지 해시 버킷 수는 대략 db_block_buffers/4 결과에 가까운 소수로 반올림, 하나의 래치는 하나의 해시 버킷 보호
  • 이후, 해시 버킷의 수는 대략 버퍼의 2 배수 결과에 가까운 소수로 반올림, 하나의 래치는 여러 개의 버킷을 보호 (래치당 32 ~ 128 개의 버킷 보호)
    • 8.1.7.4 에서 _db_block_hash_buckets 는 16,384 (소수아님) 이나, SGA 변수인 kcbnhb 를 통해 확인한 실제 해시 버킷 수는 16,411 (소수)
  • 최신 버전(10g, 11g) 에서, 해시 버킷의 수는 2의 지수 승이며 (ASMM, AMM), 래치당 버킷의 수는 32로 고정
    • 2진수의 해시 버킷 번호를 오른쪽으로 5번 쉬프트 하면 래치 번호 확인됨 (해시 버킷 번호를 32로 나눔)
  • 블록이 위치한(할) 해시 버킷을 식별하기 위해 절대 파일 번호와 블록 번호를 이용

버퍼 Pinning

  • 버퍼 관련 작업시
예상실제
1. 블록 주소를 이용하여 정확한 해시 버킷 계산
2. 연관된 cache buffers chains 래치 획득
3. 해당 버퍼를 찾기 위해 해시 버킷으로 부터 검색 시작
4. (래치를 획득한 채로) 버퍼 관련 작업 수행
5. cache buffers chains 래치 릴리즈
1. 블록 주소를 이용하여 정확한 해시 버킷 계산
2. 래치 획득
3. 버퍼를 찾고 pin 설정
4. 래치 릴리즈
5. 버퍼 관련 작업 수행
6. 래치를 획득
7. 버퍼를 unpin 설정
8. 래치 릴리즈

하나의 래치로 32개 이상의 해시 버킷을 보호 하는것은 ①매우 짧은 해시 체인 유지 ②래치의 수 최소화 를 위함임

한 쌍의 pinned 버퍼를 소유한 해시 체인

!데이터 검색.해시 체인.PNG|vspace=4!

  • 사용자 리스트, 대기자 리스트도 이중 링크드 리스트로 관리 (x$bh 의 us_nxt, us_prv, wa_nxt, wa_prv 컬럼)
  • 버퍼 핀(pin)은 공유(S) 모드 혹은 독점(X) 모드로 설정, 원하는 버퍼가 이미 비호환 모드로 핀 되어 있다면 대기자 리스트에 핀 등록 후 가용할 때 까지 buffer busy waits 대기 (다른 세션이 디스크로 부터 읽는 중 이라면 10g 부터 read by other session 대기)
  • 핀 구조는 핀을 소유 중이거나 대기 중인 세션의 주소를 포함
  • 블로킹 세션이 핀을 릴리즈 할때, 대기자 리스트의 첫번째 대기 세션을 포스트 한다(블로킹 세션이 S/X 이고 대기자가 X 일때)
  • 대기 세션이 buffer busy waits 타임아웃(1초, _buffer_busy_wait_timeout)내 포스트 되지 못하면 buffer deadlock 이벤트 보고(데드락이 발생했다고 추정)하고 자신이 소유한 모든 핀을 릴리즈 한 후, 필요한 핀을 재 획득 한다
  • 쿼리 엔진이 버퍼가 조만간 재 방문될 것이라고 판단하면, 가능한 한 버퍼 작업이 완료 된 후 즉시 핀을 릴리즈 하지 않는다 (buffer is pinned count 성능 통계)

cache buffers chains 래치 사용처는 1. 해시 버킷 체인에 버퍼 헤더를 추가/제거, 2. 핀 리스트에 핀 추가/제거, 3. 핀 모드 변경
버퍼 내용 변경은 래치가 아니라 핀 설정 필요

논리적 I/O

  • 버퍼 캐시내에 존재하는 블록의 내용을 확인하는 예
  • 해당 블록이 연결되어 있는 cache buffers hash chain 확인 후, 래치 획득 후 체인 검색
    1. CASE1) "single-get" or "half-price" : 체인 검색 시 블록을 조사 하고 즉시 래치를 릴리즈(consistent gets - examination), 인덱스 루트, 브랜치 블록, unique 인덱스의 리프 블록, unique 인덱스의 unique scan 에 의해 액세스 되는 테이블 블록 및 언두 블록 한정
    2. CASE2) "two-latch get" : 래치 획득 후, 체인 검색, 버퍼헤더에 핀(S) 설정, 래치 릴리즈, 블록 내용 확인, 래치 획득, 핀(S) 릴리즈, 래치 릴리즈 (이때 해당 버퍼가 수 microseconds 이내 다시 방문된다고 판단하면, 현재 데이터베이스 콜이 완료될 때 까지 해당 버퍼를 핀 상태로 유지)
      {note}
      버퍼 핀은 버퍼 핸들 (buffer handle)로도 불림
      _db_handles : 핀 구조의 배열 크기 정의
      _db_handles_cached : 세션에 의해 예약 될 수 있는 핀의 개수 (기본값 : 5)
      _cursor_db_buffers_pinned : 한 시점에 세션이 최대로 설정할 수 있는 핀 수
      "buffer is pinned count" 성능 통계 : 버퍼 재 방문 시 핀 설정된 버퍼를 이용한 횟수
      {note}

데이터 변경

  • primary key 에 의한 update
    1. 래치 획득 후 체인 검색
    2. 버퍼 헤더에 핀(X) 설정 후 래치 릴리즈
    3. 블록 변경
    4. 래치 획득 후 핀 릴리즈 후 래치 릴리즈
    ※ 언두 포함 하므로, 두개의 cache buffers chains 래치 필요
  • tablescan 에 의한 update
    1. 새로운 블록에 기존 블록을 복사
    2. 기존 블록을 CR 블록으로 변경 후 새로운 블록 변경 (switch current to new buffer 성능통계)
    ※ 반복 되어 CR 블록 수가 _db_block_max_cr_dba (기본값 : 6) 를 넘어서면 효율을 위해서 CR 블록들을 REPL_AUX 리스트로 이동 시킴

해시 체인 로딩

  • 버퍼를 교체 리스트에서 분리하여 해시 체인에 연결하는 작업 (물리적 I/O, CR 복제본 생성, current 블록을 새로운 블록으로 복사)
  • 두개 래치 필요 : cache buffers lru chain (레벨2/교체 리스트), cache buffers chains (레벨1/해시 체인)
    ※ 획득 하려는 래치의 레벨보다 낮은 레벨의 래치를 획득한 상태라면 willing-to-wait 모드로 래치를 요청할 수 없음
  • cache buffers chains 래치 획득한 세션 (a)가 다른 버퍼를 체인에 연결(REPL_AUX 리스트의 버퍼를 REPL_MAIN 리스트의 midpoint 로 이동)하기 위해서 cache buffers lru chain 래치가 필요하나 willing-to-wait 모드로 요청하지 못함
  • 따라서 cache buffers chains 래치를 릴리즈 한 후, cache buffers lru chain 래치를 획득 한 후, 다시 cache buffers chains 래치를 획득 해야 함

CR 복제

  • CR 복제본 생성을 위해서 새로운 버퍼를 해시 체인에 연결하는 과정
    1. 블록의 current 버전이 메모리에 존재 (블록의 SCN 이 쿼리의 start SCN 보다 큰 상태 가정)
    2. CR 복제본 존재 여부 해시 체인 검색 후, 해당 블록에 대한 CR 복제본 생성 (시작 점으로 쓸 수 있는 CR 복제본이 없다면 current 버전 복제)
    • REPL_AUX 리스트에서 버퍼 획득 후, REPL_MAIN 리스트와 해시 체인에 연결 (REPL_AUX 를 통해서 가용 버퍼를 빠르게 찾음)
    • 버퍼를 핀(X) 설정 후 복제
    • 버퍼를 원하는 시점(쿼리 start SCN)으로 되돌림 (언두 정보가 필요하며, 언두 블록이 연결된 해시 체인을 검색하기 위해 cache buffers chains 래치 획득)

물리적 I/O

1. 해당 블록을 위한 버퍼를 REPL_AUX 리스트에서 획득
2. 데이터 블록 주소를 표시 후 올바른 해시 체인에 연결
3. 래치 획득 후, 핀(X) 설정 후, 래치 릴리즈 후, 블록을 버퍼로 적재
※ 래치를 장기간 점유 하지 않도록 하고③, 다른 세션에서 동일 블록을 읽지 않도록 함(read by other session②)

테이블스캔

  • table scan, index fast full scan 는 부하가 큰 작업 중 하나
    • "long" 테이블 : 큰 오브젝트 스캔에 의한 버퍼 캐시 경합 문제 최소화
    • 8i/9i 런타임 엔진이 테이블의 크기가 데이터 캐시 크기의 2% 보다
      • 작다고 판단하면 : 일반적인 읽기와 유사하나 TCH 증가 없음 (이후에도 없음)
      • 크다고 판단하면 : 사용된 버퍼는 즉시 LRU 리스트의 끝으로 이동
    • 10g 의 2% 제한(_small_table_threshold) : 위와 동작 방식이 동일 하나 TCH 가 증가 함
    • 10g 의 10% 제한 : 처음 에는 TCH 가 증가하지 않으나, 이후 TCH 가 증가 함
    • 10g 의 25% 제한 : 처음 에는 TCH 가 증가하지 않고 버퍼들이 빠르게 REPL_AUX 로 이동 (REPL_MAIN 의 기존 버퍼를 보호)

요약

  • 데이터 캐시는 여러가지로 구분 (default, keep, recycle, 2k...) 되며 블록 크기는 기본 값이 좋다
  • 그래뉼은 SGA 내 다른 컴포넌트간 메모리 재할당을 동적으로 할 수 있도록 함
  • 각 버퍼 풀은 다수의 working data set 으로 분리 됨 (버퍼 헤더의 링크드 리스트, cache buffers lru chain 래치, REPL_MAIN, REPL_AUX)
  • 해시 버킷 체인 도 링크드 리스트 사용
  • 버퍼를 다른 해시 버킷으로 이동 시킬 때, 래치와 핀 사용
  • 블록이 위치할 해시 버킷은 데이터 블록 주소에 의해 결정(블록의 CR 복제본은 블록과 동일한 해시 버킷, 6개 까지)

참조 문서

이 자료는 (오라클 코어)를 참고 하여 작성했습니다.