h1.1. 메모리 관리

h3.A. 그레뉼

  • ASMM 기능은 SGA 전체 크기를 지정하면 스스로 최적 메모리 컴퍼넌트 계산
    ex) Disk io 감소를 위해 데이터 캐시 증가, 파싱 시간 감소 위해 코드 캐시 증가
  • 데이터 캐시(db_chche_size) 와 코드 캐시의 크기(shared_pool_size) 간 효율적 메모리 이동을 위해 그레뉼(granule) 이라는 메모리 Chunk 사용.
  • v$sgainfo 의 name='Granule Size' 로 조회 가능

SQL> select BYTES/1024/1024 from v$sgainfo where name='Granule Size';

BYTES/1024/
-----------
          4

  • 각 buffer component 별로 그레뉼 사이즈를 지원

SQL> select 
COMPONENT, current_size/1024/1024, granule_size/1024/1024
from v$sga_dynamic_components
where component like '%buffer cache';

COMPONENT                                                        CURRENT_SIZ GRANULE_SIZ
---------------------------------------------------------------- ----------- -----------
DEFAULT buffer cache                                                      20           4
KEEP buffer cache                                                          0           4
RECYCLE buffer cache                                                       0           4
DEFAULT 2K buffer cache                                                    0           4
DEFAULT 4K buffer cache                                                    0           4
DEFAULT 8K buffer cache                                                    0           4
DEFAULT 16K buffer cache                                                   0           4
DEFAULT 32K buffer cache                                                   0           4

  • x$ 뷰들을 조회하여 SGA Component 별 각 그레뉼 정보 추출 가능

SQL> select
ge.grantype, ct.component, ge.granprev, ge.grannum, ge.grannext
from x$ksmge ge, x$kmgsct ct
where
ge.grantype != 6
and ct.grantype = ge.grantype
order by  ct.component, ge.grantype;

GRANTYPE    COMPONENT              GRANPREV    GRANNUM     GRANNEXT   
----------- ---------------------- ----------- ----------- -----------
          7 DEFAULT buffer cache           209         210         211
          7 DEFAULT buffer cache           210         211         208
          7 DEFAULT buffer cache             0         212         209
          7 DEFAULT buffer cache           212         209         210
          7 DEFAULT buffer cache           211         208           0
          3 java pool                        0         214           0
          2 large pool                       0         215           0
          1 shared pool                    246         245         244
          1 shared pool                    245         244         243
          1 shared pool                    244         243         242
          1 shared pool                    243         242         241
          1 shared pool                    242         241         240
          1 shared pool                    241         240         239
          .
          .
          .

h3.B. 그레뉼과 버퍼

  • 그레뉼은 버퍼/버퍼 헤더/내부 관리용의 세 가지 목적으로 사용된다.

h1.2. Multiple 데이터캐시

  • 오라클은 8개의 데이터 캐시 종류를 제공한다.
  • 8개의 캐시는 아래 파라메터로 조정 가능하다.

1. db_cache_size - 기본 캐시이며, db 기본 블록 크기 설정을 따른다.
2. db_keep_cache_size - buffer_pool keep storage 옵션을 사용한 오브젝트를 위해 사용된다. 캐시 내에 오브젝트를 상주시키는 목적이며, 파라메터로 지정한 사이즈는 오브젝트 사이즈보다 커야 한다.
3. db_recycle_cache_size - buffer_pool recycle 옵션을 사용한 오브젝트를 위해 사용된다. 혹은 keep cache 를 설정 한 오브젝트의 읽기 일관성을 위한 복제 블록을 생성하는 공간으로도 사용한다.
4. db_2k_cacha_size - 기본 블록 크기가 2k 가 아닌 db 의 2k 블록 테이블스페이스 생성 시 사용한다.
5. db_4k_cacha_size - 기본 블록 크기가 4k 가 아닌 db 의 4k 블록 테이블스페이스 생성 시 사용한다.
6. db_8k_cacha_size - 기본 블록 크기가 8k 가 아닌 db 의 8k 블록 테이블스페이스 생성 시 사용한다.
7. db_16k_cacha_size - 기본 블록 크기가 16k 가 아닌 db 의 16k 블록 테이블스페이스 생성 시 사용한다.
8. db_32k_cacha_size - 기본 블록 크기가 32k 가 아닌 db 의 32k 블록 테이블스페이스 생성 시 사용한다.

  • 다양한 블록 크기의 버퍼 캐시를 제공하는 목적은
    1. 특정 세그먼트를 위한 (ex IOT, LOB) 테이블스페이스를 위해
    2. 서로 다른 블록 크기를 가지는 데이터베이스 간 테이블스페이스 이관을 위해

h3.A. 그레뉼과 버퍼 풀

  • 하나의 그레뉼은 하나의 버퍼 풀에 할당된다.

  • 그레뉼은 배열과 Linked list 의 혼합 형태로 관리 된다.
    --> 각각의 그레뉼은 이전-다음에 연결 된 다른 그레뉼 정보를 가지고 있다.

h3.B. 버퍼 풀

  • 오라클의 메모리 컴퍼넌트를 관리 하는 매커니즘은 일반적으로 LRU 알고리즘으로 알려져 있다.
  • 필자의 생각으로는 Working data set 을 이용하여 설명하는 것이 더 적절하다.

  • 각 그레뉼의 1/n 만큼이 Working data set 에 나뉘어서 할당 됨
  • 하나의 dbwr 은 여러 개의 Working data set 을 관리 함
  • 이러한 구조를 가지는 목적은
    1. 다수의 dbwr 간의 균등한 기록 작업 분배
    2. 버퍼 풀로 데이터 블록을 적재하려는 프로세스 간의 경쟁을 최소화
  • 각 그레뉼의 Working data set 의 갯수를 결정하는 값은 DB 버전마다 다름

h1.3. Working data set

  • Working data set(WDS) 을 설명하는 구조는 x$kcbwds 뷰를 통해 확인 가능하다.

SQL> select 
ADDR, SET_ID, CNUM_SET, SET_LATCH, NXT_REPL, PRV_REPL, NXT_REPLAX, PRV_REPLAX, CNUM_REPL, ANUM_REPL
from x$kcbwds;

ADDR             SET_ID      CNUM_SET    SET_LATCH        NXT_REPL         PRV_REPL         NXT_REPLAX       PRV_REPLAX       CNUM_REPL   ANUM_REPL  
---------------- ----------- ----------- ---------------- ---------------- ---------------- ---------------- ---------------- ----------- -----------
000000009EA7B5B0           1           0 000000009EA7B908 000000009EA7BA98 000000009EA7BA98 000000009EA7BAA8 000000009EA7BAA8           0           0
000000009EA96B48           2           0 000000009EA96EA0 000000009EA97030 000000009EA97030 000000009EA97040 000000009EA97040           0           0
000000009EAB20E0           3        2455 000000009EAB2438 0000000094FF60C0 00000000957F8330 0000000094BE76D0 0000000095BDEF70        2455         558
000000009EACD678           4           0 000000009EACD9D0 000000009EACDB60 000000009EACDB60 000000009EACDB70 000000009EACDB70           0           0
000000009EAE8C10           5           0 000000009EAE8F68 000000009EAE90F8 000000009EAE90F8 000000009EAE9108 000000009EAE9108           0           0
000000009EB041A8           6           0 000000009EB04500 000000009EB04690 000000009EB04690 000000009EB046A0 000000009EB046A0           0           0
000000009EB1F740           7           0 000000009EB1FA98 000000009EB1FC28 000000009EB1FC28 000000009EB1FC38 000000009EB1FC38           0           0
000000009EB3ACD8           8           0 000000009EB3B030 000000009EB3B1C0 000000009EB3B1C0 000000009EB3B1D0 000000009EB3B1D0           0           0

8 rows selected.

  • x$kcbwds 의 주요 컬럼은 아래와 같다

cnum_set - WDS 안의 버퍼 수
set_latch - WDS 를 보호하는 cache buffers lru chain 래치주소
nxt_repl - 링크드리스트의 양쪽 끝 주소
prv_repl - 링크드리스트의 양쪽 끝 주소
nxt_replax - 링크드리스트의 양쪽 끝 주소
prv_replax - 링크드리스트의 양쪽 끝 주소
cnum_repl - 링크드리스트의 총 버퍼 수
anum_repl - aux 링크드리스트의 총 버퍼 수

  • x$kcbwds 의 정보와 buffer cache 안의 해시 체인과의 관계는 위와 같다

h3.A. LRU/TCH 알고리즘

  • 오라클은 새로운 블럭을 메모리에 적재할 때 기본 LRU 알고리즘을 사용한다
  • 대량 트렌젝션의 환경에서 이동 경합을 줄이기 위해 TCH(Touch count),TIM(Timestamp) 값을 버퍼 헤더에 관리한다
  • 마지막 변경으로부터 3초가 지난 경우에 한해서 Touch count 를 1씩 증가시킨다

h3.B LRU/TCH 동작 방식

h5.시나리오(a) 는 WDS 로 새로운 블럭 적재
h5.시나리오(b) 는 (a) 이후 WDS 로 두 번째 블록 적재하는 단계

h5.(a) Case
1.새로운 블럭 적재 위해 프리버퍼 탐색
2. 교체 리스트(repl_main)의 LRU 끝부터 검색
3. LRU 끝의 tch 확인 - 1일 경우(자주 접근하지 않음)
4. pinned/dirty 상태인지 확인
5. 버퍼에 베타적 pin 설정
6. 블록 읽기
7. 버퍼 헤더 변경
8. LRU List 끝에서 midpoint 에 연결
9. 버퍼 unpin

  • 기존 caceh buffers chain 에 분리 되어 새 체인에 연결하므로
    두 개의 cbc 래치를 획득해야 함
  • 그 이후 하나의 큐에서는 버퍼를 분리, 다른 하나에 버퍼를 연결

h5.(b) Case - LRU 끝 블록의 TCH 가 1이 아닐 때
1. 해당 버퍼를 LRU 끝에서 분리 후 MRU 끝으로 이동
2. TCH 값을 반으로 줄임
3. 다음 번 버퍼의 TCH 값을 확인

  • 자주 사용하는 블럭이 mru 끝으로 이동하는 시점은 lru 끝에 위치함으로써 캐시에서 밀려나가는 시점
  • 사용 할 때마다 이동하지 않는다
  • 다수 버퍼가 MRU 끝으로 이동하면 기존 Hot 영역의 버퍼가 Cold 영역으로 밀림
  • Midpoint 를 넘어서면 TCH 는 1로 감소
  • LRU 끝을 넘어 밀리기 전까지 재 방문되지 않으면 메모리에서 밀려남

h1.4. REPL_AUX

  • REPL_AUX(보조 교체 리스트) - 즉시 재 사용이 가능한 버퍼들의 리스트
  • 세션은 새로운 블럭을 읽을 때 repl_main 의 LRU 가 아니라 repl_aux 의 LRU 로 검색
  • 이로 인해 지연시간 Pinned, Dirty 버퍼 처리 등을 감소

h3.A. repl_aux 의 동작 방식

1. 인스턴스 시작 시 모든 버퍼는 repl_aux 에 연결, repl_main 에는 없음

  • flush buffer 로 동일한 상황 재현 가능
  • repl_aux 에 연결 전에 각 버퍼의 상태를 free 로 변경(state 0)

2. 새 블럭 읽어들일 때 repl_aux 의 lru 끝에서부터 버퍼를 찾은 후에

3. 해당 버퍼를 repl_aux 에서 분리한 후 repl_main 에 연결

  • 버퍼는 시간이 지날수록 repl_aux -> repl_main 으로 이동

4. repl_main 에서 repl_aux 로 재 이동시키는 매커니즘 존재

  • dbwr 의 활동과 관계 없이(Dirty buffer 를 Disk 에 기록) Main 으로 이동
  • repl_aux 의 목표 크기는 Warking data set 의 25% 가 기본
  • 시간이 지날 수록 anum_repl(repl_aux 의 수)의 수치는 감소하지만 일정 시간 지나면 다시 목표 크기까지 증가
  • _db_percent_hot_default 는 기본 버퍼 풀 내의 hot 영역의 비율을 의미하며 기본은 50 --> hot:cold 는 50:50
  • repl_aux 의 목표 크기는 이 파라메터의 1/2 이다
  • 디스크 읽기 작업 이후에 repl_main 의 LEU 끝에 위치한 버퍼를 repl_aux 로 이동시키는 매커니즘이 추가로 존재한다
    Dirty, Pinned, TCH > 1 인 버퍼는 이동 대상이 아니며, 아마 cr 블록들이 이동 대상 후보일 듯

h3.B. repl_aux 리스트를 목표 크기로 유지하는 이유는?

  • 대부분의 시스템은 버퍼 블록에 대한 복제 작업을 많이 수행한다
  • 복제를 위한 버퍼 탐색이 새 블록 찾는 버퍼 탐색보다 많을 수 있다
  • 대량의 작업 시에는 링크리스트 검색 시간을 최소화 해야 한다

h1.5. 데이터 검색

  • 오라클은 많은 수의 버킷을 가진 해시 테이블을 사용
  • 각 버킷을 그루핑 한 후 cache buffer chains 래치로 보호한다
  • 해시 버킷의 수는 한 개의 래치 당 32개로 고정(10g 이후)
  • 블록 위치한 해시 버킷 식별 위해 절대파일번호+블럭번호로 해시 적용

h3.A. 버퍼 Pinning

일반적으로 버퍼 관련 작업은 아래와 같다
  • 디스크에서 블록 읽어 관련 버퍼를 해시 버킷에 위치
  • 버퍼 재 사용 시 해당 버퍼를 기존 해시버킷에서 제거
  • current 버전의 버퍼 엑세스
  • 버퍼 변경
  • 버퍼 복제본 생성
위 모든 작업은 링크드 리스트를 읽어야 하며 cache buffer chains 래치 획득이 필요하다
래치 획득 후 위 작업을 수행할 때 경합이 없도록 아래와 같은 절차로 처리 된다

1. 블록주소 이용해 해시버킷 계산
2. cache buffers chains 래치 획득
3. 해시버킷에서 필요 버퍼 검색, Pin 설정
4. cache buffers chains 래치 릴리즈
5. 버퍼 관련 작업 수행
6. cache buffers chains 획득
7. 버퍼 Unpin
8. cache buffers chains 래치 릴리즈

  • 버퍼 작업 중에는 해시버킷 래치 릴리즈
  • 버퍼 헤더에 Pin 하므로써 버퍼 보호 가능
버퍼 핀은
  • 공유(s) 모드나 독점(x) 모드로 설정 가능함
  • 핀 되지 않았거나 s 모드로 핀 되어 있다면 사용자 리스트에 핀 등록
  • 이미 x 모드로 핀 되어 있다면 대기자 리스트에 핀 등록
    버퍼 사용이 가능 할 때까지 buffer busy waits 대기
    다른 세션이 해당 블록을 디스크로부터 읽는 중이라면 read by other session 대기
  • 핀 구조는 핀 소유자/대기자 세션의 주소를 가지므로
    홀더(블로킹) 세션이 핀을 릴리즈할 때 대기 리스트의 첫 세션을 포스트 한다
  • 일반적으로 buffer busy wait 의 타임아웃은 1초(_buffer_busy_wait_timeout) 이다
  • 대기 세션이 1초 안에 포스트되지 못하면 깨어나서 데드락이 발생되었다고 추정 후 buffer deadlock 대기이벤트 발생
    데드락 해결을 위하 소유한 모든 핀을 릴리즈 한 후 필요 핀 재 획득 시도
  • 헤더에 핀 설정 후 작업이 끝나더라도 조만간 재 방문 할 것으로 예상하면 콜이 완료되기 전까지 핀 상태 유지
    buffer is pinned count 성능통계에 반영

h3.B. 논리적 I/O

버퍼캐시에 존재하는 블록 내용 확인하는 간단한 예(single-gets)

1. cache bufer hash chain 을 획득
2. 체인을 따라가며 블록 검색
3. 블록 조사 중 래치 릴리즈 가능

  • "consistent gets - examination" 성능통계에 기록
  • 이 방법(single-get)으로 가능한 종류는
    인덱스(루트/브렌치), 유니크인덱스 리프, 유니크인덱스 스캔에 의핸 테이블/언두 블록
single-get 방법을 사용할 수 없다면 two-latch get 방식 사용

1. 래치획득
2. 체인검색
3. 버퍼헤더 핀(s) 설정
4. 래치 릴리즈
5. 블록 내용 확인
6. 래치 획득
7. 핀 릴리즈
8. 래치 릴리즈

h3.C. 데이터 변경

h5.1) pk 에 의한 Update

  • 읽는 절차와 동일하지만 블록 헤더 핀을 베타 모드로 설정
    exclusive, shared 모드로 다른 세션이 접근하면 대기

*언두세그먼트 헤더/언두블록 접근 절차 추가 필요 - 두 개의 cache buffers chains 레치 획득, 두 개 체인 검색 필요

h5.2) 테이블스캔에 의한 업데이트

  • 블록을 변경하지 않음
    1. 새 블록에 기존 블록 복사
    2. 기존 블록을 cr 블록으로 변경
    3. 새로운 블록을 변경
    switch current to new buffer 성능통계에 기록
  • 만일 동일한 블록에 대한 변경을 수 차례 수행하면 이로 인한 cr 블록 생성량은 기본값을 넘어서게 됨(_db_block_max_cr_dba=6)
  • 이로인해 캐시의 효율성이 떨어지는것을 방지하고 체인 길이 짧게 하기 위해 cr 블럭을 오래 된 순서로 repl_aux 에 이동시킴

h3.D. 해시 체인 로딩

버퍼를 repl_aux 리스트에서 분리해서 해시체인에 연결하는 동작 방식을 살펴본다
  • 서로 다른 두 래치 획득 필요
    cache buffers lru chain - Working data set 에 관한 래치, level 2 높음
    cache buffers chains - 해시 버켓에 관한 래치, level 2 낮음
  • 낮은 레벨의 래치를 획득한 상태면 높은 레벨의 레치를 요청할 수 없는 규칙이 있다
상황을 가정하면
  • Session A 가 cache buffers chains 를 획득한 상태고 또 다른 버퍼를 체인에 연결하려고
    repl_aux 버퍼를 repl_main 의 Midpoint 로 이동시키기 위해
    cache buffers lru chains 래치를 획득하려 해도 이미 더 낮은 레벨의 래치를 가지고 있으므로
    Wait 모드로 요청할 수 없다.
  • 이 경우 cache buffers chains 를 릴리즈하고 cache buffers lru chains 를 획득 후 다시 cache buffers chains 획득 필요

h3.E. CR 복제

새로운 버퍼를 해시 체인에 연결하는 과정 중 CR 복제본 생성 방식을 살펴보자
  • Current 버전 블록이 메모리에 존재하고
  • Current 버전의 SCN 이 쿼리 SCN 보다 크다면(쿼리가 더 먼저 시작)

1. 해당 블록의 cr 복제본을 생성

  • 생성 전에 CR 복제본이 이미 있는지 해시 체인 검색

2. 체인 검색 후에도 CR 복제본을 찾지 못할 경우는 복제 작업 수행

  • 시작점으로 사용할 수 있는 cr 복제본이 있으면 그걸 복제하고
  • 그렇지 않으면 Current 버전의 블록을 복제 함
  • 복제를위해 repl_aux 에서 버퍼 획득 후 repl_main 체인에 연결
  • x 모드로 핀을 설정함
  • 복제 수행 및 완료

4.모든 래치를 릴리즈하고

5. 복제 블록을 원하는 시점(쿼리 시작 scn) 까지 되돌리는 작업 수행

  • 언두 레코드 적용 필요
  • 해당 언두 블럭이 연결 된 해시 체인을 검색하기 위해 cbc 래치 획득
  • 언두가 디스크에 있으면 메모리 적재 작업 추가 수행 필요

h3.F. 물리적 I/O

원하는 블록이 메모리 안에 존재하지 않으면 디스크에 읽어 적재 하는 과정

1. 적재를 위한 버퍼를 repl_aux 에서 획득 후
2. 데이터 블록 주소를 표시하고
3. 해시체인에 연결한 후
4. X 모드 핀 설정
5. 래치 릴리즈 하고 버퍼로 디스크 블록 적재

h3.G. 테이블 스캔

h5 테이블스캔, 인덱스 풀 스캔으로 인해 다수의 버퍼가 밀려날 수 있으므로 아래와 같은 처리 매커니즘으로 보완

h5 10g 에서는 테이블 크기에 따라 설정값을 참고하여 캐싱이 동작한다

  • 2% 제한
    _small_table_threshold 로 shot 테이블 설정
    일반적 읽기 작업과 유사하다 - 버퍼의 TCH 가 증가하고, repl_main 의 LRU 알고리즘을 탄다
  • 10% 테이블 제한
    버퍼 블록 적재 시점에는 TCH 가 증가하지 않아 LRU 끝으로 밀려나고
    repl_aux 의 MRU 끝으로 빠르게 이동한다
    적재 이후 반복적 스캔이 발생하면 TCH 가 증가함
  • 25% 제한
    TCH 는 증가 안하고 버퍼는 매우 빠르게 repl_aux 로 이동
    멀티블록 리드 시 몇 개의 블록을 repl_aux -> repl_main 으로 이동 후
    다음 멀티블록 읽기 시 이전 멀티블록은 다시 repl_aux 로 이동 반복
    이로 인해 해당 테이블의 repl_main 리스트 내부 버퍼는 크게 증가하지 않음
    대량 테이블 스캔으로부터 버퍼 보호 가능