Amazon DynamoDB 는 완전관리형 서버리스 NoSQL 키-값·문서 데이터베이스다. 본질은 OLTP 용이지만, 데이터 엔지니어는 두 가지 이유로 알아야 한다. 운영 데이터의 원천으로서 어떻게 꺼내 올 것인가, 그리고 파이프라인의 상태 저장소·조회 스토어로서 어떻게 설계할 것인가.
데이터는 테이블 → 아이템(행) → 속성(컬럼) 으로 구성된다. 아이템은 기본 키로 식별된다.
파티션 키(PK): 해시되어 아이템이 저장될 물리 파티션을 정한다.
정렬 키(SK): 같은 파티션 키 안에서 아이템을 정렬한다. PK + SK 조합이 복합 기본 키다.
PK = USER#1001 SK = ORDER#2026-06-26#A amount=120PK = USER#1001 SK = ORDER#2026-06-26#B amount=80PK = USER#1001 SK = PROFILE name="Kim"
키 외의 속성은 스키마가 없다. 아이템마다 속성이 달라도 된다. 아이템 하나의 크기에는 상한(400KB)이 있다.
같은 PK 를 가진 아이템들의 묶음을 아이템 컬렉션이라 한다. 위 예에서 USER#1001 의 주문과 프로필이 한 컬렉션이고, PK 하나로 한 번에 가져온다.
Scan 의 FilterExpression 은 읽은 뒤에 버리는 필터다. 결과가 10건이어도 테이블 전체의 읽기 용량이 나간다. "필터를 걸었는데 왜 비싼가"의 답이다.
Query·Scan 은 한 번에 최대 1MB 까지 돌려주고, 그 이상은 페이지를 이어 받는다.
💡 관련 이론: DynamoDB 는 접근 패턴을 먼저 정하고 키를 설계한다(access-pattern-first). 관계형 DB 는 스키마를 정규화해 두고 쿼리를 나중에 짜지만, DynamoDB 는 "어떤 키로 무엇을 꺼낼 것인가"의 목록이 곧 스키마다. 목록에 없던 질문(임의 컬럼 집계, 조인)은 Scan 으로밖에 답할 수 없고, 그것이 분석을 밖으로 내보내는 이유다.
테이블의 파티션 하나는 초당 읽기 3,000단위·쓰기 1,000단위까지 내도록 설계되어 있다. 테이블 전체 용량이 아무리 커도 한 파티션 키에 몰린 트래픽은 이 상한에 걸린다.
고른 분포: PK 는 값의 종류가 많고 트래픽이 고르게 퍼지는 속성(userId, deviceId)을 고른다. 날짜·상태·국가처럼 값이 적거나 쏠리는 속성은 나쁜 PK 다.
쓰기 샤딩: 어쩔 수 없이 쏠리는 키에는 접미사를 붙여 여러 파티션으로 나눈다. 읽을 때는 모든 접미사를 조회해 합쳐야 한다.
단일 테이블 설계: 여러 엔티티를 한 테이블에 넣고 PK/SK 접두사로 구분한다. 조인이 없는 대신 관련 데이터를 한 번의 Query 로 가져온다.
적응형 용량(adaptive capacity)이 쏠린 파티션에 여유 용량을 돌려주지만, 파티션당 상한 자체를 넘겨 주지는 못한다.
-- 안티패턴: 시계열에서 날짜만 PK 로 쓰면 그날 트래픽이 한 파티션에 집중PK = 2026-06-26-- 쓰기 샤딩: 접미사로 N개 파티션에 분산 (읽을 때는 0~N-1 을 모두 조회)PK = 2026-06-26#03-- 더 흔한 해법: 장치 ID 를 PK 로, 시각을 SK 로PK = DEVICE#8821 SK = 2026-06-26T10:15:00Z
-- GSI 예: 상태별 주문 조회GSI: PK = status (SHIPPED/PENDING), SK = order_date→ "PENDING 주문을 날짜순으로" 같은 새 접근 패턴 지원
테이블에 쓰면 GSI 가 비동기로 따라온다. 보통 1초 안이지만 방금 쓴 값이 GSI 조회에 안 보일 수 있다.
GSI 의 쓰기 용량이 부족하면 기본 테이블의 쓰기까지 스로틀된다. 프로비저닝 모드에서 GSI 쓰기 용량은 테이블 이상으로 잡는다. "테이블 용량은 남는데 쓰기가 스로틀된다"의 흔한 원인이다.
프로젝션: 인덱스에 복사할 속성을 고른다. KEYS_ONLY(키만), INCLUDE(지정한 속성 추가), ALL(전부). GSI 조회는 프로젝션되지 않은 속성을 가져오지 못하고, ALL 은 저장량과 쓰기 비용을 키운다.
희소 인덱스: 인덱스 키 속성이 없는 아이템은 인덱스에 들어가지 않는다. "처리 대기 중인 것만" 같은 부분 집합을 싸게 조회하는 기법이다.
위 예처럼 status 를 GSI 의 PK 로 쓰면 GSI 자체가 핫 파티션이 된다. 인덱스도 테이블과 같은 분포 규칙을 따른다.
💡 관련 이론: GSI 는 "미리 알고 있는 조회 축을 하나 더 만드는" 도구이지 분석 도구가 아니다. 집계 축이 늘 때마다 GSI 를 추가하면 쓰기마다 인덱스 수만큼 비용이 붙고, 그래도 SUM·GROUP BY 는 여전히 못 한다. 조회 축이 계속 바뀌는 질문은 컬럼형 분석 스토어의 일이다.
운영(OLTP): DynamoDB → 변경 캡처(Streams/Kinesis) 또는 스냅샷(Export) → 분석(OLAP): S3 + Athena / Redshift
이것은 CQRS(명령-조회 책임 분리)의 데이터 엔지니어링 버전이다. 쓰기와 키 조회는 DynamoDB, 분석과 집계는 컬럼형 스토어로 나눠 각자 잘하는 일을 시킨다. 둘 사이를 잇는 방법이 실시간이면 스트림, 주기적이면 export, 관리형으로 맡기려면 제로 ETL 이다.