#Backend

DynamoDB 핫 파티션을 해결하는 3가지 방법 (3): 조회를 인덱스 테이블로 옮기기

DynamoDB 핫 파티션을 해결하는 3가지 방법 (3): 조회를 인덱스 테이블로 옮기기
01

Summary

DynamoDB GSI를 걷어내고 핫 파티션 지옥에서 완전히 탈출하는 방법

공통 라이브러리 설계로 GSI 없이 복잡한 키 변환부터 무한 루프 없는 페이지네이션까지 완벽히 재현한 채널톡의 마이그레이션 비하인드

GSI의 핫 파티션 문제를 해결하기 위해 인덱스 테이블로의 완전한 이전을 달성한 여정의 최종장입니다. 단순히 읽기 경로를 바꾸는 것에 그치지 않고, DynamoDB 내부에서 처리해주던 복잡한 쿼리 메커니즘을 공통 라이브러리로 추상화한 기술적 해법을 소개합니다. 이로써 서비스 안정성을 확보함과 동시에 어노테이션 단 한 줄로 다른 인덱스도 이관할 수 있는 확장성을 얻었습니다.

  • 01GSI와 인덱스 테이블 조회 로직의 차이를 사내 라이브러리로 추상화하여 조회 코드 일관성 유지
  • 0213자리 epoch 타임스탬프 합성 키를 활용하여 문자열 환경에서도 대소 관계 및 범위 검색 성립
  • 03eq 조건을 begins_with와 이스케이프로 변환하여 정밀한 키 조회를 라이브러리 단에서 보장
  • 04Limit 적용 후 Filter가 실행되는 DynamoDB의 메커니즘 특성을 'remaining 루프' 구현으로 해결
  • 05네트워크를 타고 돌아온 비신뢰성 커서(LastEvaluatedKey)에 대해 엄격한 검증 및 전진 여부 판단 로직 도입

RECOMMENDATION

DynamoDB GSI의 처리량 한계나 핫 파티션 장애를 겪고 있어 별도 테이블 분리를 고민 중인 백엔드 엔지니어에게 적극 추천합니다. 특히 조회 시 페이지네이션 경계 처리나 소프트 딜리트 필터링 등 NoSQL 마이그레이션 시 밟기 쉬운 함정들에 대한 실무적 가이드가 매우 유용합니다.

The Problem

기존 GSI를 별도 인덱스 테이블로 분리해 쓰기 병목은 해결했으나, 조회(읽기) 코드가 여전히 GSI를 바라보고 있어 GSI를 물리적으로 제거할 수 없고 핫 파티션 위험이 잔존하는 문제가 있었습니다. 또한 GSI가 내부적으로 처리해 주던 키 파싱, 일치 검색, 삭제 필터, 페이지네이션 등의 복잡한 조작을 개별 조회처에서 각자 구현해야 하는 부담이 있었습니다.

The Solution

GSI와 인덱스 테이블 조회의 차이를 추상화하는 사내 공통 라이브러리를 구축하여, 합성 정렬 키 변환, begins_with를 활용한 일치 검색, 소프트 딜리트 자동 필터링, 그리고 'LastEvaluatedKey'를 이용한 페이지네이션 전진 루프(remaining 루프)를 라이브러리 내부로 은닉했습니다.

The Result

조회 경로를 완전히 인덱스 테이블로 안전하게 전환하여 핫 파티션의 근원이었던 GSI를 물리적으로 삭제할 수 있게 되었습니다. 더불어, 향후 다른 GSI 마이그레이션 시에는 비즈니스 코드 수정 없이 어노테이션 변경만으로 즉시 적용할 수 있는 공통 인프라를 확보했습니다.

Trade-off

소프트 딜리트 방식을 적용함에 따라 쿼리 시점에 삭제 표시된 tombstone 데이터를 읽기 필터로 걸러내는 과정이 필요하여, 실제 데이터가 필터링되더라도 RCU(읽기 비용)가 지속적으로 소모되는 비용적 한계가 존재합니다.

03

Key Concepts

Concept · 01

LastEvaluatedKey

DynamoDB에서 대용량 데이터를 페이지네이션 형태로 조회할 때, 마지막으로 읽은 위치를 표시하여 다음 조회 시점의 시작 위치를 알려주는 토큰 역할의 메타데이터입니다.

  • 네트워크를 타고 들어온 커서(LastEvaluatedKey)의 위조 및 오작동을 막기 위해 화이트리스트 필터링과 디코딩 검증을 수행했습니다.
  • 무한 루프를 방지하기 위해 이전 커서 위치와 현재 커서 위치를 비교하여 물리적 전진 여부를 확인하는 방식을 도입했습니다.
Concept · 02

Remaining Loop (잔여 건수 루프)

DynamoDB의 Limit 옵션이 필터(FilterExpression) 적용 전에 먼저 실행되어 페이지 크기가 불규칙하게 줄어드는 현상을 방지하기 위해, 요청한 타겟 개수가 다 채워지거나 데이터를 끝까지 읽을 때까지 쿼리를 지속적으로 반복하는 루프 메커니즘입니다.

  • tombstone(소프트 딜리트 마킹된 항목)을 필터링하는 과정에서 목록이 비어서 반환되거나 덜 차서 오는 문제를 해결하는 핵심 로직으로 활용되었습니다.
  • 더 읽을 데이터가 존재함을 의미하는 'LastEvaluatedKey의 부재' 여부만을 루프의 엄격한 최종 종료 조건으로 설정하여 무한 루프나 탐색 누락 문제를 방지했습니다.
Concept · 03

합성 정렬 키 (Synthetic Sort Key)

DynamoDB 테이블에서 정렬 키(SK)의 유일성 보장을 위해 여러 비즈니스 식별 필드를 하나의 문자열 필드로 결합한 구조입니다.

  • 단순 Number 값이었던 GSI SK 대신 'managedKey#userId' 형태의 유니크한 합성 문자열 키를 인덱스 테이블의 SK로 설정했습니다.
  • 숫자 데이터의 자릿수(13자리 epoch ms)를 고정함으로써 사전식 문자열 정렬 비교를 통해서도 숫자 범위 검색이 정상적으로 보장되도록 응용했습니다.