고급6분 분량

DynamoDB 저장 내부 구조의 작동 방식

DynamoDB는 값이 정렬된 트리인 해시 테이블로, 세 가용 영역의 머신에 걸쳐 퍼져 있습니다. 두 자료 구조가 거의 모든 일을 합니다: 파티션 키에 대한 해시가 머신을 고르고, 정렬 키에 대한 B-트리가 그 안에서 아이템을 정렬합니다.

DynamoDB는 데이터를 어떻게 저장하나요?

DynamoDB는 값이 정렬된 B-트리인 거대한 분산 해시 테이블로 데이터를 저장합니다. 파티션 키에 대한 해시가 하나의 스토리지 노드를 선택하고, B-트리가 그 안에서 정렬 키 순으로 항목을 정렬합니다. 모든 쓰기는 응답하기 전에 세 개의 가용 영역에 걸친 세 노드에 동기적으로 복제됩니다.

  • 파티션 키는 검색되는 게 아니라 해싱됩니다. DynamoDB는 여러분의 PK에 해시 함수를 돌려 그 파티션을 담은 하나의 저장 노드를 찾습니다 — 테이블 크기와 무관한 O(1) 점프입니다.
  • 정렬 키는 B-트리에 삽니다. 파티션 안에서 아이템은 SK로 사전식 정렬된 B-트리에 저장됩니다 — 그래서 범위 읽기(begins_with, between)가 값싸고 Scan은 그렇지 않습니다.
  • 모든 쓰기는 확인 응답 전에 세 노드를 거칩니다. 쓰기는 리더로 가고, 리더는 다른 AZ의 두 피어에 동기적으로 복제합니다 — 내구성은 PutItem이 반환되기 전에 값을 치릅니다.
  • 이것이 액세스 패턴 규칙이 존재하는 이유입니다. 해시-그-다음-트리는 키로 읽을 때만 빠릅니다. 키가 없으면 빠른 경로도 없습니다 — 트리를 스캔하는 것으로 떨어집니다.

API가 아니라 자료 구조에서 시작하기

SQL에서 왔다면 테이블을, 쿼리 플래너가 인덱스를 고르는, 디스크 위의 행으로 그립니다. DynamoDB에는 플래너가 없습니다. 저장 배치 자체 가 계약입니다 — 무엇이 빠르고 무엇이 지뢰인지 둘 다 두 구조에서 곧장 떨어져 나옵니다.

거대한 분산 맵을 그려 보세요. 키는 여러분의 파티션 키의 해시입니다. 값은 단일 아이템이 아닙니다 — 그 파티션 키를 공유하는, 정렬 키로 정렬된 아이템들의 통째 B-트리입니다.

그 밖의 모든 것 — 쿼리 의미론, 10 GB 경고, 빠진 키가 Scan을 강제하는 이유 — 은 그 한 문장의 결과입니다.

파티션 키를 해싱해 노드 찾기

요청이 도착하면, DynamoDB는 파티션 키 값에 내부 해시 함수를 적용합니다. 해시는 결정적으로 하나의 저장 노드 — 그 아이템들을 소유하는 물리 파티션 — 에 매핑됩니다. AWS는 이것을 테이블 크기와 무관한 상수 시간 키 조회의 배후 메커니즘으로 문서화합니다.

그게 O(1) 단계입니다. 10 TB 테이블과 10 KB 테이블이 찾는 데 드는 비용은 같습니다: 해시, 점프, 끝. 노드를 찾는 인덱스 스캔도, 통계도, 계획도 없습니다.

함정은 그 이면입니다. 파티션 키를 공급하지 않으면, DynamoDB는 점프할 노드가 없습니다 — 모든 파티션을 걸어야 합니다. 그게 Scan이며, O(1)과 테이블 전체 읽기의 차이입니다.

PutItem / GetItemPK=DEVICE#a91Hash(PK)저장 노드(한 파티션)SK에 대한 B-트리READING#... 정렬됨아이템

요청은 정확히 하나의 파티션으로 해싱된 다음, 그 파티션의 정렬 키 B-트리를 타고 아이템으로 내려갑니다 — 테이블 순회가 아니라 값싼 두 단계입니다.

파티션별 B-트리에서 아이템 정렬하기

단일 파티션 안에서 아이템은 힙이 아닙니다. 정렬 키로 키가 지정된 B-트리에 사전식으로 정렬되어 담깁니다. B-트리 검색은 O(log n)이며, 결정적으로 그 n은 전체 테이블이 아니라 파티션의 아이템 수입니다.

이것이 정렬 키 범위 읽기가 값싼 이유 전부입니다. 각 장치의 측정값이 한 파티션 키 아래에 사는 차량 텔레메트리 테이블을 봅시다:

PKSK
PK = DEVICE#a91SK = READING#2026-06-23T08:00Z
PK = DEVICE#a91SK = READING#2026-06-23T08:05Z
PK = DEVICE#a91SK = READING#2026-06-23T08:10Z

B-트리가 정렬되어 있으므로, "08:00과 09:00 사이의 모든 측정값"은 시작 값으로의 트리 하강에 순차 걷기를 더한 것입니다 — 그 장치가 보낸 모든 측정값에 대한 필터가 아닙니다. 일치하는 범위만 읽습니다.

그 정렬은 또한 begins_with(SK, "READING#2026-06-23") 쿼리가 빠르고 비키 속성에 대한 필터링은 그렇지 않은 이유입니다. 트리는 SK로 탐색할 수 있지만, 다른 무엇으로도 탐색할 수 없습니다. 그 키 조건을 안전하게 구성하려면, 문자열을 손으로 이어 붙이는 대신 DynamoDB 표현식 빌더에서 만드세요:

KeyConditionExpression  PK = :pk AND begins_with(SK, :day)

모든 쓰기를 세 AZ에 복제하기

파티션은 한 머신이 아닙니다. 각각은 세 가용 영역의 세 노드에 걸쳐 복제됩니다 — 2007년 Amazon Dynamo 논문의 복제 모델로 거슬러 올라가는 설계 혈통입니다.

한 노드가 그 파티션의 리더입니다. 쓰기는 리더로 가고, 리더는 로컬에 쓰고 두 피어에 복제합니다. 리더는 노드의 내구성 정족수가 그것을 가진 뒤에 쓰기를 확인 응답합니다 — 그래서 AZ에 걸친 내구성은 여러분의 PutItem이 반환되기 전에 값을 치릅니다.

읽기에는 선택지가 있습니다. 강력한 일관성 읽기는 리더로 가서 최신 커밋된 쓰기를 봅니다. 최종적 일관성 읽기는 세 노드 중 아무거나가 서빙할 수 있으며, 그중 하나는 몇 밀리초 뒤처져 있을 수 있습니다 — 그게 더 값싸고 가용한 읽기를 위해 맞바꾸는 지연입니다.

최종적 일관성강력한 일관성
서빙 주체3 노드 중 아무거나리더 노드만
최신 쓰기를 봄아마도(작은 지연)항상
RCU 비용절반전체
가용성더 높음더 낮음(단일 노드)

같은 비동기 전파 아이디어가 GSI 읽기가 오래될 수 있는 이유입니다 — GSI는 최종적으로 일관적이다를 보세요.

구조에서 규칙을 읽어 내기

거의 모든 DynamoDB "규칙"은 그저 저장 물리학입니다:

  • 항상 파티션 키를 공급하세요. 키가 없으면 해시 타깃도 없습니다 — 전체 맵을 스캔하는 것입니다. 이것이 Query vs Scan의 핵심입니다.
  • 함께 읽는 것을 한 파티션 키 아래에 같이 두세요 — 그래서 단일 해시 + 트리 걷기가 전체 아이템 컬렉션을 반환합니다. 그게 단일 테이블 설계의 토대입니다.
  • 파티션을 한정되게 유지하세요. 한 파티션은 유한한 노드 집합 위의 하나의 B-트리입니다. 폭주하는 핫 키나 LSI의 10 GB 상한은 둘 다 그 물리 파티션의 한계입니다.

해시-그-다음-B-트리 형태를 한번 보고 나면, 액세스 패턴 규율이 자의적으로 느껴지길 멈춥니다 — 여러분은 그저 모든 읽기를 빠른 경로에 두고 있을 뿐입니다.

다음 단계

정렬 키 전략단일 테이블 설계로 구조에 맞게 키를 모델링한 다음, 실제 표현식을 DynamoDB 표현식 빌더에서 조립하세요. DynoTable을 사용해 보세요 — 여러분 자신의 테이블에 대해 이 읽기가 실행되는 것을 지켜보고, 키 조건이 어떤 아이템을 끌어오는지 정확히 확인하세요.

업데이트됨