최종 발표
CUBRID 데이터베이스의 볼륨은 크게 영구적 볼륨, 일시적 볼륨, 백업 볼륨으로 분류한다.
영구적 볼륨은 영구적 데이터를 저장하지만 일시적 데이터도 저장할 수 있는 데이터 볼륨도 있다.
DWB 파일은 flush 대상 페이지를 먼저 쓰기 위한 이중 쓰기 버퍼(Double Write Buffer) 저장 파일이다.
DWB 파일은 부분 쓰기(Partial write)로 인한 I/O 에러 방지를 위하여 disk에 쓰여지는 데이터 페이지들의 복사본을 저장하는 공간이다.
double_write_buffer_size
에 의해 결정된다.Flush : Memory에 있는 data를 disk에 쓰고 동기화하는 행위
Partial write : OS의 file I/O 단위인 block과 DB 시스템의 page 단위의 크기가 같지 않기 때문에 발생 가능한 문제 (일반적으로 DB page의 크기가 더 큼)
Buffer pool : Memory에 있는 data를 page로 관리하는 data cache 영역
fsync()
를 호출해서 진행하게 되는데, OS 상의 작업은 4KB로 나누어서 작업이 발생하게 되고, 시스템 설정에 따라 cache에 미리 저장되기도 한다. 이 때, 작업을 다 완료하기 전 특정 page의 일부분만 I/O 작업이 완료되고, 특정 page의 다른 부분은 아직 완료되지 않은 경우에 system crash가 발생하면 page가 깨질 수 있다. 이런 경우 recovery가 진행되는데, 이 때, DWB에 기록된 정보를 기준으로 page를 복구하게 된다.fsync()
라고 불리는 기능을 통해 하나의 큰 덩어리 단위로 순차적으로 쓰여지기 때문이다.fsync() : 프로세스가 파일에 쓰기 작업을 요청하면 운영체제가 요청을 수행하게 되는데 시스템에서 효율을 위해 쓰기 요청을 버퍼에 기록해두었다가 처리한다. (쓰기 요청한 시간 != 실제 디스크에 쓰여지는 시간) fsync()는 kernel에 저장된 buffer를 무조건 disk에 write하게 하는 함수 (data, file의 meta data를 함께 동기화)
DB 시스템이 DWB를 사용하는 방식
Slot Hash Entry : Page Replacement에서 cache 역할을 담당 / Memory에 찾으려는 page가 없다면 DWB에서 찾는다. (key, value 값으로 저장)
DWB라 함은 메모리에 할당되어 있는 DWB block들을 의미하며 block들은 slot들로 구성되어 있다.
DWB volume은 disk에 저장되어 있는 DWB의 단일 블록으로 DB에 flush 하려는 DWB block을 먼저 disk에 flush한 block이다.
dwb_Global
storage/double_write_buffer.c: 306
/* DWB */
static DOUBLE_WRITE_BUFFER dwb_Global;
typedef struct double_write_buffer DOUBLE_WRITE_BUFFER;
struct double_write_buffer
{
unsigned int num_blocks;
// 블록의 전체 개수
unsigned int num_pages;
// 페이지의 전체 개수
unsigned int num_block_pages;
// 블록당 페이지 수
unsigned int log2_num_block_pages;
// log2(블록당 페이지 수)
pthread_mutex_t mutex;
// wait queue 변경 시 사용될 lock
DWB_WAIT_QUEUE wait_queue;
// wait queue
UINT64 volatile position_with_flags;
// 현재 페이지를 저장해야 할 블록과 슬롯의 index, 현재 블록 혹은 전체 DWB의 상태
// flags는 각 블록의 상태(시작, 종료)를 유지하고 DWB 상태를 생성하고 DWB 상태를 수정
...
}
potision_with_flags
VPID : Volume PID (page identifier)
LSA : Log Sequence Address, 각 로그마다 붙는 고유 식별자
dwb_create()
에 들어가면 먼저 구조 변경이 시작되게 되는데 dwb_starts_structure_modification()
을 통해 position_with_flags
를 체크 및 세팅한다.dwb_Global.position_with_flags
에 DWB_MODIFY_STRUCTURE
bit가 1이면 ER_FAILED를 리턴하고, 0이면 DWB_MODIFY_STRUCTURE
를 1로 세팅한다. 1로 설정하는 이유는 다른 thread들이 dwb_create()
에 접근하는 것을 막기위해서이다.position_with_flags
에서 MSB부터 정방향으로 block_no번째 bit가 세팅되어있다면 writing 중인 것이다. 확인 결과 writing 중이라면 version이 가장 낮은 block부터 오른쪽으로 순회하면서 flush가 필요한 blocks_count만큼 해당 block을 flush한다. flush 후 dwb_log_error()
로 로그를 기록한다.DWB_CREATE
flag 확인)하고 이미 만들어져있으면 12번
으로 가고 아직 만들어지지 않았다면 6번
으로 간다.dwb_create_internal()
을 통해 실질적으로 DWB를 생성하는 과정을 거친다.double_write_buffer_size
와 num_blocks
를 시스템 파라미터로 읽어와 num_pages
와 num_block_pages
를 구한다. 이 때 num_blocks
, num_pages
, num_block_pages
의 값은 모두 2의 거듭제곱이어야 한다.dwb_create_blocks()
를 통해 dwb block의 메모리 할당 및 초기화를 진행한 후 dwb_Global
구조체에 block과 데이터를 입력한다.position_with_flags
를 초기화(MSB에서 32bit, DWB_CREATE
, DWB_MODIFY_STRUCTURE
을 제외한 flag clear)하고 DWB_CREATE
bit을 1로 세팅한다.dwb_ends_structure_modification()
를 통해 DWB_MODIFY_STRUCTURE
를 0으로 세팅하고, 해당 thread의 점유 상태를 해제하고, wait_queue
에 있는 다음 thread를 깨운다. (internal 함수에서 wait_queue를 초기화했기 때문에 사실상 기능 안함)