대규모 트래픽이 몰리는 이벤트 서비스나 투표 시스템을 운영하다 보면, 충분한 WCU(Write Capacity Units)를 프로비저닝했음에도 불구하고 ProvisionedThroughputExceededException 오류를 마주하게 된다. 이는 전형적인 핫 파티션 문제(Hot Partition Issue)다. AWS 데이터베이스 환경에서 특정 파티션 키(Partition Key)에 요청이 집중되면, 단일 파티션의 물리적 한계(일반적으로 초당 1,000 WCU 또는 3,000 RCU)를 초과하게 되어 병목 현상이 발생한다. 이 글에서는 물리적 한계를 우회하기 위한 NoSQL 모델링 기법인 쓰기 샤딩(Write Sharding) 전략을 실제 코드로 구현한다.
핫 키(Hot Key) 분석과 기존 설계의 한계
DynamoDB는 파티션 키의 해시 값을 기반으로 데이터를 물리적 스토리지에 분산 저장한다. 만약 CandidateA라는 단일 키로 모든 투표 트래픽이 몰린다면, 해당 데이터는 하나의 물리적 파티션에만 저장되므로 분산 처리의 이점을 전혀 누릴 수 없다. 이것이 바로 잘못된 DynamoDB 설계가 초래하는 재앙이다.
이 문제를 해결하기 위해서는 논리적으로는 하나의 키지만, 물리적으로는 여러 파티션에 분산되도록 키를 쪼개야 한다. 이를 위해 접미사(Suffix)를 활용한 쓰기 샤딩 기법을 적용한다.
해결책: 랜덤 접미사를 이용한 쓰기 샤딩 구현
가장 효과적인 방법은 파티션 키 뒤에 난수(Random Number)를 접미사로 붙여 데이터를 강제로 분산시키는 것이다. 예를 들어 CandidateA라는 키 대신 CandidateA#1, CandidateA#2, ... CandidateA#N 형태로 저장한다. 이를 통해 쓰기 작업은 N개의 파티션으로 고르게 분산된다.
아래는 Python과 Boto3를 사용하여 1부터 10까지의 샤딩 범위를 적용한 쓰기 로직이다.
import boto3
import random
from botocore.exceptions import ClientError
# DynamoDB 리소스 초기화
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('VoteTable')
def write_vote(candidate_id, user_id):
"""
쓰기 샤딩을 적용하여 투표 데이터를 저장한다.
candidate_id에 1~10 사이의 랜덤 접미사를 붙여 파티션을 분산시킨다.
"""
sharding_range = 10
suffix = random.randint(1, sharding_range)
# 파티션 키 변형: CandidateA -> CandidateA#3
sharded_pk = f"{candidate_id}#{suffix}"
try:
response = table.put_item(
Item={
'PK': sharded_pk, # 분산된 파티션 키
'SK': f"USER#{user_id}", # 정렬 키 (유니크성 보장)
'VoteCount': 1,
'OriginalPK': candidate_id # 추후 GSI 집계를 위한 원본 키
}
)
return response
except ClientError as e:
print(f"Error putting item: {e}")
raise
# 사용 예시
# 트래픽이 CandidateA#1 ~ CandidateA#10 으로 분산됨
write_vote("CandidateA", "User123")
OriginalPK)를 기준으로 데이터를 모아야 한다면, 해당 속성을 파티션 키로 하는 GSI(Global Secondary Index)를 생성하는 것이 좋다. 다만, GSI 역시 쓰기 시점에 핫 파티션이 될 수 있으므로, 읽기 패턴에 따라 Scatter-Gather(병렬 스캔) 방식을 사용할지 GSI를 사용할지 결정해야 한다.
읽기 패턴: Scatter-Gather 조회
데이터가 분산 저장되었으므로, 총 투표수를 조회하려면 분산된 모든 키를 읽어 합산해야 한다. 이를 Scatter-Gather 패턴이라 부르며, 애플리케이션 레벨에서 병렬로 처리한다.
| 접근 방식 | 설명 | 장점 | 단점 |
|---|---|---|---|
| BatchGetItem | 알고 있는 모든 샤드 키(예: #1~#10)를 한 번에 요청 | 가장 빠르고 비용 효율적 | 샤드 개수가 많으면(100+) 복잡도 증가 |
| Query with GSI | OriginalPK를 GSI 파티션 키로 두고 조회 | 구현이 단순함 | GSI 쓰기 지연 및 GSI 자체의 핫 파티션 가능성 |
Conclusion
DynamoDB에서 대규모 트래픽을 처리할 때 핫 파티션 문제는 피할 수 없는 과제다. 단순히 AWS 인프라 비용을 늘리는 것이 아니라, 데이터 액세스 패턴을 분석하고 쓰기 샤딩을 적용하는 것이 근본적인 해결책이다. 초기 설계 단계에서 예상 트래픽을 산정하고 적절한 샤딩 계수(N)를 설정한다면, 스로틀링 없는 안정적인 서비스를 구축할 수 있다.
Post a Comment