In-Memory DB 구현 10편 - Decoder와 Checksum으로 영속화 된 데이터 불러오기

들어가며 이전 포스팅에서 구현 했던 Encoder와 SAVE 명령으로 데이터가 바이너리 파일로 저장되어 생성되긴 하지만 아직 복원 기능이 없어서 서버를 재시작하면 저장된 바이너리 파일이 있어도 해당 바이너리 파일을 역직렬화하고 로드하는 기능은 구현하지 않았기때문에 데이터는 전부 사라진다. 이번 포스팅에서는 바이너리 파일을 읽어 메모리 자료구조로 복원하는 Decoder를 구현하고 CRC32 Checksum으로 파일이 손상되지 않았는지 검증하는 메커니즘을 추가해보자. 서버 시작 시 자동으로 RDB 파일을 로드하여 영속성 사이클을 완성하는 것이 최종 목표다. 데이터 무결성 데이터 무결성은 데이터가 저장, 전송, 처리되는 전 과정에서 의도하지 않은 변경이 발생하지 않았음을 보장하는 성질이다. 데이터베이스 이론에서는 ACID의 Consistency와 연결되지만 여기서 다루는 것은 더 낮은 레벨의 문제인 파일 자체가 물리적으로 온전한가에 대한 것이다. ...

2026년 2월 25일 PM04:16 · PolarBear

In-Memory DB 구현 9편 - 데이터 영속화하기

들어가며 현재까지 구축한 데이터베이스로는 서버를 종료하거나 장애가 발생하면 저장된 데이터는 전부 사라진다. 이번 포스팅에서는 메모리의 현재 상태를 바이너리 파일로 기록하는 SAVE 명령어를 구현한다. 이 과정에서 바이너리 인코딩, 직렬화 포맷 설계, 파일 I/O의 핵심 개념을 다뤄보자. 영속성과 스냅샷 데이터베이스에서 영속성(Persistence)이란, 데이터가 프로세스의 생명주기를 넘어서 유지되는 성질이다. 인메모리 데이터베이스는 기본적으로 영속성이 없다. 메모리는 휘발성 저장장치이기 때문이다. 레디스는 두 가지 방식으로 영속성을 제공한다. RDB (Redis Database) - 특정 시점의 메모리 상태를 통째로 파일에 덤프하는 스냅샷 방식이다. 파일이 작고 로딩이 빠르지만 마지막 스냅샷 이후의 데이터는 유실될 수 있다. AOF (Append Only File) - 모든 쓰기 명령을 로그 파일에 순서대로 기록하는 저널링 방식이다. 데이터 유실이 거의 없지만 파일 크기가 크고 로딩이 느리다. 필자는 RDB 방식을 구현할 생각이다. SAVE 명령을 실행하면 메모리의 모든 데이터를 바이너리 파일에 기록한다. ...

2026년 2월 24일 AM12:12 · PolarBear

In-Memory DB 구현 8편 - TTL과 만료 전략, 최소힙으로 데이터 수명 관리하기

들어가며 지금까지 구현한 데이터베이스에서 저장된 모든 데이터는 명시적으로 삭제하지 않는 한 영구적으로 존재한다. 하지만 세션 토큰, 캐시 데이터, 인증 코드 같은 것들은 일정 시간이 지나면 자동으로 사라져야 한다. 이번 포스팅에서는 키에 만료 시간(TTL)을 설정하고 만료된 키를 자동으로 삭제하는 두 가지 전략을 구현한다. 그 과정에서 최소 힙 자료구조를 직접 구현하고 백그라운드 고루틴의 라이프사이클도 관리해보는 과정을 다뤄보겠다. TTL TTL(Time To Live)은 데이터가 유효한 시간을 의미한다. 네트워크 패킷에서는 홉 수를 제한하고 DNS 캐시에서는 레코드의 유효 기간을 정하고, CDN에서는 캐시 갱신 주기를 결정한다. 컴퓨터 시스템 전반에서 “이 데이터가 얼마나 오래 살아야 하는가"를 제어하는 수단으로 널리 쓰인다. ...

2026년 2월 19일 PM08:28 · PolarBear

In-Memory DB 구현 7편 - 이중 연결 리스트로 List 명령어 구현하기

들어가며 이번 포스팅에서는 이중 연결 리스트를 직접 구현하고, 저장소를 다중 타입으로 확장하여 LPUSH, RPUSH, LPOP, RPOP, LRANGE 명령어를 추가한다. 배열과 연결 리스트의 근본적인 차이부터 시작해서 왜 이중 연결 리스트가 이 상황에 적합한지, 그리고 포인터로 노드 간 연결을 어떻게 구현하는지를 다룬다. 배열과 연결 리스트 데이터를 순차적으로 저장하는 가장 기본적인 두 자료구조가 배열과 연결 리스트이다. 이 둘의 핵심 차이는 메모리 배치에 있다. 배열은 연속된 메모리 공간에 요소를 나란히 저장한다. 컴파일러가 시작 주소 + (인덱스 × 요소 크기)로 바로 계산할 수 있기 때문에 인덱스 접근이 O(1)이다. 하지만 앞쪽에 요소를 삽입하거나 삭제하면 나머지 요소를 전부 한 칸씩 밀거나 당겨야 하므로 O(n)이 된다. go의 슬라이스([]string)가 내부적으로 배열 기반이다. ...

2026년 2월 11일 PM07:50 · PolarBear

In-Memory DB 구현 6편 - Race Condition과 뮤텍스로 동시성 안전한 저장소 만들기

들어가며 이전 포스팅에서 go 키워드 하나로 서버를 동시성 서버로 바꿨다. 여러 클라이언트가 동시에 접속해서 PING을 보내면 모두 PONG을 정상적으로 받는다. 다수의 클라이언트가 동시에 연결하는 문제는 해결되었다. 하지만 PING은 저장소에 접근하지 않고 서버에서 특정 응답만 한다. 클라이언트들이 서버에 연결하는 순간 서로에게 공유되는 자원인 map 저장소에 동시에 SET과 GET을 수행하는 순간 현재 구조에서는 경쟁 상태가 발생한다. 이번 포스팅에서는 이 문제의 본질인 Race Condition을 이론적으로 파고들고, 세마포어부터 뮤텍스까지 동기화 기법의 계보를 살펴본 뒤 RWMutex로 저장소를 보호하는 과정을 알아보자. ...

2026년 2월 9일 PM06:46 · PolarBear

In-Memory DB 구현 5편 - 고루틴으로 동시성 서버 만들기(프로세스/스레드/컨텍스트스위칭/GMP)

들어가며 지금까지 만든 서버는 RESP 프로토콜을 파싱하고, SET/GET 명령어로 데이터를 저장하고 조회할 수 있다. 기능적으로는 꽤 그럴듯하지만 치명적인 한계가 하나 있다. 현재의 코드 구조로는 클라이언트 A가 연결 중이면, 클라이언트 B는 A의 처리가 끝날 때까지 기다려야 한다. 1편에서 살펴본 Accept Queue에 연결이 쌓이기만 할 뿐 서버가 꺼내가지 못하는 상황이다. 실제 데이터베이스에서 이런 구조는 쓸 수 없다. 이번 포스팅에서는 이 문제를 해결하기 위해 동시성의 핵심 개념을 살펴보고 go의 고루틴이 왜 경량 스레드라 불리는지 GMP 스케줄러 모델까지 파고든 뒤 코드 한 줄로 서버를 동시성 서버로 탈바꿈시키는 과정을 다루겠다. ...

2026년 2월 7일 PM05:43 · PolarBear

In-Memory DB 구현 1편 - HTTP 없이 TCP 소켓 직접 제어하기

들어가며 요즘 대부분의 개발자들이 바이브 코딩 또는 못해도 gpt나 클로드, 제미나이 같은 LLM을 사용해서 개발을 할 것이다. AI가 코드를 구현해주면 개발자의 검증이 반드시 필요한데 성능 문제, 비즈니스 및 도메인 지식, 소프트웨어 구조 설계 등이 AI 시대의 개발자로서 필요한 역량이라고 생각한다. 코드가 그냥 굴러만 가더라도, 병목현상이 벌어지는 구간이 어디인지 검증을 해봐야하고 비즈니스가 어떻게 흘러가는지와 도메인 지식을 결합하여 코드를 어떻게 처리해야 좋을지, 그리고 복합적으로 따져봤을 때 최적의 아키텍처나 구조 등을 직접 결정해야할 것이다. ...

2026년 1월 28일 PM03:29 · PolarBear