Shared Buffer 구성요소


Shared Buffer 를 구성하는 요소

  1. 해시 테이블
  2. 해시 테이블에 딸린 해시 엘리먼트 (및 엘리먼트 키)
  3. 버퍼 상태를 관리하는 버퍼 디스크립터
  4. 실제 블록을 저장하는 버퍼 풀

1. 해시테이블


![[IMG_0388.jpg | 500]]

버퍼 파티션이란?

![[IMG_0389.jpg | 500]]

LW 락은 Light Weight 의 줄인말인데, 매우 가벼운 락을 의미하여 메모리에 액세스 할 때 사용하는 방식이다. LW 락은 테이블 내의 데이터를 보호하는 락과는 다른 개념이며, 참고로 오라클은 LW 락을 Latch 라고 표현한다.

2. 해시 엘리먼트

/*
 * HASHELEMENT is the private part of a hashtable entry.  The caller's data
 * follows the HASHELEMENT structure (on a MAXALIGN'd boundary).  The hash key
 * is expected to be at the start of the caller's hash entry data structure.
 */
typedef struct HASHELEMENT
{
    struct HASHELEMENT *link;   /* link to next entry in same bucket */
    uint32      hashvalue;      /* hash function result for this entry */
} HASHELEMENT;

엘리먼트 Key 구성 요소

- 엘리먼트 `KEY` 는 `BufferTag` 구조체와 버퍼 디스크립터 배열 인덱스로 구성된다.
- `BufferTag` 구조체는 `spcOid`, `dbOid`, `RelNumber`, `forkNum`, `blockNum` 으로 구성된다.
- `RelFileNode` 구조체는 테이블 스페이스 번호, 데이터베이스 번호, 오브젝트 번호로 구성된다.

Buffer Tag 란?

/* entry for buffer lookup hashtable */
typedef struct
{
    BufferTag   key;            /* Tag of a disk page */
    int         id;             /* Associated buffer ID */
} BufferLookupEnt;

/*
 * Buffer tag identifies which disk block the buffer contains.
 *
 * Note: the BufferTag data must be sufficient to determine where to write the
 * block, without reference to pg_class or pg_tablespace entries.  It's
 * possible that the backend flushing the buffer doesn't even believe the
 * relation is visible yet (its xact may have started before the xact that
 * created the rel).  The storage manager must be able to cope anyway.
 *
 * Note: if there's any pad bytes in the struct, InitBufferTag will have
 * to be fixed to zero them, since this struct is used as a hash key.
 */
typedef struct buftag
{
    Oid         spcOid;         /* tablespace oid */
    Oid         dbOid;          /* database oid */
    RelFileNumber relNumber;    /* relation file number */
    ForkNumber  forkNum;        /* fork number */
    BlockNumber blockNum;       /* blknum relative to begin of reln */
} BufferTag;
typedef enum ForkNumber
{
    InvalidForkNumber = -1,
    MAIN_FORKNUM = 0,
    FSM_FORKNUM,
    VISIBILITYMAP_FORKNUM,
    INIT_FORKNUM

    /*
     * NOTE: if you add a new fork, change MAX_FORKNUM and possibly
     * FORKNAMECHARS below, and update the forkNames array in
     * src/common/relpath.c
     */
} ForkNumber;

3. 버퍼 디스크립터


typedef struct BufferDesc
{
    BufferTag   tag;            /* ID of page contained in buffer */
    int         buf_id;         /* buffer's index number (from 0) */

    /* state of the tag, containing flags, refcount and usagecount */
    pg_atomic_uint32 state;

    int         wait_backend_pgprocno;  /* backend of pin-count waiter */
    int         freeNext;       /* link in freelist chain */
    LWLock      content_lock;   /* to lock access to buffer contents */
} BufferDesc;

SPIN 락과 LW


항목 Spin LW
사용부하 매우 매우 적음 매우 적음
콘텍스트 스위칭 발생하지 않음 발생할 수 있음
동작 방식 Spin 방식 큐 & 포스팅 방식
사용 용도 구조체 내 변수를 액세스 할 대 사용 구조체를 액세스 할 대 사용
사용모드 EXCLUSIVE SHARE & EXCLUSIVE

Spin

Spin 락 구현방식

- `mutex` 를 이용하는 방식
- TAS (`Test` & `Set`)를 인라인 어셈블리어로 구현하는 방식

PostgreSQL 은 두 번재 방식을 사용한다.

src/backend/storage/lmgr.s_lock.c

/*
 * s_lock(lock) - platform-independent portion of waiting for a spinlock.
 */
int
s_lock(volatile slock_t *lock, const char *file, int line, const char *func)
{
    SpinDelayStatus delayStatus;

    init_spin_delay(&delayStatus, file, line, func);

    while (TAS_SPIN(lock))
    {
        perform_spin_delay(&delayStatus);
    }

    finish_spin_delay(&delayStatus);

    return delayStatus.delays;
}

LW

LW 락 획득 절차 src/backend/storage/lmgr/lwlock.c

/*
 * LWLockAcquire - acquire a lightweight lock in the specified mode
 *
 * If the lock is not available, sleep until it is.  Returns true if the lock
 * was available immediately, false if we had to sleep.
 *
 * Side effect: cancel/die interrupts are held off until lock release.
 */
LWLockAcquire(LWLock *lock, LWLockMode mode)
LOOP
	LWLockAttemptLock() 함수를 호출해서 LW  획득을 시도한다.
	락을 획득하면 LOOP 탈출한다.

	만일 락을 획득하지 못하면 대기 큐에 등록한다.
	   LwLockAttemptLock() 함수를 호출한다.
	만일 락을 획득하면 대기 큐에 등록된 항목을 삭제한  LOOP 탈출한다.

	만일 락을 획득하지 ㅁ소하면 대기 상태를 시작한다.
	(다른 프로세스가 깨워줄 때까지 대기 상태가 유지 된다.)
END

정리


![[IMG_0389.jpg | 500]]

출처


PostgreSQL 9.6 성능 이야기(https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=107542125)

>> Home