글로벌 보조 인덱스 (GSI, Global Secondary Index) 분산 환경 오버헤드

핵심 인사이트 (3줄 요약)

  1. 본질: 글로벌 보조 인덱스(GSI, Global Secondary Index)는 NoSQL 데이터베이스(예: DynamoDB, Cassandra)에서 메인 파티션 키(PK)가 아닌 다른 속성으로 데이터를 전역적으로 빠르게 검색하기 위해 데이터 자체를 재정렬하여 별도의 파티션에 비동기 복제해 두는 메커니즘이다.
  2. 가치: NoSQL은 PK 이외의 조건으로 검색하면 모든 샤드(노드)를 뒤져야 하는 비효율적인 전체 스캔(Full Scan / Scatter-Gather)이 발생하는데, GSI는 원하는 검색 조건(Secondary Key)을 기준으로 데이터를 모아두어 O(1) 수준의 빠른 읽기 성능을 제공한다.
  3. 융합: 읽기 성능은 획기적으로 개선되지만, 원본 테이블에 쓰기(Write)가 발생할 때마다 비동기적으로 GSI 테이블까지 업데이트해야 하므로 쓰기 지연(Write Overhead), 스토리지 비용 증가, 원본과 인덱스 간의 최종 일관성(Eventual Consistency) 감수라는 명확한 트레이드오프(Trade-off)가 수반된다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

  • 개념: GSI는 베이스 테이블(원본)의 파티션 키와는 완전히 다른 파티션 키와 정렬 키를 갖는 독립적인 인덱스(사실상 또 하나의 테이블)다. '글로벌(Global)'이라는 이름이 붙은 이유는 베이스 테이블의 데이터 파티션 경계를 무시하고 시스템 전체(글로벌) 데이터를 새로운 키 기준으로 재정렬하기 때문이다. 반대로 베이스 테이블과 파티션 키를 공유하는 인덱스는 LSI(Local Secondary Index)라 부른다.

  • 필요성: 관계형 데이터베이스(RDBMS)에서는 인덱스를 걸면 B-Tree 구조의 포인터만 추가되어 비교적 저렴하게 다양한 검색을 지원한다. 하지만 수백 대의 서버로 데이터가 분할(Sharding)된 NoSQL 환경에서는 포인터만으로 인덱스를 구현할 수 없다. 특정 필드로 검색하기 위해 수백 대의 서버에 분산 질의를 날리는 것(Scatter-Gather)은 네트워크 대역폭을 심각하게 낭비한다. 따라서 "검색하려는 키 기준으로 데이터 사본을 아예 한곳에 몰아놓는" GSI 방식이 필수적이다.

  • 💡 비유: 전국 도서관(NoSQL)의 책들이 '장르(PK)' 기준으로 각 지역에 흩어져 보관되고 있다고 상상해보자. 만약 어떤 손님이 '저자명'으로 책을 찾으려면 전국 도서관에 일일이 전화(전체 스캔)를 걸어야 한다. 이 문제를 해결하기 위해, 원래 책은 그대로 두되 '저자명(GSI)' 기준으로 책의 사본(또는 요약본)을 복사해 새로운 도서관 창고를 하나 더 만드는 것이 바로 GSI다.

  • 등장 배경 및 발전 과정:

    1. 초기 NoSQL의 한계: Amazon DynamoDB 초창기 모델은 오직 PK 단위의 Key-Value 조회만 허용했다. 복잡한 쿼리가 불가능해 비즈니스 로직 구현에 제약이 컸다.
    2. GSI의 도입: RDBMS의 유연성과 NoSQL의 확장성 사이의 타협점으로 GSI가 도입되었다. 데이터를 아예 중복 저장하여 읽기 속도를 극한으로 끌어올리되 일관성을 양보했다.
    3. 현대 아키텍처의 필수 요소: 현재 DynamoDB, Cassandra(Materialized View), Spanner 등 대부분의 분산 DB는 성능 저하 없는 다차원 검색을 위해 GSI 개념을 핵심 스토리지 엔진에 내재화하여 제공한다.
  • 📢 섹션 요약 비유: 두꺼운 전공서적 앞에는 '목차(PK)'가 있지만, 뒤에는 '찾아보기/색인(GSI)'이 따로 붙어 있죠? 색인을 새로 만드는 과정(쓰기 오버헤드)은 번거롭지만, 덕분에 독자(읽기)는 원하는 단어를 순식간에 찾을 수 있습니다.


Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

구성 요소

요소명역할내부 동작비유
베이스 테이블 (Base Table)원본 데이터 저장소고유한 파티션 키(PK) 기반 물리적 해싱 저장도서관의 원본 책 창고 (장르별 분류)
GSI 테이블검색에 최적화된 복제 데이터 저장소새로운 파티션 키(GSI PK)를 기준으로 물리적 재해싱저자명 기준으로 정리된 요약본 창고
투영 (Projection)원본 데이터 중 GSI로 복사할 속성 선택KEYS_ONLY, INCLUDE, ALL 등 복사 범위 결정책에서 '제목'과 '위치'만 베껴 적기
비동기 복제기 (Replicator)베이스 테이블 변경을 GSI에 반영백그라운드 프로세스로 큐잉 및 업데이트 수행변경 사항을 새 창고에 적어 넣는 야간 알바생

GSI의 동작 원리와 물리적 분산 메커니즘

GSI는 논리적인 인덱스(포인터)가 아니라, 베이스 테이블의 데이터(전부 또는 일부)를 물리적으로 완전히 다른 노드(서버)에 복사하여 재정렬하는 **물리적 뷰(Materialized View)**다.

  ┌───────────────────────────────────────────────────────────────┐
  │         베이스 테이블과 GSI (Global Secondary Index)의 구조 차이 │
  ├───────────────────────────────────────────────────────────────┤
  │                                                               │
  │  [베이스 테이블 - PK: 부서명(Dept), SK: 사번(EmpID)]             │
  │  (노드 A) Dept: 영업부                                           │
  │    ├─ EmpID: 101 | Name: 김철수 | Age: 30                       │
  │    └─ EmpID: 105 | Name: 박영희 | Age: 25                       │
  │                                                               │
  │  (노드 B) Dept: 개발부                                           │
  │    └─ EmpID: 102 | Name: 이민수 | Age: 28                       │
  │                                                               │
  │  [쿼리 1] "나이가 28살인 사람 찾아줘!"                             │
  │   ▶ GSI가 없다면? 노드 A, B를 모두 뒤져야 함. (Scatter-Gather 오버헤드)│
  │                                                               │
  │                           ▼ 비동기 투영 (Projection)           │
  │                                                               │
  │  [GSI 테이블 - GSI PK: 나이(Age), SK: 사번(EmpID)]               │
  │  (노드 C) Age: 25                                                │
  │    └─ EmpID: 105 | Dept: 영업부 | Name: 박영희                    │
  │                                                               │
  │  (노드 D) Age: 28                                                │
  │    └─ EmpID: 102 | Dept: 개발부 | Name: 이민수                    │
  │                                                               │
  │  (노드 E) Age: 30                                                │
  │    └─ EmpID: 101 | Dept: 영업부 | Name: 김철수                    │
  │                                                               │
  │  [쿼리 2] "나이가 28살인 사람 찾아줘!"                             │
  │   ▶ GSI 조회: Age=28의 해시값을 갖는 노드 D에만 즉시 1번 쿼리! (속도 극대화)│
  └───────────────────────────────────────────────────────────────┘

[다이어그램 해설] 베이스 테이블은 '부서명' 기준으로 데이터가 파티셔닝되어 있다. 부서명을 모른 채 '나이'만으로 사람을 찾으려면 전체 노드를 훑어보는 악몽이 발생한다. 이를 위해 '나이'를 새로운 파티션 키(GSI PK)로 삼는 GSI 테이블을 만들면, 데이터는 나이 해시값 기준으로 완전히 다른 노드 구조(C, D, E)에 재배치된다. 이제 나이로 검색할 때는 GSI 테이블의 단일 파티션에만 접근하여 O(1) 성능으로 응답을 받아낼 수 있다. 데이터의 복사 과정에서 전체 속성을 다 복사할지(ALL), 특정 속성만 복사할지(INCLUDE), 아니면 PK만 복사할지(KEYS_ONLY) 결정하는 것을 투영(Projection)이라 하며, 이는 스토리지 비용과 직결된다.


Ⅲ. 실무 적용 및 기술사적 판단

쓰기 오버헤드(Write Overhead)와 트레이드오프 분석

GSI의 최대 맹점은 쓰기 성능의 저하다. 베이스 테이블에 레코드가 1개 쓰일 때마다 내부적으로 다음과 같은 연쇄 반응이 발생한다.

  1. 쓰기 증폭 (Write Amplification): 베이스 테이블 INSERT + GSI 1 INSERT (인덱스가 5개면 총 6번의 쓰기가 발생한다). 특히 UPDATE 시에 이전 인덱스 값을 삭제하고 새 인덱스를 추가해야 하므로 오버헤드는 더 커진다.
  2. 쓰기 스로틀링 (Write Throttling) 문제: DynamoDB의 경우, GSI의 쓰기 용량(WCU)이 부족하면 GSI 업데이트만 실패하는 것이 아니라 베이스 테이블의 쓰기까지 덩달아 거부(Throttled)되는 백프레셔(Backpressure) 현상이 발생한다. 즉, 보조 시스템의 장애가 메인 비즈니스를 마비시킬 수 있다.

실무 시나리오

  1. 시나리오 — GSI 핫 파티션 (Hot Partition) 장애: 사용자 이벤트 로그를 DynamoDB에 저장하고 있다(PK: UserID). 관리자 대시보드에서 '오늘 발생한 오류 건수'를 빠르게 보려고 GSI PK를 '날짜(Date)'로 잡아버린 상황.

    • 판단: 완벽한 설계 오류다. 날짜 단위로 GSI를 구성하면 특정 날짜(오늘)에 해당하는 단일 물리 노드(파티션)에 모든 트래픽이 집중되어 쓰기 스로틀링이 즉각 발생한다.
    • 해결책: GSI 파티션 키 설계 시 분산도가 낮은 속성(예: 상태값, 날짜, 성별)을 사용하면 안 된다. 분산시키기 위해 Date_01, Date_02 처럼 해시값을 강제로 덧붙이는 쓰기 샤딩 (Write Sharding) 기법을 사용하거나, 날짜 조회용 시계열 DB를 별도로 구축해야 한다.
  2. 시나리오 — 최종 일관성(Eventual Consistency)으로 인한 혼란: 고객이 배송 주소를 '서울'에서 '부산'으로 수정(Update)하고 즉시 검색창에 '부산'으로 주소 검색을 했는데 자신의 주문이 나오지 않는 상황.

    • 판단: 베이스 테이블 업데이트 완료 후 백그라운드에서 GSI 로 데이터가 복제되기까지는 약간의 시간(통상 수십 밀리초~수 초)이 소요된다. GSI는 구조적으로 강한 일관성(Strong Consistency) 읽기를 지원할 수 없다.
    • 해결책: 즉각적인 조회가 필요한 영역(결제, 재고 확인 등)은 베이스 테이블을 직접 조회하도록 아키텍처를 강제하고, GSI를 통한 조회는 화면에 '업데이트 반영 중'이라는 문구를 띄우거나 프론트엔드의 로컬 상태(Optimistic UI)를 우선 보여주는 방식으로 UX 한계를 우회해야 한다.

도입 체크리스트

  • 비즈니스적: 추가하고자 하는 검색 조건이 스토리지 비용과 쓰기 성능 하락을 감수할 만큼 매우 빈번하게 호출되는가? (간헐적인 분석용 쿼리라면 GSI 대신 데이터 레이크 기반의 OLAP 분석 도구를 연동하는 것이 타당하다).
  • 아키텍처적: GSI 생성 시 투영(Projection) 속성을 최소화(INCLUDE)하여 스토리지 비용과 네트워크 복제 페이로드를 극한으로 줄였는가?

Ⅳ. 기대효과 및 결론

정량/정성 기대효과

구분전체 스캔 (Scan) 검색GSI 기반 질의 (Query)개선 효과
정량 (속도)데이터 크기에 비례하여 조회 느려짐 (O(N))해시 키 기반 즉시 접근 (O(1))복잡한 조건 검색 응답 속도 수백 배 향상
정량 (비용)수십 노드의 컴퓨팅/네트워크 자원 소모GSI 스토리지 비용만 추가 부담막대한 읽기 비용 대신 통제 가능한 저장 비용 지불
정성복잡한 쿼리 구현 불가로 비즈니스 요건 지연유연한 멀티 디멘전 검색 지원RDBMS 수준의 쿼리 유연성을 NoSQL에서 확보

글로벌 보조 인덱스(GSI)는 NoSQL의 한계였던 '검색의 경직성'을 허물고 대규모 분산 데이터베이스를 범용적으로 사용할 수 있게 만든 아키텍처의 꽃이다. 기술사는 단순히 '인덱스 추가'라는 개발자의 관점을 넘어, GSI 추가가 시스템의 쓰기 증폭(Write Amplification)과 핫 파티션 문제로 어떻게 파생될 수 있는지 꿰뚫어 보고, 프로젝션 튜닝과 파티션 키 설계를 리드하는 용량 산정(Capacity Planning) 역량을 발휘해야 한다.


📌 관련 개념 맵 (Knowledge Graph)

개념 명칭관계 및 시너지 설명
파티셔닝 (Partitioning/Sharding)NoSQL에서 데이터를 물리적으로 분산시키는 기준이며, GSI는 이 파티셔닝 기준을 하나 더 만들어 데이터를 완벽히 재분산하는 기법이다.
전체 스캔 (Scatter-Gather)GSI가 없을 때 전체 노드에 쿼리를 던지고 취합해야 하는 값비싼 분산 쿼리 패턴으로, GSI가 타파하고자 하는 핵심 문제다.
최종 일관성 (Eventual Consistency)베이스 테이블과 GSI 간의 동기화는 비동기로 이루어지므로, 사용자는 두 데이터 간의 일시적 불일치를 항상 감수해야 한다.
CQRS (명령 조회 책임 분리)GSI의 철학 자체가 쓰기 모델(베이스)과 다양한 읽기 모델(인덱스 테이블)을 물리적으로 분리하는 CQRS 패턴의 마이크로 버전이라 할 수 있다.
핫 파티션 (Hot Partition)GSI의 키를 카디널리티(Cardinality)가 낮은 속성(성별, 상태값 등)으로 잡으면 특정 노드로 트래픽이 몰려 시스템이 붕괴되는 심각한 안티패턴이다.

👶 어린이를 위한 3줄 비유 설명

  1. 전국에 흩어진 수백 개의 레고 상자가 '색깔'별로 정리되어 있다고 해보세요. 만약 '바퀴 모양' 레고만 찾고 싶다면 모든 상자를 다 뒤져야 하니 너무 힘들겠죠?
  2. 그래서 원래 레고들은 그대로 두고, 창고를 하나 더 만들어서 아예 '모양'별로 레고들을 똑같이 하나씩 복사해서 정리해 두는 거예요.
  3. 복사해서 창고를 새로 만드는 건 귀찮고 돈도 들지만, 다음부터 '바퀴 모양'을 찾을 때는 새로 만든 창고만 쏙 열어보면 1초 만에 찾을 수 있는 엄청난 장법이 바로 GSI랍니다!