중급6분 분량

DynamoDB 핫 파티션

DynamoDB는 데이터를 여러 물리 파티션에 분산하며, 각 파티션은 자기 몫의 처리량을 가집니다. 핫 파티션이란 하나의 키가 자기 몫이 감당할 수 있는 것보다 훨씬 많은 읽기 또는 쓰기를 끌어들여, 테이블의 나머지가 놀고 있는 동안 그 키로 향하는 요청만 스로틀되는 상황입니다.

DynamoDB 핫 파티션이란 무엇인가요?

DynamoDB 핫 파티션은 하나의 파티션 키가 자기 몫의 처리량으로 감당할 수 있는 것보다 훨씬 많은 읽기 또는 쓰기를 흡수해, 테이블의 나머지가 놀고 있는 동안 그 키로 향하는 요청만 스로틀되는 현상입니다. 원인은 테이블 크기가 아니라 키 설계입니다 — 유명인 아이템, 낮은 카디널리티의 키, 오늘 날짜 같은 것이죠. 해법은 쓰기를 분산하는 것입니다.

  • 원인은 테이블 크기가 아니라 키 설계입니다. 트래픽을 한 곳에 집중시키는 하나의 파티션 키 — 유명인 사용자, status="OPEN" 플래그, 오늘 날짜 — 가 함정입니다.
  • 적응형 용량이 도움은 되지만 해결책은 아닙니다. DynamoDB가 자동으로 부하를 재조정하지만, 단일 아이템이나 단일 키 하나가 여전히 한 파티션이 감당할 수 있는 한계를 넘어설 수 있습니다.
  • 해법은 쓰기를 분산하는 것입니다. 키에 엔트로피를 추가하거나(쓰기 샤딩) 핫 읽기 경로를 더 잘 분산된 액세스 패턴으로 옮기세요.
  • SQL에서 넘어왔다면 이에 대응되는 개념이 없습니다. 관계형 테이블에는 "한 행의 인덱스 값이 너무 인기가 많다"는 개념이 없습니다 — DynamoDB의 키별 처리량이 평평한 모델에는 있습니다.

애초에 파티션이 존재하는 이유

DynamoDB는 단일 노드 SQL 모델을 분할되고 수평으로 확장되는 모델로 바꾼 2007년 Amazon Dynamo 논문의 프로덕션 후예입니다. 데이터는 파티션 키의 해시로 여러 물리 스토리지 노드에 샤딩됩니다.

각 파티션은 한정된 양의 데이터를 보관하고 한정된 양의 처리량을 제공합니다. AWS는 파티션당 초당 3,000 읽기 단위와 1,000 쓰기 단위라는 하드 상한을 문서화하고 있습니다 (AWS — 파티션 동작).

그 상한이 전부입니다. 테이블의 프로비저닝된 처리량은 모든 파티션의 합이지만, 어떤 하나의 키든 단 하나의 파티션에만 떨어집니다.

함정의 정체: 한 키에 쌓이는 트래픽

처리량은 액세스가 키 전반에 고르게 분산될 때에만 고르게 공유됩니다. 한 키가 불균형한 트래픽을 받는 순간, 테이블의 전체 용량은 놀고 있는데 그 키만 스로틀됩니다.

전형적인 핫 키 형태:

  • 유명인 아이템 — 모두가 읽는 하나의 사용자, 제품, 또는 테넌트.
  • 낮은 카디널리티의 파티션 키status, country, type. 구별되는 값이 적다는 것은 모든 작업을 떠맡는 파티션이 적다는 뜻입니다.
  • 시간 버킷 키PK = "2026-06-23". 오늘의 모든 쓰기가 하나의 파티션을 두들기고, 어제 것은 영원히 차갑게 식습니다.

SQL에서 넘어왔다면 이 중 어느 것도 문제가 되지 않습니다. 인기 있는 값에 대한 B-트리 인덱스는 괜찮습니다. DynamoDB에서는 인기 있는 값이 물리적 배치의 단위 그 자체이므로, 인기가 처리량의 절벽이 됩니다.

실전 예제: 유명인 리더보드

전 세계 게임 리더보드를 운영한다고 합시다. 점수는 다음과 같이 키가 지정된 테이블에 저장됩니다:

PK = "BOARD#global"
SK = "PLAYER#<playerId>"

읽기는 점수 상위 N개를 가져오고, 쓰기는 매 경기 후 플레이어의 currentScore를 올립니다. 글로벌 보드의 모든 행이 하나의 파티션 키 — BOARD#global — 를 공유하므로, 모든 읽기와 쓰기가 단일 파티션에 떨어집니다.

여기에 200만 명의 라이브 시청자를 거느린 스트리머가 자기 순위 페이지의 새로고침 버튼을 연타하면, 그 하나의 파티션이 3,000 읽기 단위를 넘어섭니다. 테이블의 다른 모든 보드는 놀고 있는데 글로벌 보드에서는 ProvisionedThroughputExceededException이 발생합니다.

지뢰는 BOARD#global의 붕괴입니다. 하나의 논리적 보드를 하나의 물리적 키로 모델링한 것이죠.

쓰기를 분산하기: 키 샤딩

해법은 카디널리티를 인위적으로 만드는 것입니다. 파티션 키에 샤드 접미사를 붙여 하나의 논리적 보드가 N개의 물리 파티션으로 부채처럼 펼쳐지게 하세요:

PK = "BOARD#global#<shard>"  -- shard = playerId mod 10
SK = "PLAYER#<playerId>"

이제 쓰기가 하나가 아니라 열 개의 파티션에 흩어집니다 — 쓰기 여유가 열 배가 됩니다. 대가는, 보드 전체를 읽으려면 열 개의 샤드를 모두 조회해 병합해야 한다는 점입니다. 단일 Query로는 샤드 경계를 넘을 수 없기 때문이죠. 읽기의 단순함을 쓰기의 분산과 맞바꾸는 것입니다.

AWS는 이를 쓰기 샤딩이라 부르며, 고속·저카디널리티 키에 대해 정확히 이를 권장합니다 (AWS — 쓰기 샤딩 사용하기).

이것은 단일 테이블 설계의 바탕에 깔린 것과 같은 키 오버로딩 본능입니다 — 데이터가 "자연스럽게" 놓이는 방식이 아니라, 액세스 패턴에 맞춰 키를 형성하는 것이죠.

적응형 용량에 쉬운 부분을 맡기기

DynamoDB에는 적응형 용량이 탑재되어 있으며, re:Invent 2018 세션 "Amazon DynamoDB Under the Hood"(DAT401)에서 다뤄졌습니다. 이것은 테이블의 처리량을 부하를 받는 파티션 쪽으로 지속적으로 재분배하며, 지속적으로 뜨거운 키를 자체 파티션으로 격리합니다 (키 수준 격리, AWS — 버스팅 및 적응형 용량).

이것은 즉각적이고 무료지만, 물리 법칙에 의해 한계가 있습니다. 적응형 용량은 부하를 키 사이에서 옮길 수는 있어도, 단일 키를 파티션당 상한 너머로 밀어붙일 수는 없습니다. 진짜 유명인 키는 여전히 스로틀됩니다. 그 벽을 넘게 해주는 것이 샤딩입니다.

바쁜 키에서 스로틀이 보일 때의 결정 경로는 다음과 같습니다:

아니오, 같은 접두사를가진 여러아니오 키에서스로틀링 발생?단일 아이템이너무 뜨거운가?키를 샤딩하거나읽기를 캐싱낮은 카디널리티의파티션 키인가?접두사를쓰기 샤딩적응형 용량이처리할 가능성 높음

대부분의 핫 파티션은 "키를 샤딩하라" 아니면 "적응형 용량이 흡수하게 두라"로 귀결됩니다 — 다이어그램은 그저 당신이 어느 분기에 있는지를 보여줄 뿐입니다.

재설계 전에 진단부터

볼 수 없는 것은 고칠 수 없습니다. 스로틀링은 ProvisionedThroughputExceededException(프로비저닝됨)으로, 또는 CloudWatch의 ThrottledRequests와 파티션별 ThrottleCount로 나타납니다 (AWS — CloudWatch 지표).

여기에 CloudWatch Contributor Insights for DynamoDB를 함께 사용하세요. 가장 많이 액세스되는 키를 직접 순위로 보여주므로 — 유명인 키를 이름으로 확인하는 가장 빠른 방법입니다 (AWS — Contributor Insights).

샤딩된 읽기 경로를 테스트할 때는 각 샤드에 대한 KeyConditionExpression을 손으로 만들게 됩니다. 오타 없이 생성하려면 DynamoDB Expression Builder를 사용하세요 — 샤드별로 정확한 PK = :pk AND begins_with(SK, :sk) 형태를 내보냅니다.

피해야 할 함정

  • 자동 증가 또는 단조 증가 키. 순차 ID와 타임스탬프를 파티션 키로 쓰면 연속된 쓰기가 같은 파티션으로 향합니다. 해시 접두사를 추가하세요.
  • 불필요하게 읽기가 많은 경로를 샤딩하기. 읽기가 지배적이고 아이템이 작다면, 캐시나 더 잘 분산된 키를 가진 GSI가 샤딩의 흩어 모으기 읽기 비용을 이기는 경우가 많습니다.
  • 핫 파티션을 느린 Scan과 혼동하기. Scan은 모든 것을 읽기 때문에 느립니다. 핫 파티션은 하나의 키가 과부하되어 스로틀됩니다. 서로 다른 문제입니다 — Query vs Scan을 참고하세요.

다음 단계

샤딩된 키를 스케치한 다음, 실제 데이터로 읽기 경로를 검증하세요. DynamoDB Expression Builder에서 샤드별 조건을 구성하고, DynoTable을 다운로드하여 자신의 테이블에서 실행하면서 실제로 어느 파티션이 부하를 받는지 지켜보세요.

업데이트됨