DynamoDB 물리 파티션
물리 파티션은 DynamoDB가 여러분의 데이터를 실제로 저장하는 단위입니다: 가용 영역에 걸쳐 복제된 SSD의 한 조각으로, 여러분의 키 공간 한 조각을 담습니다. 테이블은 논리적인 것입니다. 파티션은 바이트 — 그리고 처리량 한계 — 가 진짜로 사는 곳입니다.
DynamoDB 파티션은 어떻게 작동하나요?
DynamoDB는 테이블을 물리 파티션에 걸쳐 저장합니다 — 가용 영역에 걸쳐 복제된 SSD 조각들입니다. 각 파티션은 저장 용량 ~10 GB, 초당 3,000 읽기 단위, 초당 1,000 쓰기 단위에서 상한이 걸립니다. 파티션 키의 해시가 아이템이 어느 파티션에 안착할지 결정하며, DynamoDB는 파티션이 커지거나 핫해지면 자동으로 분할합니다.
- 모든 파티션은 저장 용량 ~10 GB, 초당 3,000 읽기 단위, 초당 1,000 쓰기 단위에서 상한이 걸립니다. 그 천장은 테이블당이 아니라 파티션당입니다.
- 파티션 키의 해시가 파티션을 고릅니다. 같은 키를 가진 아이템은 함께 안착합니다 — 핫 키 하나는 핫 파티션 하나를 뜻합니다.
- DynamoDB가 파티션을 알아서 분할합니다 — 크기에 따라, 그리고 지속되는 열에 따라 — 하지만 모든 트래픽을 한 곳으로 깔때기처럼 모는 키는 고칠 수 없습니다.
- 용량을 남기고도 스로틀되는 것이 신호입니다. 테이블이 5% 사용률에 머무는데도 뜨는
ProvisionedThroughputExceeded오류는 단일 파티션이 한계에 다다랐다는 뜻입니다.
아이템이 파티션을 찾는 방법
DynamoDB는 여러분의 파티션 키 값을 내부 해시 함수에 넣습니다. 해시 출력이 물리 파티션을 고릅니다. 같은 키가 들어가면 같은 파티션이 나옵니다 — 매번요.
SQL에서 왔다면 대응물이 없습니다. 튜닝하는 인덱스 B-트리도, 손으로 배정하는 샤드 키도 없습니다. 배치는 여러분이 제어하지도 보지도 못하는 해시입니다.
파티션 키를 공유하는 아이템은 아이템 컬렉션을 이루며, 함께 저장되고 정렬 키로 정렬됩니다.
그게 한 키에 대한 Query를 값싸게 만드는 것입니다 — 한 파티션의 연속된 한 줄을 읽습니다.
(Query vs Scan 참고.)
게임의 매치 이벤트 저장소를 봅시다. 테이블 키는 arenaId(파티션)와 eventKey(정렬)입니다:
# Item
arenaId = "ARENA#7f3a"
eventKey = "EVT#1719100800#a91c"
playerTag = "Nightjar"
dmgDealt = 412
아레나 7f3a의 모든 이벤트는 같은 파티션으로 해싱되고 정렬 키 순서로 쌓입니다. "이 매치의
타임라인 읽기"에는 훌륭합니다. 그 한 아레나가 모든 트래픽을 받으면 부담입니다.
모든 파티션이 강제하는 세 가지 천장
단일 파티션은 최대 다음을 제공하도록 설계됩니다:
| 한계 | 파티션당 | 계산 방식 |
|---|---|---|
| 저장 용량 | ~10 GB | 원시 아이템 바이트 |
| 읽기 용량 | 초당 3,000 읽기 단위 | 1 RU = 4 KB 강력한 일관성 읽기 한 번 |
| 쓰기 용량 | 초당 1,000 쓰기 단위 | 1 WU = 1 KB 쓰기 한 번 |
출처: AWS 파티션 키 설계 모범 사례 가이드.
아이템 크기가 계산을 키웁니다. 20 KB 아이템은 강력한 일관성 읽기당 5 읽기 단위가 드므로, 한 파티션은 스로틀되기 전에 그런 읽기를 초당 ~600회 — 3,000회가 아니라 — 서빙합니다. 쓰기 비용은 1 KB당, 읽기 비용은 4 KB당 올림합니다.
함정: 이것들은 파티션 한계지 테이블 한계가 아닙니다. 테이블이 40,000 WCU로 프로비저닝되어 있어도 스로틀될 수 있습니다 — 모든 쓰기가 1,000에서 한계인 한 파티션을 두드리고 있기 때문입니다.
파티션이 분할되는 방법
DynamoDB는 두 경우에 파티션을 자동으로 추가합니다. 여러분은 명령을 실행하지 않습니다.
크기로 분할. 파티션이 ~10 GB를 향해 차오르면, DynamoDB는 그 키 범위를 둘로 나누고 아이템의 절반을 새 파티션으로 옮깁니다. 저장 용량은 투명하게 늘어나고, 그동안 읽기와 쓰기는 계속 작동합니다.
열로 분할. 파티션이 처리량 천장 근처의 지속되는 트래픽을 받으면, DynamoDB는 핫 키 범위를 분할해 각 절반이 자기 파티션에 안착하게 합니다. AWS는 이를 split-for-heat 메커니즘이라 부릅니다. 스스로 멈추는 짧은 스로틀 버스트는 보통 방금 분할이 일어났다는 뜻입니다.
분할은 여러 키에 걸쳐 여유를 사들이지만, 맨 아래 노드가 함정입니다: 분할은 키 범위 를 나누지, 단일 키를 절대 나누지 않습니다.
핫 키가 분할기를 이기는 이유
여기가 지뢰입니다. 분할은 파티션 키의 범위 를 재분배합니다. 트래픽이 한 키 값에 집중되면, 모든 요청이 같은 파티션으로 해싱되고, 나눌 범위가 남아 있지 않습니다.
아레나 7f3a가 다른 모든 아레나는 노는 동안 초당 4,000 쓰기를 끌어모으는 토너먼트 결승이라면,
여러분은 1,000에서 스로틀됩니다 — 그리고 분할은 도울 수 없습니다, 그 4,000 전부가 한 키를
공유하니까요. 더 새로운 KeyRangeThroughputExceeded 스로틀 사유는 정확히 이것을 가리킵니다:
테이블이 아니라 한 파티션의 키 범위가 한계를 넘었다는 것.
해법은 용량 슬라이더가 아니라 데이터 모델에 있습니다. 핫 키를 쓰기 샤딩하세요: 작은 접미사를 붙여 하나의 논리적 아레나가 N개의 물리 파티션에 걸쳐 퍼지게 합니다.
arenaId = "ARENA#7f3a#3" # shard 0..9, chosen per write그러면 읽기는 샤드에 걸쳐 부채꼴로 펼쳐지고 클라이언트 측에서 병합됩니다. 애플리케이션 코드를
한 줄도 건드리기 전에 키 형태와 각 샤드에 대한 Query를
DynamoDB 표현식 빌더에서 시제품으로 만들어 볼 수 있습니다.
한 가지 미묘함: LSI 예외
저장 용량이 정말로 파티션 키별로 상한이 걸리는 한 경우가 있습니다. Local Secondary Index가 없으면, 아이템 컬렉션은 필요한 만큼 많은 파티션에 걸쳐 자동 분할됩니다 — 수십억 개의 정렬 키 값도 괜찮습니다.
LSI를 추가하면, 한 파티션 키에 대한 컬렉션 전체가 단일 10 GB 파티션에 들어가야 합니다, LSI가 그것을 공유하기 때문입니다. 그게 GSI vs LSI에서 다루는 PK당 절벽이며 — 대부분의 팀이 GSI에 손을 뻗는 또 하나의 이유입니다.
파티션이 시원하게 유지되도록 설계하기
여러분이 실제로 제어하는 레버는 파티션 키입니다. 행 수 대비 서로 다른 값이 많은 것을 골라 트래픽이 고르게 퍼지게 하세요. (더 많은 패턴은 단일 테이블 설계에 있습니다.)
- 고카디널리티 키. 사용자별이나 테넌트별 키가, 모두가 동시에 두드리는 날짜별이나 상태별 키를 이깁니다.
- 알려진 핫 키를 경계하세요. "현재 토너먼트"나 "오늘" 값은 출시 후가 아니라 출시 전의 집중 위험입니다.
- 피할 수 없는 핫 키는 샤딩하세요. 한 키가 반드시 과도한 트래픽을 받아야 할 때, 접미사가 표준 탈출구입니다.
용량을 남기고도 스로틀되는 것은 한 파티션이 핫하다는 신호입니다. 문제의 아이템을 점검하고 샤딩된 키 배치를 DynoTable에서 연습하세요 — 그것을 여러분 자신의 테이블에 겨누고, 과부하된 키를 찾고, 그것이 여러분을 호출하기 전에 해법을 모델링하세요.