4 분 소요

[PostgreSQL]PostgreSQL Shared Buffer 2

Buffer Descripter

버퍼 디스크립터는 메모리에서 사용하는 데이터 페이지에 대한 상태, 참조 카운트, 락 정보등을 저장하고 관리함.

name description
버퍼 아이디 (Buffer ID) 실제 버퍼가 저장된 버퍼 풀 배열에 해당하는 위치를 저장
상태 플래그 (Status Flag) 페이지가 사용되고 있는 상태를 나타내는 플래그.
참조 카운트 (Reference count) 메모리에서 페이지가 참조되고 있는 프로세스의 수. 이 수치로 페이지 교체 대상을 결정하는 데 사용됨.

상태 플래그의 3종류는

  • Empty : 버퍼 풀 슬롯에 페이지가 존재하지 않고 비어있는 상태
  • Pinned : 버퍼 풀 슬롯이 페이지가 존재하고 PostgreSQL 프로세스가 페이지를 사용 중인 상태
  • Unpinned : 버퍼 풀 슬롯에 페이지가 존재하고 PostgreSQL 프로세스가 사용하지 않는 상태

Buffer Pool

버퍼 디스크립터는 버퍼 풀에 관련된 상태 정보와 메타 데이터를 저장하고, 버퍼 풀을 관리하기 위한 목적을 가지고 있다면,

버퍼풀은 실제 데이터 페이지가 저장되는 장소며 디스크의 IO를 줄여 성능을 향상시키는 것이 버퍼풀의 가장 큰 목적

img (1)

다음과 같이 버퍼 디스크립터와 버퍼 풀은 1:1 관계로 매핑되어 있는데, 버퍼 풀은 테이블 및 인덱스와 같은 데이터 페이지를 저장하기 위한 배열로 구성되어 있으며, 이러한 배열의 인덱스를 buffer_id라고 함.

버퍼 풀에 존재하는 버퍼의 크기는 데이터 페이지의 크기(8KB)와 동일함. 그래서 하나의 버퍼 풀 슬롯에 하나의 데이터 페이지 전체를 저장할 수 있음.

이 버퍼풀에는 주로 테이블, 인덱스 등의 데이터 파일 페이지가 저장되며 FSM, VM에 대한 정보도 같이 저장됨.

한 버퍼는 헤더와 페이지, 2가지로 구분할 수 있으며 헤더에는 버퍼의 상태정보 및 페이지에 대한 메타 정보들이 있음.

(ex. 상태정보로는 dirty bit, usage count, refcount, 메타정보로는 relation id, fork block number, transaction id)

페이지 상태정보

  • Dirty Bit : 페이지가 변경된 상태를 비트로 표시
  • Usage Count : 버퍼 풀에 데이터 페이지가 로드 된 이후의 사용 횟수, 페이지가 읽힐 때마다 값이 하나씩 증가함
    • 사용 빈도가 적을수록 페이지 교체 대상의 가능성이 높아짐..
  • Reference Count : 객체가 프로세스에 의해 참조된 횟수.

페이지 메타 정보

  • Relation ID : 페이지가 속한 릴레이션이 테이블, 인덱스, 시퀀스, 뷰 등의 객체를 고유하게 식별할 수 있는 숫자
  • Fork Block Number : 데이터 페이지 번호, 번호는 0부터 시작하며 데이터 페이지의 특정 위치를 나타냄
  • Transaction ID : 마지막 트랜잭션 ID, 이 페이지가 변경된 시점을 추적 가능

Shared Buffer에서 데이터 읽기

backend프로세스가 데이터를 요청했을 때 Shared Buffer에서 데이터 페이지를 읽는 과정을 3가지 형태로 살펴보자

1. 버퍼 풀에서 요청한 페이지 읽기

Backend 프로세스에서 데이터를 요청하면 메모리에서 데이터를 찾은 후 데이터를 반환함.

(데이터 요청 → 메모리에 데이터 존재 → 페이지 읽기)

  1. Backend 프로세스에서 데이터를 요청하기 위해 버퍼 태그를 생성 (버퍼 태그를 해시함수 거쳐서 버킷 슬롯을 계산함)
  2. 해당 버퍼 파티션에 공유모드의 BufMappingLock을 획득함.
  3. 해시 테이블에서 버킷 슬롯을 검색한 후 엘리먼트를 통해 버퍼 디스크립터 배열 인덱스를 찾음.
  4. 해당 디스크립터의 배열에 PIN을 고정함. (Refcount, usage_count숫자를 1씩 증가)
  5. BufMappingLock을 해제함.
  6. 버퍼 디스크립터 배열에 content_lock을 획득함.
  7. 버퍼 풀에서 페이지를 읽음
  8. 버퍼 디스크립터 배열에 content_lockPIN을 해제함. (Refcount값을 1씩 감소)

Backend프로세스가 버퍼풀에서 페이지를 읽기 전 먼저 content_lock을 공유모드로 획득함 (이와 동시에 여러 프로세스에서 버퍼 풀 페이지의 데이터를 읽을 수 있음.)

2. 버퍼풀에서 요청한 페이지가 존재하지 않는 경우

Backend프로세스에서 요청한 페이지가 메모리에 존재하지 않아 빈 페이지를 할당 받은 후 페이지를 읽고 데이터를 반환함

( 데이터 요청 → 메모리에 해당 데이터 없음 → 빈 페이지에 페이지 로딩 → 페이지 읽기)

  1. Backend프로세스에서 데이터 요청을 위해 버퍼 태그를 생성
    1. 버퍼 태그로 해시함수 거쳐서 나온 해시값으로 버킷슬롯을 계산
    2. 먼저 해당 버퍼 파티션에 공유모드로 BufMappingLock을 획득함
    3. 버킷에 배핑되는 해시값이 존재하지 않는 걸 확인하고 BufMappingLock을 해제
  2. Free List를 탐색해 빈 버퍼 디스크립터 중 첫번째 배열에 해당하는 버퍼에 Pin을 고정 (Refcount 1증가)
  3. 해당 버퍼 파티션에 배타 잠금 모드로 BufMappingLock 을 획득
  4. 해시 엘리먼트 풀에서 새로운 엘리먼트를 할당 받고 새 데이터 항목을 생성 후 저장.
  5. 해당 버퍼 디스크립터 배열에 락을 설정 후 플래그 비트를 설정
    1. (다른 프로세스 접근 방지위해 bm_io_in_progress 플래그 비트를 1로 설정)
    2. content_lock락 설정 → bm_io_in_progress 플래그 비트를 0으로 설정 → content_lock 락 해제
  6. 디스크에서 원하는 페이지를 버퍼 풀 슬롯에 로드함
  7. 해당 버퍼 디스크립터 배열에 락 해제 후 플래그 비트를 설정.
    1. (content_lock 락 설정 → bm_io_in_progress 플래그 비트를 0으로 설정 → content_lock 락 해제)
  8. 버퍼 파티션에 BufMappingLock해제
  9. 버퍼 풀 배열에서 데이터를 읽은 후 PIN해제 (refcount 1 감소)

3. 버퍼 풀에서 데이터 페이지 교체 후 읽기

Backend프로세스가 데이터를 읽기 위해 버퍼 풀에 접근했으나, 버퍼 풀에 원하는 데이터가 없는경우, 페이지를 디스크로부터 읽어 비어 있는 버퍼에 로드해야함. 하지만 버퍼에는 비어 있는 슬롯이 존재하지 않기 때문에 페이지 교체 대상을 선택한 후 디스크에 기록하고 요청한 데이터 페이지를 로드함.

(데이터 요청 → 요청한 페이지가 존재x → 할당된 빈 페이지도 없음 → Clock Sweep알고리즘 수행 → 빈페이지에 페이지 로딩 → 페이지 읽기)

이렇게 기존 사용하던 데이터 페이지를 교체할때, 교체 대상을 Clock Sweep 알고리즘을 통해 선정함.

(Clock Sweep 알고리즘은 대충 메모리 상주중인 버퍼중에 자주 사용되지 않는 페이지를 효율적으로 선정하는 알고리즘)

  1. Backend 프로세스가 요청한 데이터가 해시 엘리먼트에 존재 x (해시 엘리먼트 검색과정은 위와 동일)
  2. Clock Sweep 알고리즘으로 빈 페이지로 대체할 페이지 즉, Victim페이지를 선정함.
    1. Victim으로 선정된 페이지에 PIN 설정
  3. Clock Sweep 알고리즘에 의해 선정된 페이지가 Dirty 페이지면 페이지가 교체되기 전 디스크에 기록함
    1. content_lock 및 io_in_progress잠금 획득 → io_in_progress비트 1설정 → vimtim페이지를 디스크에 플러시 → io_in_progress 0설정, 유효한 비트는 1로 설정 → content_lock 및 io_in_progress 락 해제
  4. 배타적 모드로 교체대상 항목이 존재하는 버퍼 파티션에 BufMappingLock을 획득
  5. 교체 대상이 존재하는 버퍼 파티션에 배타 모드로 BufMappingLock을 획득
  6. 배타 모드로 BufMappingLock을 추가 획득 후 해시 엘리먼트를 할당 받고 새 데이터 항목을 생성
  7. 기존 연결되어 있던 해시 엘리먼트를 삭제하고 BufMappingLock해제
  8. 해당 버퍼 디스크립터 배열에 락을 설정 후 플래그 비트 설정
    1. content_lock 설정 → bm_io_in_progress 0설정 → content_lock 해제
  9. 디스크에서 원하는 페이지를 버퍼 풀 슬롯에 로드함
  10. 해당 버퍼 디스크립터 배열에 락 해제 후 플래그 비트를 설정
    1. content_lock 설정 → bm_io_in_progress 1 설정 → content_lock 해제
  11. BufMappingLock을 모두 해제
  12. 버퍼 풀 배열에서 데이터를 읽음.

댓글남기기