569. 카산드라 쓰기 경로(Write Path)와 병목 배제 모델

⚠️ 이 문서는 초당 수백만 건의 센서 데이터나 로그가 폭포수처럼 쏟아지는 빅데이터 환경에서, 기존 RDBMS의 'B-Tree' 기반 디스크 쓰기가 가진 치명적인 속도 저하(랜덤 I/O)를 완벽하게 극복하기 위해, **메모리와 디스크의 순차 쓰기(Sequential I/O)만을 극한으로 활용하여 쓰기 병목을 0으로 만들어버린 분산 NoSQL 데이터베이스 '아파치 카산드라(Cassandra)'의 쓰기 경로(Commit Log $\rightarrow$ Memtable $\rightarrow$ SSTable)**를 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: 기존 DB는 데이터를 저장할 때 '디스크의 어느 위치에 넣어야 할지' 빈칸을 찾느라 하드디스크의 헤드가 미친 듯이 움직이는(Random I/O) 최악의 병목을 겪는다. 카산드라는 이를 무시하고 그냥 순서대로 끝에다 적어버리는 방식을 택했다.
  2. 가치: 쓰기 작업(Write)이 램(RAM)에 데이터를 꽂아 넣는 것과 거의 동일한 엄청난 속도로 처리된다. 넷플릭스, 애플 같은 전 세계 스케일의 기업들이 하루 수십 테라바이트의 유저 활동 로그를 끊김 없이 받아내는 핵심 심장이다.
  3. 기술 체계: 데이터가 들어오면 만약의 정전을 대비해 하드디스크 끝에 대충 휘갈겨 적고(Commit Log), 동시에 메모리(Memtable)에 예쁘게 정리해 둔다. 메모리가 꽉 차면 그걸 그대로 얼려서 디스크에 불변의 파일(SSTable)로 툭 찍어내는 완벽한 3단계 논블로킹(Non-blocking) 아키텍처다.

Ⅰ. RDBMS B-Tree 쓰기의 치명적 딜레마 (랜덤 I/O)

도서관에 책을 꽂을 때마다 빈자리를 찾아야 한다.

  1. B-Tree의 정렬 집착:
    • 오라클(Oracle)이나 MySQL 같은 RDBMS는 데이터를 조회(Read)할 때 빛의 속도로 찾기 위해, 데이터가 들어올 때부터 예쁘게 가나다순으로 정렬(B-Tree)해서 디스크에 저장한다.
  2. 랜덤 I/O 병목의 발생:
    • 새로운 유저 '김철수' 데이터가 초당 1만 건씩 쏟아져 들어온다 치자.
    • DB는 이 데이터를 그냥 디스크 맨 끝에 던져두는 게 아니라, 디스크 한가운데를 비집고 들어가서 "강호동"과 "박지성" 사이에 자리를 억지로 만들어 넣어야 한다(Page Split).
    • 디스크 헤드가 빈자리를 찾으러 이리저리 미친 듯이 점프(Random I/O)하다가 큐(Queue)가 꽉 차서 DB가 기절해 버린다. 쓰기 속도는 1건당 수십 밀리초(ms)로 뚝 떨어진다.

📢 섹션 요약 비유: 서점에 신간 10,000권이 한꺼번에 박스로 도착했습니다. 직원이 책 1권을 집어 들고 "가나다순이네? 이건 3층 '가'열 구석에 꽂고... 다음 책은 1층 '하'열 구석에 꽂자"라며 엘리베이터를 오르락내리락하며 일일이 빈자리를 찾아 꽂는 끔찍한 막노동(Random I/O)이 RDBMS의 쓰기 병목입니다.


Ⅱ. 카산드라의 마법 쓰기 경로 (Write Path)

도서관 바닥에 그냥 던져놓고, 밤에 묶어서 한 번에 정리한다.

  1. 1단계: Commit Log (순차 쓰기):
    • 데이터가 들어오면 카산드라는 일단 서버가 정전되어 데이터가 날아갈 것에 대비해, 디스크의 무조건 '맨 끝부분'에 그냥 일기 쓰듯 쭉쭉 이어서 텍스트를 적어버린다(Sequential I/O).
    • 디스크 헤드가 점프할 필요 없이 끝에만 붙여 쓰기 때문에 디스크 쓰기 속도가 메모리 쓰기 속도와 맞먹을 정도로 엄청나게 빠르다. (복구용 장부 역할)
  2. 2단계: Memtable (메모리 정렬):
    • Commit Log에 적음과 동시에, 그 데이터를 메인 메모리(RAM) 공간인 '멤테이블(Memtable)'에도 올린다.
    • RAM은 빛의 속도이기 때문에, 여기서 데이터를 예쁘게 가나다순(키 순서)으로 정렬해 둔다.
    • 이 시점에서 클라이언트(사용자)에게 "저장 완료(Ack)!" 사인을 날려버린다. (디스크에 억지로 끼워 넣는 병목이 완전히 배제됨)
  3. 3단계: SSTable (디스크 덤프 플러시):
    • 10분쯤 지나 램(Memtable)이 꽉 찼다. 카산드라는 이 예쁘게 정렬된 메모리 덩어리를 그대로 꽝! 얼려서 하드디스크에 1개의 거대한 파일(SSTable)로 도장 찍듯 밀어낸다(Flush).
    • 이 파일은 한 번 굳어지면 절대 수정할 수 없다(Immutable). 수정이 안 되니 누군가 이 파일을 읽고 있을 때 락(Lock)을 걸 필요도 없어 속도가 또 빨라진다.

📢 섹션 요약 비유: 신간 10,000권이 오면 직원은 서가로 가지 않습니다. 혹시 모르니 "오늘 1만 권 들어옴"이라고 장부에 대충 휘갈겨 적고(Commit Log), 넓은 1층 로비(Memtable)에 일단 책을 쫙 깔아서 가나다순으로 임시로 예쁘게 분류만 해놓고 퇴근합니다. 로비가 꽉 차면, 그 상태 그대로 본드로 굳혀서 통째로 하나의 거대한 새 책장(SSTable)으로 만들어 버리는 천재적인 속도전입니다.


Ⅲ. 트레이드오프: 콤팩션(Compaction)과 읽기 패널티

쓰기를 미친 듯이 빠르게 만든 대가로, 읽을 때 고통받는다.

  1. SSTable 파편화의 저주:
    • 위의 방식대로 한 달을 운영하면, 로비에 굳혀진 책장(SSTable 파일)이 1,000개나 생긴다.
    • 누군가 "홍길동 정보 내놔!"라고 조회(Read) 쿼리를 날리면? 카산드라는 홍길동이 1번 파일에 있는지 1,000번 파일에 있는지 몰라 수백 개의 파일을 다 뒤져야 하는 '읽기 성능 저하(Read Amplification)'의 업보를 맞는다. (그래서 이를 막기 위해 어제 배운 '블룸 필터'를 앞단에 깐다.)
  2. 콤팩션 (Compaction) 백그라운드 작업:
    • 파편화된 1,000개의 파일을 그대로 둘 수 없으니, 카산드라는 손님이 없는 새벽 시간에 백그라운드 프로세스를 돌려 이 1,000개의 낡은 파일들을 읽어 들여 1개의 거대하고 깨끗한 새 파일(SSTable)로 뭉치고 합치는 대청소를 수행한다. (이때 CPU와 디스크 I/O가 치솟는다.)
  3. 아키텍처의 결론:
    • 읽기(Read)보다 쓰기(Write)가 압도적으로 많은 환경(9:1 비율 이상), 즉 주식 틱 데이터 저장, IoT 온도 센서 데이터 적재 환경에서 카산드라의 이 구조는 지구상 어떤 RDBMS도 범접할 수 없는 궁극의 아키텍처다.

📢 섹션 요약 비유: 매일매일 통째로 굳혀버린 책장(SSTable)이 창고에 1,000개가 넘게 쌓였습니다. 손님이 책을 찾으러 오면 책장 1,000개를 다 뒤져야 하니 너무 느립니다(읽기 저하). 그래서 도서관 사서는 주말 밤을 새우며 1,000개의 낡은 책장을 부수고 똑같은 책은 하나로 뭉쳐(Compaction) 최신식 거대 책장 1개로 리모델링하는 뼈 깎는 대청소를 해야만 도서관이 정상적으로 유지됩니다.