한계를 경계로 바꾸다: 대규모 커머스 DB 샤딩 전환기
유정민 - 카카오
샤딩 도입 배경과 목표
기존 두 방식으로는 증가하는 컴퓨팅 수요 대응 어려워 샤딩 결정
- 카카오 커머스 성장: 2010 선물하기(위탁 판매) → 2017 쇼핑하기(오픈마켓) → 2019 쇼핑하기 회원 통합 → 2020 쇼핑라이브
- 1차: 스케일업
- 코드·애플리케이션 구조 변경 없이 장비 스펙만 올림. 가장 빠르고 간단, 서비스 초기에 효과적
- 데이터 증가 속도가 인프라 성장 속도를 앞지르면서 한계. 한 테이블에 데이터가 쌓여 쿼리 성능 저하, DDL 수행 시간 증가
- 2차: 핫/콜드 분리 (Hot-Cold Separation)
- 최신 데이터는 핫 DB, 과거 데이터는 콜드 DB. 쓰기는 핫 DB에서만, CDC로 콜드 DB에 실시간 적재, 핫 DB 오래된 데이터는 주기적 삭제, 콜드 DB는 읽기 전용
- 대부분 요청이 핫 DB에서 처리돼 초기 애플리케이션 변경 범위가 작음
- 실시간 트래픽·쓰기 부하는 여전히 핫 DB에 집중, 오래된 데이터 조회 때문에 서비스 로직이 두 DB를 모두 고려해야 함
- 성장할수록 삭제 주기가 짧아지고 조회 지연 등 운영 문제 발생
- 정리: 스케일업은 확장 한계와 비용, 핫/콜드 분리는 남은 병목과 복잡도가 문제
- 샤딩 목표: 용량 확보, 트래픽 분산, 단순하고 유연하게 확장 가능한 구조
샤딩 아키텍처 설계
- 샤드 키 선정
- 후보: 결제번호, 주문번호, 구매자 유저 ID
- 대표 유스케이스 두 가지로 판단: 주문하기(한 결제 안에 복수 주문 생성), 구매 내역 조회(구매자 유저 ID 기준)
- 결제번호·주문번호는 구매 내역 조회를 단일 DB로 처리 못 함 → 두 로직 모두 단일 DB로 처리되는 구매자 유저 ID를 샤드 키로 선정
- 샤딩 방식 비교 (핫스팟 가능성, 구현·운영 난이도, 리샤딩 비용)
- 레인지: 범위 지정으로 부분적으로 계속 샤딩 가능. 최근 데이터에 요청이 몰려 마지막 샤드가 핫스팟이 되기 쉬움
- 모듈러: 샤드 키 % 샤드 수. 구현·라우팅 단순, 고르게 분산돼 핫스팟 가능성 낮음. 샤드 수가 바뀌면 대부분 데이터 재이동
- 컨시스턴트 해싱 링: 노드 추가·제거 시 영향이 인접 샤드로 한정돼 이동량 적음. 구현·라우팅 복잡하고 데이터 위치를 코드만으로 알기 어려움
- 운영 복잡도를 낮게 유지하는 걸 우선해 모듈러 선택, 리샤딩 비용 문제는 논리 샤드로 해결
- 모듈러 + 논리 샤드
- 물리 DB 안의
order 스키마를 여러 스키마로 나눠 각 스키마를 논리 샤드 하나로 취급
- 샤드 키를 모듈러 연산해 논리 샤드에 분배하고, 논리 샤드를 물리 DB에 나눠 배치 (예: 논리 12개를 물리 3대에 4개씩, 물리 4대·6대 구성도 가능)
- 장점: 구현·라우팅 단순, 샤드 키만으로 데이터 위치 파악, 핫스팟 가능성 낮음, 테이블 크기를 작게 유지, 데이터 재분배 없이 물리 DB만 증설 가능
- 단점: 초기에 정한 논리 샤드 수가 확장 상한, 균등 분포를 위해 물리 DB 수가 논리 샤드 수의 약수여야 함
- 샤드 대수 선정: 용량 확보와 샤딩 상태에서의 트래픽 처리를 동시에 만족해야 함
- 용량: 안정적인 DDL/DML을 위해 테이블 크기를 작게 유지하는 게 목표. 논리 샤드 수로 테이블 크기가 나뉘고, 물리 DB 수로 전체 용량이 늘어남 (예: 가장 큰 테이블 2TB를 논리 샤드 10개로 나누면 샤드당 약 200GB)
- 커넥션: 서버가 논리 샤드마다 별도 데이터 소스로 커넥션을 맺어 샤드가 늘면 커넥션도 늘어남. DB Max Used Connections를 확인한 결과 해당 DB 스펙에서 물리 DB 1대당 논리 샤드 2개가 한계
- 트래픽: 판매자 ID 조회는 샤드 키가 아니라 전체 샤드를 조회해야 함. 쿼리당 비용은 줄지만 서버가 모든 DB를 조회하고 병합해 서버 부하 증가
- 판매자 ID 기준 조회 최대 TPS 측정 (기존 핫 DB = 1만 기준 상대치): 물리 12대·논리 24개 = 1만 5천(1.5배), 물리 24대·논리 48개 = 9천(오히려 하락)
- 최종 구성: 물리 DB 12대, 논리 샤드 24개
데이터 쓰기 전략 및 배포 전략
- 서비스를 멈추고 신규 DB로 바꾸면 로직 오류 시 즉시 롤백 불가 (신규 DB 데이터를 구 DB로 다시 적재해야 함) → 무중단 전환과 즉시 롤백을 위해 듀얼 라이트 적용
- 듀얼 라이트 커밋 순서
- ChainedTransactionManager 기본 순서(신규 DB 커밋 → 구 DB 커밋)는 구 DB 커밋이 실패하면 신규 DB에만 데이터가 남음. 전환 기간 기준은 구 DB라 서비스가 보는 데이터가 사라지는 심각한 문제
- 순서를 바꿔 구 DB 먼저, 신규 DB 나중에 커밋. 신규 DB 커밋이 실패해도 구 DB에는 정상 저장되고 서비스는 그대로 동작, 신규 DB 누락은 검증·보정으로 메움
- 같은 듀얼 라이트라도 커밋 순서에 따라 위험이 완전히 달라짐
- 데이터 검증과 보정
- 구 DB·신규 DB의 CDC로 실시간 스트리밍 검증, 주기적으로 배치 검증
- 구 DB에는 신뢰할 수 있는 데이터만 쌓이므로 불일치 알림이 오면 구 DB 기준으로 보정
- 되돌릴 수 있는 순서로 단계적 배포
- 0단계: 구 DB만 읽기·쓰기
- 1단계: 듀얼 라이트 배포, 읽기는 구 DB 유지하며 두 DB 검증. 불일치가 적으면 신규 DB 보정, 많으면 롤백 검토
- 2단계: 검증이 안정되면 읽기를 신규 DB로 전환. 쓰기는 양쪽 유지라 문제 시 읽기만 되돌리면 됨
- 3단계: 구 DB 쓰기 제거. 이 단계부터는 즉시 되돌릴 수 없어 검증 결과를 충분히 확인한 뒤 진행
- 0~2단계는 구 DB에 계속 쓰고 있어 언제든 롤백 가능, 되돌릴 수 없는 시점은 마지막 단계뿐
결과 및 회고
- 목표 3가지 달성
- 용량: 테이블당 크기를 충분히 낮춰 DML/DDL에 유연하게 대응, 10년 이상 용량 확보
- 처리량: 물리 DB 12대로 트래픽 분산, 샤드 키 기준 단건 조회는 12배, 전체 샤드 조회는 기존 대비 1.5배
- 확장성: 모듈러 + 논리 샤드로 데이터 재분배 없이 물리 DB만 증설 가능한 구조
- 서비스 특성 - 샤드 키 선정
- 구매자 유저 ID를 샤드 키로 해서 주요 로직을 단일 DB로 처리
- 구현과 운영 - 모듈러와 논리 샤드
- 용량과 처리량 - 샤드 대수 선정
- 테이블 크기, 커넥션 수, 전체 샤드 조회 성능 측정으로 결정
- 전환 안정성 - 쓰기 및 배포 전략 설계
- 전환 중 문제가 생겨도 보정·롤백할 수 있게 설계
- 샤딩이 항상 정답은 아님. DB를 여러 대로 늘리는 것으로 끝나지 않고 샤드 키·라우팅 설계, 분산 조회와 트랜잭션까지 다뤄야 함 → 더 단순한 방법으로 해결 가능한지 먼저 확인
- 한 대로 충분하면 스케일업
- 읽기 트래픽만 병목이면 리드 레플리카
- 용량이 문제고 과거 데이터 분리로 충분하면 핫/콜드 분리. 단, 신규 데이터와 쓰기 트래픽은 여전히 핫 DB에 집중돼 쓰기 병목은 늦출 뿐 근본 해결은 어려움
- 카카오 커머스는 오래된 주문도 계속 조회해야 하고 신규 주문·쓰기 트래픽도 계속 증가 → 개발·전환·운영 비용을 감수하고 샤딩 선택