본문으로 건너뛰기
김신건의 로그

[DB] Cassandra: LSM-tree, quorum, eventually consistent

· 수정 · 📖 약 3분 · 945자/단어 #cassandra #nosql #distributed #lsm-tree #database
Cassandra, Apache Cassandra, LSM tree, memtable, SSTable, quorum read/write, CQL, ScyllaDB

정의

Apache Cassandra = 마스터리스 (peer-to-peer) 분산 KV 스토어. write 우선, eventually consistent, 수평 무한 확장. 2008 Facebook 의 Dynamo + BigTable 결합.

ScyllaDB = Cassandra 호환 C++ 재구현. 10x throughput.

핵심 특성

Cassandra
토폴로지peer-to-peer (master 없음)
일관성튜닝 가능 (per query)
스토리지LSM-tree (write-optimized)
쿼리 언어CQL (SQL-like)
인덱스primary key + secondary (제한)
Join없음
트랜잭션제한적 (LWT, lightweight transaction)

LSM-Tree (Log-Structured Merge-Tree)

flowchart TB
    Write[Write] --> CommitLog["Commit Log<br/>(WAL)"]
    Write --> Memtable["Memtable<br/>(메모리)"]
    Memtable -.flush.-> SST1[SSTable L0]
    SST1 -.compact.-> SST2[SSTable L1]
    SST2 -.compact.-> SST3[SSTable L2]
    SST3 -.compact.-> SST4[SSTable L3]
컴포넌트의미
Commit LogWAL (crash recovery)
Memtable메모리 정렬 자료
SSTable불변 정렬 파일 (디스크)
CompactionSSTable 들을 큰 SSTable 로 병합

외부 정렬 머지의 일반 직관. LSM-tree 의 compactionlevel 들을 순차 머지.

LSM vs B-tree

항목B-treeLSM
Writein-place (random)append (sequential)
Read1 lookup여러 SSTable 검색
Write amplification작음큼 (compaction)
Read amplification작음
적합OLTP read-heavywrite-heavy, log-like

클러스터 토폴로지

flowchart LR
    subgraph Ring[Token Ring]
        N1[Node A<br/>token 0]
        N2[Node B<br/>token 1/3]
        N3[Node C<br/>token 2/3]
    end
    N1 --- N2 --- N3 --- N1
    Key1[hash key=42] -->|시계방향| N2
  • consistent hashing ring.
  • N 개 노드, Replication Factor (RF) 만큼 복제.
  • 전체가 peer. master 없음.

Consistency Level (per query)

Level의미
ANY어떤 노드라도 응답 (write)
ONE1 노드 응답
TWO, THREE2 / 3 노드
QUORUM과반 (RF/2 + 1)
LOCAL_QUORUM같은 DC 안 과반
EACH_QUORUM모든 DC 의 과반
ALL모든 replica

Quorum 의 W + R > N 조건

flowchart LR
    Write[W = QUORUM] --> N[RF = 3]
    Read[R = QUORUM]
    Cond["W + R > N → 항상 최신 read"]
    Write --> Cond
    Read --> Cond

RF = 3, W = QUORUM (2), R = QUORUM (2). W + R = 4 > 3 → 최소 1 노드는 양쪽 모두 봄strong consistency.

Read Path

sequenceDiagram
    Client->>Coord: SELECT
    Coord->>R1: read
    Coord->>R2: read
    par 응답 대기
        R1-->>Coord: v1 (ts=100)
        R2-->>Coord: v2 (ts=110)
    end
    Coord->>Coord: 최신 (ts=110) 반환
    Coord->>R1: read repair (백그라운드)

Last-write-wins by timestamp. 시계 어긋남데이터 정정 의 함정.

CQL (Cassandra Query Language)

CREATE KEYSPACE shop
  WITH replication = { 'class': 'NetworkTopologyStrategy', 'dc1': 3 };

CREATE TABLE orders (
  user_id UUID,
  order_id TIMEUUID,
  total INT,
  status TEXT,
  PRIMARY KEY (user_id, order_id)
) WITH CLUSTERING ORDER BY (order_id DESC);

-- partition key = user_id, clustering key = order_id
SELECT * FROM orders WHERE user_id = ? AND order_id > ?;
INSERT INTO orders (user_id, order_id, total, status)
  VALUES (?, now(), ?, ?);
의미
Partition Keypartition (노드) 결정
Clustering Keypartition 안의 정렬

LWT (Lightweight Transaction)

INSERT INTO users (id, email) VALUES (?, ?) IF NOT EXISTS;
UPDATE users SET email = ? WHERE id = ? IF email = ?;
  • Paxos 기반. 4 round-trip.
  • 느림. 가끔만 사용.

적합 / 부적합

flowchart TD
    Q{사용 패턴}
    Q --> Good[적합]
    Q --> Bad[부적합]
    Good --> G1[Write-heavy log]
    Good --> G2[Time-series]
    Good --> G3[IoT telemetry]
    Good --> G4[수십 TB 데이터]
    Bad --> B1[복잡한 ad-hoc 쿼리]
    Bad --> B2[강한 트랜잭션]
    Bad --> B3[Join 많음]
    Bad --> B4[작은 데이터셋]

흔한 함정

WARNING

  1. Secondary index 남용 = 거의 항상 전체 노드 fan-out. 모든 access pattern 에 전용 테이블.
  2. Tombstone (삭제 마커) 누적 = 압축 안 되면 read 폭증. gc_grace_seconds + 정기 repair.
  3. Cross-partition query = 비싸다. partition key 디자인이 결정적.
  4. Strong consistency 기대 = QUORUM 으로도 시계 어긋남 시 잘못된 결과. NTP + 보수적 timestamp.

Compaction 전략

전략특징적합
STCS (Size-Tiered)비슷한 크기 SSTable 병합write-heavy
LCS (Leveled)고정 크기 level 유지read-heavy, 공간 효율
TWCS (Time-Window)시간 창 단위 병합time-series, TTL
ALTER TABLE orders
  WITH compaction = {
    'class': 'TimeWindowCompactionStrategy',
    'compaction_window_unit': 'HOURS',
    'compaction_window_size': 1
  };

TIP

Time-series 데이터는 TWCS + TTL 조합. 오래된 데이터가 자동 삭제되어 tombstone 누적 없음.

데이터 모델링 원칙

flowchart TD
    Q["쿼리 패턴 먼저 정의"] --> T["테이블 설계"]
    T --> P["Partition Key: 균등 분산 여부"]
    P -->|불균등| FP["다른 partition key 선택"]
    FP --> P
    P -->|균등| C["Clustering Key: 정렬 방향 결정"]
    C --> OK["테이블 확정"]
  1. 쿼리 주도 설계: 먼저 쿼리를 정의하고, 그에 맞는 테이블을 만든다.
  2. 1 쿼리 = 1 테이블: 각 access pattern 에 전용 테이블.
  3. Partition 크기: 100MB 이하 권장. 너무 크면 hot partition.
  4. Denormalization: 중복 저장은 의도적. 조인 없음.
  5. TTL 활용: 만료 데이터는 INSERT ... USING TTL 로 자동 삭제.

nodetool 운영 명령어

# 클러스터 상태 확인
nodetool status

# 특정 키가 어느 노드에 있는지 확인
nodetool getendpoints shop orders <partition_key>

# 수동 compaction 실행
nodetool compact shop orders

# replica 동기화 (repair)
nodetool repair shop

# Tombstone 현황 확인
nodetool cfstats shop.orders | grep "Tombstone"

# 노드 드레인 (점검 전)
nodetool drain

Bloom Filter

각 SSTable 은 Bloom Filter 를 가진다. 읽기 시 SSTable 을 열기 전에 키가 없음을 빠르게 판별.

  • False positive 가능: 있다고 했지만 없음 → 실제 SSTable 열어 확인
  • False negative 없음: 없다고 하면 진짜 없음
  • bloom_filter_fp_chance 로 정밀도 조정 (낮을수록 메모리 더 사용)
ALTER TABLE orders
  WITH bloom_filter_fp_chance = 0.01;  -- 기본 0.1, 낮을수록 정확

Write Path 상세

sequenceDiagram
    Client->>Coord: WRITE (CL = QUORUM)
    Coord->>N1: write
    Coord->>N2: write
    Coord->>N3: write (hinted handoff)
    par 응답 대기
        N1-->>Coord: ack
        N2-->>Coord: ack
    end
    Coord-->>Client: OK
    Note over N3: 오프라인 시 힌트 보관, 복귀 후 전달
  • Hinted Handoff: 대상 노드 다운 시 코디네이터가 임시 보관. 복귀 후 데이터 전달.
  • commit log 에 먼저 기록 후 memtable 업데이트: 내구성 보장.
  • ANY consistency 는 hint 만으로 성공 → 실제 replica 에 아직 없을 수 있음.

Read Repair & Anti-Entropy

메커니즘시점설명
Read Repair읽기 직후 백그라운드응답 노드 간 불일치 발견 시 자동 수정
Anti-Entropy수동 (nodetool repair)Merkle tree 비교로 전체 데이터 동기화

WARNING

Repair 를 주기적으로 실행하지 않으면 노드 간 데이터 드리프트가 누적됩니다. gc_grace_seconds 이내에 반드시 repair 완료.

관련 위키

이 글의 용어 (5개)
[DB] DynamoDB: PK + SK, single-table design, GSI / LSIdatabase-internals
정의 DynamoDB 는 AWS 의 fully managed key-value + document NoSQL. low-latency, infinite scale, schemale…
[DB] MongoDB: 문서 DB, WiredTiger, replica set, aggregationdatabase-internals
정의 MongoDB 는 BSON (binary JSON) 문서 기반 NoSQL DB. schema-less 라기보다 flexible schema. 2026 시점 Atlas (ma…
[DB] Sharding vs Partitioning: 수평 확장의 두 얼굴database-internals
정의 | | Partitioning | Sharding | |---|---|---| | 범위 | 한 DB 안 | 여러 DB 노드 | | 목적 | 큰 테이블 관리 | 수평 확장 (…
[DB] WAL: Write-Ahead Log, crash recovery, replication 의 토대database-internals
정의 Write-Ahead Log (WAL) = 데이터 변경 전 로그를 먼저 디스크에 기록. 모든 모던 RDB / KV / 분산 시스템의 내구성 기반. 핵심 약속: 로그가 디스크…
[Distributed Systems] Consensus: Raft, Paxosdistributed-systems
정의 Consensus (합의): 여러 노드가 같은 값에 동의 하는 분산 알고리즘. CAP 의 C 보장의 기반. 용도: - Leader election: 1개 leader 선출 …

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기