제목
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
데이터베이스 개발
대상자
- 소프트웨어 개발자, 특히 데이터베이스 구현에 관심 있는 개발자
- 초보자 ~ 중급자 수준의 실무 적용에 초점
핵심 요약
- 파일 포맷 설계는 최소한으로 시작해야 한다. 복잡한 헤더는 장기적인 유지보수와 호환성 문제를 유발할 수 있다.
- 바이트 순서(endianness)를 명시적으로 처리해야 한다.
binary.LittleEndian.PutUint32
와 같은 함수를 사용하여 플랫폼 간 호환성을 보장한다. - 에러 처리를 먼저 구현해야 한다. 파일 손상, 디스크 공간 부족, 시스템 크래시 등 예외 상황에 대비해야 한다.
- 데이터의 지속성(durability)을 위해
fsync()
를 사용해야 한다. OS 버퍼에 남아 있는 데이터를 디스크에 강제로 쓰는 것이 필수적이다. - 기능 추가는 기본 기능이 완성된 후에야 한다. 복잡한 기능은 안정성과 성능에 부정적 영향을 줄 수 있다.
섹션별 세부 요약
1. 파일 포맷 설계의 단순화
- 최초의 복잡한 헤더(15개 필드)는 유지보수와 호환성 문제를 유발했다.
- 최종적으로 16바이트의 최소한의 헤더로 설계하여, 후속 확장성을 보장했다.
"KVDB2024"
파일 싸이너처, 첫 번째 프리리스트 페이지 포인터, 첫 번째 데이터 페이지 포인터를 포함하는 구조를 채택했다.
2. 바이트 순서(endianness) 문제
- 32비트 정수를 직렬화할 때 플랫폼별 바이트 순서를 명시하지 않으면, ARM과 x86 기기에서 값이 다르게 해석된다.
(uint32)(unsafe.Pointer(&buf[0])) = pageNum
은 플랫폼에 따라 오류를 발생시킬 수 있다.binary.LittleEndian.PutUint32(buf, pageNum)
를 사용하여 일관된 바이트 순서를 보장해야 한다.
3. 에러 처리의 중요성
- 파일 손상, 디스크 공간 부족, 시스템 크래시 등 예외 상황을 대비하지 않으면, 데이터 손실이나 불일치가 발생할 수 있다.
openExistingDatabase
함수에서 헤더 검증, 파일 싸이너처 검증, 필드 검증 등 체계적인 에러 처리를 구현해야 한다.- 에러 처리를 먼저 구현하고, 이후 기능 구현에 집중하는 것이 안정적인 데이터베이스 설계의 핵심이다.
4. 지속성(durability) 보장: `fsync()` 사용
file.Write()
호출은 OS 버퍼에 데이터를 저장할 뿐, 즉시 디스크에 쓰지 않는다.file.Sync()
를 호출하여 버퍼에 있는 데이터를 디스크에 강제로 쓰는 것이 필수적이다.writeHeader
함수에서file.Sync()
를 사용하여 데이터의 지속성을 보장한다.
5. 기능 추가의 타이밍
- 초기에는 인덱싱, 압축, 트랜잭션 등 복잡한 기능을 추가하려는 유혹이 있었지만, 최소한의 기능으로부터 시작하는 것이 더 효율적이다.
- 고정 크기 페이지, 단순한 append-only 모델, 최소한의 헤더 구조로 설계했음에도, 성능과 안정성이 뛰어났다.
- 복잡한 기능은 기본 기능이 완전히 동작하는 후에 추가해야 한다.
결론
- 데이터베이스 개발은 알고리즘보다 실무적 문제에 집중해야 한다. 파일 손상, 시스템 크래시, 데이터 지속성 문제를 해결하는 것이 핵심이다.
- 최소한의 설계, 명시적인 바이트 순서 처리, 체계적인 에러 처리, fsync() 사용, 기능 추가의 타이밍이 성공적인 데이터베이스 구현의 핵심이다.
- "기능이 작동하는 간단한 데이터베이스"가 "복잡하지만 작동하지 않는 데이터베이스"보다 훨씬 유리하다.