← 목록으로
DATABASE2026.09.12 07:18

Cassandra 파티션 키와 클러스터링 컬럼으로 Wide Row 쓰기 성능 최적화

Apache Cassandra는 분산 환경에서 초당 수십만 건의 쓰기를 처리할 수 있는 NoSQL 데이터베이스로, 시계열 데이터·이벤트 로그·IoT 스트림 같은 쓰기 집약적 워크로드에 자주 채택됩니다.

목차

  1. 개요
  2. Cassandra 데이터 모델의 핵심 구조
  3. 파티션 키 설계 전략
  4. 클러스터링 컬럼과 Wide Row 패턴 설계
  5. 쓰기 성능 심화 최적화
  6. 운영 환경 적용 시 고려사항
  7. 맺음말

개요

문제 배경

Apache Cassandra는 분산 환경에서 초당 수십만 건의 쓰기를 처리할 수 있는 NoSQL 데이터베이스로, 시계열 데이터·이벤트 로그·IoT 스트림 같은 쓰기 집약적 워크로드에 자주 채택됩니다. 그러나 Cassandra가 내부적으로 데이터를 어떻게 배치하는지 이해하지 못한 채 스키마를 설계하면, 뛰어난 하드웨어를 갖추고도 성능이 기대에 한참 못 미치는 상황을 맞닥뜨리게 됩니다. 특히 파티션 키(Partition Key)클러스터링 컬럼(Clustering Column) 의 조합이 잘못 설계되면, 특정 노드에 부하가 집중되거나 읽기·쓰기 모두 비효율적인 구조가 만들어집니다.

이 글에서는 Cassandra가 Wide Row를 어떻게 저장하는지 내부 메커니즘을 살펴보고, 파티션 키와 클러스터링 컬럼을 어떻게 설계해야 쓰기 성능이 극대화되는지 구체적인 원칙과 사례 중심으로 설명합니다. 단순 설정 나열이 아니라, 각 결정이 왜 성능에 영향을 주는지 이유와 트레이드오프를 함께 다룹니다.

기존 방식의 한계

관계형 데이터베이스 사고방식을 그대로 Cassandra에 적용하면 필연적으로 문제가 발생합니다. RDBMS에서는 정규화가 미덕이지만, Cassandra에서는 쿼리 우선(Query-First) 설계 가 원칙입니다. JOIN이 없기 때문에 읽기 패턴을 먼저 결정하고 그에 맞춰 테이블을 역정규화해야 합니다. 또한 기본 키를 단순히 고유성 보장 목적으로만 설정하면, Cassandra가 제공하는 Wide Row의 장점을 전혀 활용하지 못합니다. Wide Row란 하나의 파티션 안에 수천~수백만 개의 행을 클러스터링 컬럼으로 정렬하여 저장하는 패턴으로, SSTable에 순차 기록되어 쓰기·읽기 모두 효율적입니다. 이 패턴을 올바르게 설계하지 못하면 오히려 파티션 크기가 무한정 커지는 핫 파티션(Hot Partition) 문제나 Tombstone 누적으로 인한 읽기 성능 저하를 겪게 됩니다.


Cassandra 데이터 모델의 핵심 구조

파티션과 SSTable의 관계

Cassandra의 데이터는 파티션(Partition) 단위로 노드에 분산됩니다. 파티션 키를 해시한 토큰값이 해당 데이터가 저장될 노드를 결정합니다. 쓰기 요청이 들어오면 데이터는 즉시 디스크에 기록되는 것이 아니라 Memtable 이라는 인메모리 구조에 먼저 적재되고, Memtable이 가득 차거나 커밋 로그(Commit Log) 임계치에 도달하면 SSTable(Sorted String Table) 로 플러시됩니다. SSTable은 불변(Immutable) 구조로, 한 번 작성되면 수정되지 않으며 이후 Compaction 과정에서 여러 SSTable이 병합됩니다.

이 구조에서 핵심은 쓰기가 항상 순차(Sequential) I/O 라는 점입니다. 랜덤 I/O가 없기 때문에 HDD에서도 높은 쓰기 처리량을 낼 수 있습니다. 단, 이 이점을 누리려면 같은 파티션 내 데이터가 하나의 SSTable 세그먼트에 연속적으로 배치되어야 합니다. 파티션 키 설계가 잘못되면 같은 파티션 데이터가 여러 SSTable에 흩어지고, Compaction 부담이 증가하여 쓰기 성능이 오히려 저하됩니다.

Cassandra 쓰기 경로

클라이언트 요청
     │
     ▼
Commit Log (순차 append, 내구성 보장)
     │
     ▼
Memtable (인메모리, 파티션별 정렬)
     │ 임계치 초과 시 flush
     ▼
SSTable (디스크, 불변, 파티션 키 순 정렬)
     │
     ▼
Compaction (주기적 SSTable 병합 & 정리)

기본 키 구조 이해

Cassandra의 PRIMARY KEY 는 크게 두 부분으로 구성됩니다. 괄호 맨 앞에 오는 요소가 파티션 키이고, 그 뒤에 오는 컬럼들이 클러스터링 컬럼입니다. 파티션 키가 복합(Composite)인 경우에는 이중 괄호로 감쌉니다.

구성 요소 역할 위치 변경 가능 여부
파티션 키 노드 라우팅 결정, 파티션 경계 정의 PRIMARY KEY 첫 번째 요소 불변 (변경 시 새 파티션 생성)
클러스터링 컬럼 파티션 내부 정렬 순서 결정 PRIMARY KEY 두 번째 이후 선언 시 ASC/DESC 지정
일반 컬럼 실제 데이터 저장 기본 키 이외 컬럼 자유롭게 업데이트

파티션 키는 데이터가 어느 노드로 가는지를 결정하고, 클러스터링 컬럼은 그 파티션 안에서 데이터가 어떤 순서로 저장되는지를 결정합니다. Wide Row를 설계할 때 이 두 가지가 쓰기 패턴과 읽기 패턴 모두에 영향을 줍니다.

토큰 링과 데이터 분산

Cassandra는 일관된 해싱(Consistent Hashing) 을 사용하여 데이터를 노드에 분산합니다. 파티션 키의 해시값(토큰)이 0부터 2^63-1 범위의 링 위에 배치되고, 각 노드는 링의 특정 구간을 담당합니다. 파티션 키가 잘못 설계되어 특정 값에 집중되면 일부 노드만 과부하를 받는 핫스팟(Hotspot) 이 발생합니다. 반면 파티션 키가 너무 고카디널리티(High Cardinality)이면 파티션이 과도하게 분산되어 Wide Row의 장점인 로컬 정렬 효율을 잃게 됩니다.

핵심 원칙: 파티션 키는 데이터가 균등하게 분산되도록 충분한 카디널리티를 가져야 하지만, 동시에 자주 함께 조회되는 데이터는 같은 파티션에 모여 있어야 합니다.


파티션 키 설계 전략

카디널리티 균형 맞추기

파티션 키 설계에서 가장 먼저 결정해야 할 것은 카디널리티(Cardinality) 입니다. 카디널리티가 너무 낮으면 특정 파티션으로 데이터가 집중되고, 너무 높으면 파티션이 지나치게 분산되어 범위 쿼리가 비효율적이 됩니다.

예를 들어 IoT 센서 데이터를 저장하는 경우, device_id만을 파티션 키로 쓰면 장치 수가 수백 개에 불과할 때 노드 분산이 균등하지 않습니다. 반대로 (device_id, timestamp)를 파티션 키로 쓰면 모든 레코드가 다른 파티션에 흩어져 특정 장치의 시계열 데이터를 한 번에 읽기가 어렵습니다.

현업 프로젝트에서 자주 사용하는 접근법은 버킷팅(Bucketing) 입니다. 시간 기반 버킷을 파티션 키에 포함시켜 적절한 파티션 크기를 유지합니다.

-- 버킷팅을 적용한 센서 데이터 테이블 정의
CREATE TABLE sensor_readings (
    device_id    TEXT,
    bucket       TEXT,          -- 예: '2026-09-12' (일별 버킷)
    recorded_at  TIMESTAMP,
    temperature  DOUBLE,
    humidity     DOUBLE,
    PRIMARY KEY ((device_id, bucket), recorded_at)
) WITH CLUSTERING ORDER BY (recorded_at DESC)
   AND compaction = {
       'class': 'TimeWindowCompactionStrategy',
       'compaction_window_unit': 'DAYS',
       'compaction_window_size': 1
   };

-- 쓰기 시 bucket 값을 애플리케이션에서 계산하여 삽입
INSERT INTO sensor_readings (device_id, bucket, recorded_at, temperature, humidity)
VALUES ('sensor-001', '2026-09-12', toTimestamp(now()), 23.5, 65.2);
-- 결과: 파티션 키 = ('sensor-001', '2026-09-12')
--       클러스터링 키 = recorded_at (DESC 정렬)

버킷 단위를 결정할 때는 파티션당 예상 행 수를 계산해야 합니다. Cassandra 공식 문서에서는 파티션당 최대 행 수를 약 10만~100만 행 수준으로 권장합니다. 하루에 센서 한 대가 1분 간격으로 데이터를 보내면 1,440행이므로 일별 버킷이 적합합니다. 초 단위라면 86,400행이 되어 일별 버킷이 여전히 안전 범위 안에 있지만, 밀리초 단위라면 시간별 버킷으로 조정해야 합니다.

복합 파티션 키의 효과적인 활용

복합 파티션 키는 단일 컬럼만으로 데이터 분산이 불균등할 때 추가 컬럼을 포함시켜 해시 공간을 넓히는 기법입니다. 단, 복합 파티션 키를 사용하면 두 컬럼을 모두 알아야만 파티션을 특정 할 수 있으므로, 쿼리 패턴에 두 컬럼이 항상 함께 존재해야 한다는 제약이 생깁니다.

파티션 키 구성 파티션 수 최대 파티션 크기 범위 쿼리 가능 여부 주의점
device_id 단일 장치 수 전체 데이터량 / 장치 수 가능 (ALLOW FILTERING 없이) 장치 수 적으면 핫스팟 위험
(device_id, bucket) 복합 장치 수 × 버킷 수 버킷 기간 내 데이터량 bucket 없이는 불가 쿼리 시 bucket 필수
(device_id, shard) 해시 샤딩 장치 수 × 샤드 수 전체 / (장치 × 샤드) shard 없이는 불가 읽기 시 fan-out 쿼리 필요

복합 파티션 키에서 또 한 가지 주의할 점은 파티션 키 내 컬럼 순서가 해시에 영향을 준다는 것입니다. (device_id, bucket)(bucket, device_id)는 파티션 키로서 해시 결과가 다를 수 있으며, 의미적으로도 첫 번째 컬럼이 "주 분류 기준"처럼 읽히기 때문에 가독성을 위해 더 의미 있는 컬럼을 앞에 두는 것이 관례입니다.

쓰기 집중도를 분산시키는 해시 샤딩

버킷팅만으로는 특정 시간대에 쓰기가 집중되는 문제를 해결하기 어렵습니다. 예를 들어 bucket = '2026-09-12'에 해당하는 하루 치 데이터가 모두 같은 날 기록된다면, 그 파티션이 배치된 노드에 부하가 집중됩니다. 이 경우 해시 샤딩(Hash Sharding) 을 추가합니다. 파티션 키에 shard_id = record_id % N 같은 값을 포함시켜, 같은 장치·같은 날 데이터를 N개의 파티션으로 분산합니다.

트레이드오프: 해시 샤딩은 쓰기 분산에는 효과적이지만, 읽기 시 N개의 파티션을 모두 조회(Fan-Out)해야 하므로 읽기 비용이 N배로 증가합니다. 쓰기가 읽기보다 훨씬 빈번한 워크로드에서만 적용을 권장합니다.


클러스터링 컬럼과 Wide Row 패턴 설계

클러스터링 컬럼이 만드는 정렬 구조

클러스터링 컬럼은 파티션 내부에서 데이터가 저장되는 물리적 순서를 결정합니다. Cassandra의 SSTable은 파티션 키 순으로 정렬되고, 각 파티션 내부에서는 클러스터링 컬럼 순으로 정렬됩니다. 이 정렬이 디스크 상의 연속 배치 를 보장하기 때문에, 범위 스캔(Range Scan)이 랜덤 I/O 없이 순차 읽기로 처리됩니다.

Wide Row 패턴의 핵심은 바로 여기에 있습니다. 하나의 파티션 안에 클러스터링 컬럼으로 수천~수백만 행을 정렬하여 저장하면, "최근 N건 조회"나 "특정 시간 범위 조회" 같은 쿼리를 인덱스 없이 순차 읽기로 처리할 수 있습니다.

Wide Row 물리 저장 구조 (파티션 내부)

파티션 키: ('sensor-001', '2026-09-12')
─────────────────────────────────────────────────────
클러스터링 키(recorded_at DESC) │ temperature │ humidity
─────────────────────────────────────────────────────
2026-09-12 23:59:00           │   24.1      │  63.0
2026-09-12 23:58:00           │   23.9      │  63.5
2026-09-12 23:57:00           │   23.8      │  63.8
...                            │   ...       │  ...
2026-09-12 00:01:00           │   20.1      │  70.2
2026-09-12 00:00:00           │   20.0      │  71.0
─────────────────────────────────────────────────────
모두 SSTable의 연속된 블록에 저장 → 순차 I/O

정렬 방향과 쿼리 패턴의 일치

클러스터링 컬럼의 정렬 방향(ASC / DESC)은 테이블 생성 시 CLUSTERING ORDER BY 절로 지정하며, 이후 변경할 수 없습니다. 정렬 방향을 잘못 선택하면 가장 자주 실행하는 쿼리에서 전체 파티션을 역순 스캔해야 하는 비효율이 발생합니다.

시계열 데이터의 경우 "최신 데이터 우선 조회"가 일반적이므로 DESC 정렬이 유리합니다. DESC로 설정하면 파티션 첫 블록에 최신 데이터가 배치되어 LIMIT N 쿼리가 파티션 초반만 읽고 멈출 수 있습니다. 반대로 ASC로 설정했다면 최신 데이터를 얻으려고 파티션 끝까지 읽어야 합니다.

정렬 방향 적합한 쿼리 패턴 부적합한 쿼리 패턴 디스크 상 배치
ASC 오래된 것부터 순서대로 처리 (로그 재생, 이벤트 소싱) 최신 N건 조회 오래된 행이 파티션 앞쪽
DESC 최신 N건 조회 (대시보드, 피드) 특정 시각 이후 전체 조회 최신 행이 파티션 앞쪽

다중 클러스터링 컬럼의 계층적 설계

클러스터링 컬럼은 여러 개를 지정할 수 있으며, 지정된 순서대로 계층적으로 정렬됩니다. 이를 활용하면 복잡한 쿼리 패턴도 인덱스 없이 처리할 수 있습니다.

예를 들어 사용자별 알림 목록을 저장하는 경우를 생각해봅니다. 알림은 "읽지 않은 것을 위에" 보여주고, 같은 읽음 상태 내에서는 최신순으로 정렬하고 싶습니다.

-- 다중 클러스터링 컬럼으로 계층 정렬 구현
CREATE TABLE user_notifications (
    user_id      UUID,
    is_read      BOOLEAN,
    created_at   TIMESTAMP,
    notification_id UUID,
    message      TEXT,
    PRIMARY KEY (user_id, is_read, created_at, notification_id)
) WITH CLUSTERING ORDER BY (
    is_read     ASC,   -- false(0)가 앞 → 읽지 않은 알림 우선
    created_at  DESC,  -- 같은 읽음 상태 내에서 최신순
    notification_id DESC
);

-- 조회: 특정 사용자의 알림 최신 20건 (읽지 않은 것 우선)
SELECT * FROM user_notifications
WHERE user_id = 550e8400-e29b-41d4-a716-446655440000
LIMIT 20;
-- 결과: is_read=false인 알림이 먼저, 그 다음 is_read=true 알림
--       각 그룹 내에서 created_at DESC 정렬 적용

다중 클러스터링 컬럼을 사용할 때 중요한 제약이 있습니다. WHERE 절에서 클러스터링 컬럼을 필터링할 때, 앞 순서의 컬럼 조건 없이 뒤 순서의 컬럼만 조건으로 사용하는 것은 허용되지 않습니다. is_read 조건 없이 created_at만으로 필터링하려면 ALLOW FILTERING이 필요하며, 이는 전체 파티션 스캔을 유발하므로 운영 환경에서는 사용을 피해야 합니다.


쓰기 성능 심화 최적화

Compaction 전략과 쓰기 증폭

Cassandra의 쓰기 성능에 가장 큰 영향을 미치는 백그라운드 작업은 Compaction 입니다. Compaction은 여러 SSTable을 병합하여 삭제된 데이터(Tombstone)를 정리하고, 같은 키에 대한 여러 버전을 최신 값으로 합치는 과정입니다. Compaction 전략 선택이 잘못되면 쓰기 증폭(Write Amplification) — 실제 쓰기보다 훨씬 많은 I/O가 발생하는 현상 — 이 심화됩니다.

시계열 데이터처럼 특정 시간 범위의 데이터가 함께 생성되고 함께 삭제되는 워크로드에서는 TWCS(Time Window Compaction Strategy) 가 가장 적합합니다. TWCS는 같은 시간 윈도우에 속하는 SSTable끼리만 Compaction하고, 시간 윈도우가 닫히면 해당 SSTable을 더 이상 건드리지 않아 쓰기 증폭을 최소화합니다.

Compaction 전략 쓰기 성능 읽기 성능 공간 효율 적합한 워크로드
STCS (기본값) 높음 낮음 (많은 SSTable 조회) 낮음 쓰기 중심, 점 조회 거의 없음
LCS 낮음 (쓰기 증폭 높음) 높음 높음 읽기 중심, 업데이트 빈번
TWCS 매우 높음 중간 높음 시계열, 시간 기반 삭제
UCS (Cassandra 5+) 높음 높음 높음 범용, 세밀한 튜닝 필요

TWCS를 사용할 때는 compaction_window_sizecompaction_window_unit을 파티션 버킷 크기와 일치시키는 것이 중요합니다. 일별 버킷 파티션이라면 compaction_window_unit = DAYS, compaction_window_size = 1로 설정합니다. 윈도우 크기와 버킷 크기가 맞지 않으면 Compaction이 기대만큼 효율적으로 동작하지 않습니다.

TTL과 Tombstone 관리

Cassandra에서 데이터를 삭제할 때 실제로 즉시 제거되지 않고 Tombstone 이라는 삭제 마커가 기록됩니다. Tombstone은 Compaction 시에 최종적으로 정리됩니다. 문제는 Tombstone이 누적되면 읽기 성능이 크게 저하된다는 것입니다. SELECT 쿼리가 실제 데이터를 찾기 위해 수많은 Tombstone을 건너뛰어야 하기 때문입니다.

시계열 데이터처럼 보존 기간이 정해진 경우, 명시적 DELETE보다 TTL(Time To Live) 을 활용하는 것이 Tombstone 관리에 유리합니다. TTL 기반 만료는 TWCS와 결합될 때 특히 효과적입니다. 시간 윈도우가 닫히고 해당 윈도우의 모든 데이터가 TTL을 초과하면, TWCS는 해당 SSTable 전체를 Tombstone 없이 바로 드롭할 수 있습니다.

핵심 함정: DELETE 문으로 데이터를 대량 삭제하면 Tombstone이 폭발적으로 증가합니다. 시계열 데이터의 오래된 레코드 정리는 TTL 설정으로 처리하고, DELETE는 최소한으로만 사용해야 합니다.

배치 쓰기와 UNLOGGED BATCH

Cassandra에서 BATCH 문은 여러 쓰기를 묶어 원자적으로 처리하는 기능입니다. 그러나 일반적인 LOGGED BATCH는 코디네이터 노드에서 배치 로그를 관리하기 때문에 오히려 쓰기 성능을 저하시킵니다. 같은 파티션에 대한 여러 쓰기를 묶을 때는 UNLOGGED BATCH 가 더 효율적입니다.

UNLOGGED BATCH는 원자성을 보장하지 않지만, 같은 파티션을 대상으로 할 때는 Cassandra 내부적으로 단일 파티션 쓰기로 최적화됩니다. 따라서 같은 (device_id, bucket) 파티션에 여러 행을 한꺼번에 쓸 때 UNLOGGED BATCH를 사용하면 왕복(Round-Trip) 횟수를 줄이고 쓰기 처리량을 높일 수 있습니다.

단, 서로 다른 파티션에 대한 BATCH는 절대 사용하지 말아야 합니다. 다른 파티션을 포함하는 BATCH는 코디네이터 노드가 각 파티션을 담당하는 여러 노드에 요청을 분산해야 하므로, 단순 개별 쓰기보다 오버헤드가 훨씬 큽니다.


운영 환경 적용 시 고려사항

파티션 크기 모니터링과 핫스팟 탐지

파티션 키 설계가 올바르더라도 데이터 패턴이 예상과 달라지면 핫스팟이 발생할 수 있습니다. 운영 환경에서는 주기적으로 파티션 크기를 모니터링해야 합니다. Cassandra는 nodetool tablehistograms 명령으로 파티션 크기 분포를 확인할 수 있으며, nodetool toppartitions(Cassandra 4.0+)로 실시간으로 크기가 큰 파티션을 찾을 수 있습니다.

nodetool toppartitions 명령 활용 예시

$ nodetool toppartitions iot sensor_readings 10 1000
-- 위 명령은 1초(1000ms) 동안 가장 쓰기/읽기가 많은 파티션 10개를 표시

예상 출력:
WRITES Sampler:
  Partitions: 10
  Samples: 1523
  ┌──────────────────────────────────────┬───────┐
  │ Partition                            │ Count │
  ├──────────────────────────────────────┼───────┤
  │ sensor-999:2026-09-12                │  312  │  ← 핫스팟 의심
  │ sensor-001:2026-09-12                │   45  │
  │ sensor-002:2026-09-12                │   41  │
  └──────────────────────────────────────┴───────┘
-- sensor-999의 쓰기 비율이 비정상적으로 높다면 파티션 키 재검토 필요

파티션 크기가 지속적으로 커진다면 버킷 단위를 줄이거나 해시 샤딩을 추가해야 합니다. Cassandra 공식 문서에서는 단일 파티션의 디스크 크기를 100MB 이하 로 유지할 것을 권장합니다. 100MB를 초과하면 Compaction과 읽기 모두에서 성능 문제가 나타나기 시작합니다.

스키마 마이그레이션과 무중단 배포

Cassandra는 ALTER TABLE로 새 컬럼을 추가하거나 기본 키가 아닌 컬럼의 타입을 변경할 수 있습니다. 그러나 파티션 키나 클러스터링 컬럼의 구성을 변경하는 것은 불가능 합니다. 기본 키 구조를 바꾸려면 새 테이블을 만들고 데이터를 마이그레이션해야 합니다.

운영 환경에서 테이블 마이그레이션을 무중단으로 수행하는 일반적인 절차는 다음과 같습니다.

  1. 새 스키마로 신규 테이블 생성
  2. 애플리케이션에서 기존 테이블과 신규 테이블에 이중 쓰기(Dual-Write) 시작
  3. 기존 데이터를 배치 작업으로 신규 테이블에 백필(Backfill)
  4. 읽기를 점진적으로 신규 테이블로 전환 (카나리 배포)
  5. 이중 쓰기 종료 후 기존 테이블 드롭

이 과정에서 가장 주의해야 할 점은 백필 작업이 운영 클러스터에 미치는 부하 입니다. 대량의 읽기-쓰기가 동시에 발생하므로, 백필 속도를 제한(rate limiting)하고 피크 시간대를 피해서 실행해야 합니다.

일관성 수준과 쓰기 성능의 트레이드오프

Cassandra에서 쓰기 일관성 수준(Consistency Level)은 쓰기 성능에 직접적인 영향을 줍니다. 일관성 수준이 높을수록 더 많은 레플리카가 응답을 확인해야 하므로 레이턴시가 증가합니다.

일관성 수준 응답해야 하는 레플리카 수 (RF=3 기준) 쓰기 레이턴시 데이터 손실 위험
ANY 0 (힌트도 포함) 매우 낮음 높음 (노드 장애 시 힌트 유실 가능)
ONE 1 낮음 낮음
LOCAL_QUORUM 2 (로컬 DC) 중간 매우 낮음
QUORUM 2 (전체 DC) 높음 (멀티 DC 시) 거의 없음
ALL 3 매우 높음 없음 (단, 가용성 저하)

쓰기 집약적 워크로드에서는 LOCAL_QUORUM을 기본으로 사용합니다. ONE은 단일 노드 장애 시 미확인 쓰기가 유실될 수 있으므로, 금융 거래처럼 데이터 손실이 허용되지 않는 경우에는 적합하지 않습니다. ANY는 힌트(Hint) 기반으로 동작하여 코디네이터 노드에 데이터가 임시 저장되었다가 나중에 레플리카로 전달되기 때문에, 코디네이터 장애 시 데이터가 사라질 수 있어 실제 프로젝트에서는 거의 사용하지 않습니다.


맺음말

핵심 요약

Cassandra에서 파티션 키와 클러스터링 컬럼을 올바르게 설계하는 것은 단순한 스키마 선택이 아니라, 데이터가 물리적으로 어떻게 배치되고 어떤 경로로 읽히는지를 결정하는 아키텍처 결정입니다. 이 글에서 다룬 핵심 원칙을 정리하면 다음과 같습니다. 첫째, 파티션 키는 데이터를 균등하게 분산하면서 자주 함께 조회되는 데이터를 같은 파티션에 모을 수 있도록 카디널리티와 버킷팅을 함께 고려해야 합니다. 둘째, 클러스터링 컬럼은 가장 빈번한 쿼리 패턴의 정렬 방향과 일치시켜야 하며, 한번 정해지면 테이블 재생성 없이는 바꿀 수 없으므로 초기 설계가 매우 중요합니다. 셋째, Compaction 전략은 워크로드 특성에 맞게 선택해야 하며, 시계열 데이터라면 TWCS와 TTL의 조합이 Tombstone 누적을 최소화하고 쓰기 증폭을 억제합니다.

적용 판단 기준

모든 문제에 Wide Row가 답은 아닙니다. Wide Row 패턴은 하나의 엔터티(사용자·장치·주문)에 대한 시간순 데이터를 빠르게 범위 조회 해야 할 때 가장 강력합니다. 단순한 키-값 조회가 주를 이루거나, 관계가 복잡하게 얽힌 데이터라면 Redis, PostgreSQL, 또는 MongoDB 같은 다른 데이터베이스를 먼저 검토하는 것이 맞습니다. Cassandra의 강점은 쓰기 처리량과 선형 확장성이지, 유연한 쿼리나 트랜잭션 일관성이 아닙니다. 이 특성을 파악하고 Cassandra가 적합한 문제에 올바른 파티션 설계를 적용했을 때, 수백 GB의 데이터에서도 밀리초 단위의 쓰기 레이턴시를 안정적으로 유지하는 결과를 얻을 수 있습니다.