494. 멤테이블 (MemTable)과 SSTable 플러시
⚠️ 이 문서는 LSM-Tree(493번 문서) 엔진이 미친듯한 쓰기 속도를 내기 위해 데이터를 어떻게 메모리에서 모으고(MemTable), 어떤 방식으로 하드디스크에 영구적으로 얼려버리는지(SSTable Flush) 그 물리적인 데이터 이동 과정을 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: 하드디스크의 랜덤 I/O를 피하기 위해 도입된, '메모리 버퍼링 후 순차 디스크 쓰기'라는 이단적인 데이터 저장 아키텍처다.
- MemTable (메모리): 데이터가 처음 들어와서 쌓이는 RAM 공간이다. 여기서 데이터는 Key 값을 기준으로 예쁘게 오름차순 정렬(Red-Black Tree 등)된다.
- SSTable (디스크): MemTable이 꽉 차면 그대로 디스크로 내려써진(Flush) 불변(Immutable)의 파일이다. 이미 정렬되어 있으므로 디스크 바늘이 한 번에 쭉 읽고 쓸 수 있다.
Ⅰ. 개요: 우체국 분리수거 (Context & Necessity)
전국에서 우체국으로 1초에 1만 통의 편지가 쏟아져 들어온다.
- 바보 우체부 (B-Tree): 편지가 올 때마다 우편번호(Key)를 보고 "아, 이건 강남구니까 강남구 우편함에 넣어야지!" 하고 편지함 100개를 왔다 갔다 하며 꽂아 넣는다. (Random Write, 디스크 폭발)
- 천재 우체부 (LSM-Tree): 편지가 오면 일단 **내 책상 위(MemTable)**에 우편번호 순서대로 차곡차곡 예쁘게 포개놓는다. 책상 위가 꽉 차면, 그 뭉치 그대로 고무줄로 묶어서 **창고(SSTable)**에 던져버리고 다시 빈 책상에서 일을 시작한다.
이 '책상'과 '창고'의 관계, 그리고 책상에서 창고로 짐을 던지는 행위(Flush)가 바로 현대 NoSQL과 빅데이터 저장소의 핵심 기술이다.
📢 섹션 요약 비유: MemTable은 **'테트리스 블록을 조립하는 임시 판'**과 같습니다. 이리저리 블록을 돌려서 완벽한 네모(정렬)를 만들죠. 꽉 차면 그 네모 덩어리 전체를 바닥으로 뚝 떨어뜨립니다(Flush). 바닥에 쌓인 덩어리(SSTable)는 굳어버려서 다시는 모양을 바꿀 수 없습니다(Immutable).
Ⅱ. 2대 핵심 컴포넌트의 작동 원리 ★
1. 멤테이블 (MemTable - Memory Table)
- 위치: RAM (휘발성 메모리)
- 자료구조: 보통 스킵 리스트(Skip List)나 레드-블랙 트리(Red-Black Tree)를 쓴다. 왜? 데이터가 무작위로 들어와도 메모리 안에서는 항상 $O(\log N)$ 속도로 Key 순서대로 예쁘게 정렬되어 있어야 하기 때문이다.
- 생존기: 정전이 나면 멤테이블의 데이터는 다 날아간다. 그래서 항상 디스크의 WAL(456번 문서) 로그 파일에 먼저 적어두고 멤테이블에 적는다.
2. 에스에스테이블 (SSTable - Sorted String Table)
- 위치: Hard Disk / SSD (영구 저장소)
- 생성 (Flush): 멤테이블이 64MB(설정값) 쯤 차면, 더 이상 데이터를 받지 않고(얼어붙음) 백그라운드 스레드가 이걸 통째로 디스크에 파일 1개로 저장한다.
- 특징 (Immutable): 파일 이름에 타임스탬프나 일련번호가 붙어 생성된다. 한 번 디스크에 써진 SSTable은 지구가 멸망할 때까지
UPDATE나DELETE로 수정되지 않는다. (오직 나중에 콤팩션-378번 문서-을 할 때만 지워지고 새로 만들어진다.)
Ⅲ. 실무 팁: 멤테이블 플러시(Flush) 병목 현상
"메모리에서 디스크로 밀어내는 게 그렇게 빠르다면서요? 문제없겠네요!" 아니다. 실무에서는 데이터가 들어오는 속도가 디스크가 받아쓰는 속도보다 빠르면 **'Write Stall (쓰기 멈춤)'**이라는 치명적 장애가 발생한다.
- 상황: 1번 멤테이블이 꽉 차서 디스크로 밀어내기(Flush) 시작했다.
- DB는 2번 멤테이블을 열어서 새로 들어오는 트래픽을 받는다.
- 그런데 트래픽이 미친 듯이 몰려와서, 디스크가 1번을 다 쓰기도 전에 2번 멤테이블마저 꽉 차버렸다!
- 결과: DB는 메모리가 터지는 걸 막기 위해, 순간적으로 클라이언트의
INSERT요청을 싹 다 거부해 버린다. (Write Stall 발생)
이를 막기 위해 DBA는 멤테이블의 크기를 늘리거나, 멤테이블 개수(Buffer Size)를 넉넉하게 잡고, 디스크 I/O 성능(SSD)을 최고로 끌어올려야 한다.
┌──────────────────────────────────────────────────────────────┐
│ MemTable에서 SSTable로의 Flush(밀어내기) 시각화 │
├──────────────────────────────────────────────────────────────┤
│ │
│ [ 👨💻 실시간 데이터 (마구잡이 순서) ] │
│ Key: 99, 12, 45, 1, 80 │
│ │ │
│ ▼ │
│ ┌───────────────────────────┐ (정렬 트리로 조립됨) │
│ │ 🧠 MemTable (RAM) │ [ 1, 12, 45, 80, 99 ] │
│ └───────────┬───────────────┘ │
│ │ 100% 꽉 참! (Flush 발동 🚀) │
│ ▼ │
│ ┌───────────────────────────┐ (정렬된 덩어리 그대로 파일로 뚝 떨어짐)│
│ │ 💾 SSTable_003 (Disk) │ [ 1, 12, 45, 80, 99 ] │
│ └───────────────────────────┘ (이제 절대 수정 불가! Immutable) │
│ │
│ ★ 특징: 디스크 바늘이 1번부터 99번까지 그냥 쭈욱~ 밀면서 쓰면 끝난다. │
└──────────────────────────────────────────────────────────────┘
Ⅳ. 결론
"디스크를 가장 편안하게 해주는 메모리의 희생." RDBMS의 전통적인 B-Tree가 디스크의 임의 접근(Random Access)을 당연하게 받아들였다면, MemTable과 SSTable 구조는 "디스크는 무조건 일렬로 쫙 쓰게 해줘야 한다"는 철학으로 만들어졌다. 이 구조 덕분에 구글, 페이스북, 넷플릭스는 하루에 수조 건이 쏟아지는 로그와 시계열 데이터를 단 한 번의 서버 다운 없이 디스크에 우겨넣을 수 있었다. 다만 이 무식한 쓰기 속도 뒤에는 쪼개진 SSTable들을 읽기 위해 고군분투하는 블룸 필터(Bloom Filter)와 콤팩션(Compaction)이라는 또 다른 거대한 기술적 희생이 숨어있음을 명심해야 한다.
📌 관련 개념 맵
- 상위 아키텍처: LSM-Tree (493번 문서)
- 뒷수습 기술: Compaction (콤팩션 - 조각난 SSTable 합치기, 378번 문서)
- 관련 데이터베이스: Apache Cassandra, HBase, RocksDB
- 필수 로깅: WAL (Write-Ahead Log - 456번 문서)
👶 어린이를 위한 3줄 비유 설명
- 멤테이블(MemTable)은 장난감 공장의 '조립 테이블'이에요. 무작위로 쏟아지는 레고 블록을 예쁜 로봇으로 빠르게 조립해 두죠.
- 로봇이 다 완성되면(꽉 차면) 포장 상자에 쓱 밀어 넣어서 창고로 보내버리는데, 이 상자를 쓱 밀어 넣는 행동을 플러시(Flush)라고 해요.
- 창고에 차곡차곡 쌓인 상자들을 SSTable이라고 부르는데, 한 번 테이프를 붙여서 창고에 넣은 상자는 절대로 다시 뜯어서(수정) 내용물을 바꿀 수 없답니다!