22강
샤딩
한 권으로 안 되면 여러 권으로 나눈다 — 다만 나누는 기준이 전부다
먼저 보면 좋은 강의: 지연 시간과 처리량, 데이터베이스 인덱스, 데이터베이스 복제
단계별로 보기
스페이스바로 재생/정지, 좌우 화살표로 단계 이동, R로 처음으로 돌아갑니다.
대기 중1 / 4 단계 — 복제로는 넘을 수 없는 벽
쓰기가 초당 300건 들어옵니다. 원본은 하나뿐이고 한계에 부딪혔습니다. 여기서 사본을 아무리 늘려도 소용이 없습니다. 기록은 원본에만 할 수 있고, 사본이 늘수록 원본은 알려줄 곳이 많아져 오히려 더 바빠집니다.
이 강의를 마치면
- 복제로는 해결되지 않는 쓰기 부하를 샤딩이 어떻게 나누는지 설명한다.
- 키를 기준으로 데이터를 여러 데이터베이스에 나누는 구조를 직접 구성한다.
- 샤드 키 선택이 잘못되면 특정 샤드만 과부하가 되는 이유를 설명한다.
- 샤딩이 가져오는 대가(여러 샤드에 걸친 질의의 어려움)를 판단한다.
복제로는 못 고치는 병
앞 강의에서 사본을 여러 권 만들어 읽기 부하를 나눴습니다. 그런데 이번엔 기록이 초당 5,000건씩 들어옵니다. 원본 장부는 초당 1,000건이 한계입니다.
사본을 100권 만들면 될까요? 안 됩니다. 기록은 원본에만 할 수 있으니까요. 사본을 늘리면 원본은 오히려 100권에 변경 사항을 알려주느라 더 바빠집니다.
약이 병에 안 맞는 겁니다. 복제는 읽기 병의 약이고, 지금 앓는 건 쓰기 병입니다.
그러면 장부를 쪼개자
유일한 방법은 원본 자체를 여러 개로 나누는 것입니다.
고객 성씨 ㄱ~ㅁ → 1번 장부
고객 성씨 ㅂ~ㅅ → 2번 장부
고객 성씨 ㅇ~ㅈ → 3번 장부
고객 성씨 ㅊ~ㅎ → 4번 장부각 장부는 자기 몫에 대해서는 완전한 원본입니다. 사본이 아닙니다. 1번 장부에 기록하는 동안 2번 장부에도 동시에 기록할 수 있습니다. 장부가 4개면 기록 능력도 4배입니다. 이걸 샤딩이라고 하고, 각 조각을 샤드라고 부릅니다.
이제 장부를 늘리는 만큼 쓰기가 늘어납니다. 복제로는 절대 얻을 수 없던 것입니다.
그런데 누가 어느 장부인지 어떻게 아나
요청이 들어오면 어느 샤드로 갈지 정해주는 존재가 필요합니다. "김철수의 정보를 줘"라는 요청이 오면, 이름을 보고 "1번 장부"라고 판단합니다.
중요한 것은 이 판단이 매번 같아야 한다는 점입니다. 김철수를 저장할 때 1번에 넣었는데 꺼낼 때 3번을 뒤지면 영영 못 찾습니다. 그래서 이 판단은 키를 계산해서 나온 항상 같은 답이어야 합니다. 이 계산 방식이 다음 강의의 주제인 일관된 해시입니다.
이 강의의 캔버스에서는 샤드를 여러 개의 데이터베이스로 두고, 그 앞의 로드밸런서에 '일관된 해시' 방식을 지정해서 샤드 라우터 역할을 시킵니다. 실제 시스템에서는 이 역할을 전용 프록시나 애플리케이션 코드가 맡습니다.
무엇을 기준으로 나눌 것인가 — 여기가 전부다
샤딩에서 유일하게 중요한 결정은 "무엇을 기준으로 나눌 것인가"입니다. 이걸 샤드 키라고 합니다. 그리고 한 번 정하면 바꾸기가 매우 어렵습니다.
나쁜 예를 봅시다. 가입 연도로 나눴다고 합시다.
| 샤드 | 담당 | 실제 트래픽 |
|---|---|---|
| 1번 | 2021년 가입자 | 거의 없음 |
| 2번 | 2022년 가입자 | 거의 없음 |
| 3번 | 2023년 가입자 | 조금 |
| 4번 | 2024년 가입자 | 폭주 |
신규 가입자는 전부 4번으로 갑니다. 샤드를 4개로 늘렸는데 일하는 건 하나뿐입니다. 이걸 핫스팟이라고 합니다. 나머지 3대는 돈만 쓰고 놀고 있습니다.
좋은 샤드 키의 조건은 두 가지입니다.
- 고르게 퍼진다 — 어느 샤드에도 몰리지 않는다 (고객 ID처럼 값이 다양한 것)
- 대부분의 질의에 그 키가 들어 있다 — 안 그러면 모든 샤드를 다 뒤져야 한다
샤딩의 대가
공짜가 아닙니다. 데이터가 흩어진 순간 여러 샤드에 걸친 질문이 어려워집니다.
- "김철수의 주문 내역" → 쉽습니다. 김철수의 샤드 하나만 봅니다.
- "지난달 전체 매출" → 4개 샤드를 전부 조회해서 합산해야 합니다.
- "A의 잔액을 B에게 이체" → 두 사람이 다른 샤드에 있으면 매우 어렵습니다.
한쪽만 성공하고 다른 쪽이 실패하면 돈이 사라집니다.
마지막 항목이 특히 중요합니다. 하나의 데이터베이스 안이라면 이 문제는 트랜잭션이 해결해 줍니다. 그게 다음다음 강의의 주제입니다. 그런데 샤드가 갈리는 순간 그 안전장치가 사라집니다.
그래서 순서가 중요합니다. 샤딩은 마지막 카드입니다. 인덱스로 안 되고, 캐시로 안 되고, 복제로도 안 될 때 꺼내는 것입니다. 처음부터 샤딩하는 설계는 대부분 필요 없는 고통을 자초하는 일입니다.
이 강의가 단순화한 것
교육용 모형은 언제나 무언가를 생략합니다. 무엇을 생략했는지 아는 것도 학습의 일부입니다.
전제한 것
- 이 강의의 캔버스에는 전용 샤드 라우터 컴포넌트가 없어서, '일관된 해시'로 설정한 로드밸런서를 샤드 라우터 대신 쓴다. 실제 샤드 라우팅은 로드밸런싱과 목적이 다르다 — 로드밸런서는 아무 서버나 골라도 되지만 샤드 라우터는 반드시 정해진 한 곳을 찾아가야 한다.
- 각 샤드를 독립된 주 데이터베이스로 표현하며, 샤드마다 복제본을 두는 실제 구성(샤딩 + 복제 조합)은 생략한다.
- 샤드 키는 요청에 딸린 키 값으로 이미 정해져 있다고 본다. 현실에서는 어떤 필드를 샤드 키로 삼을지 정하는 일이 설계의 대부분이다.
- 샤드를 추가할 때 기존 데이터를 옮기는 재배치 작업은 이 강의에서 시간이 들지 않는 것으로 본다. 실제로는 이 이사가 몇 시간에서 며칠까지 걸린다.
다루지 않은 것
- 범위 기반 샤딩과 해시 기반 샤딩의 비교
- 샤드 간 분산 트랜잭션과 2단계 커밋
- 샤드 재배치(resharding)와 무중단 데이터 이전
- 글로벌 보조 인덱스와 스캐터-개더 질의
더 읽을거리: Vitess — Sharding Documentation
이제 직접 만들어 보세요
읽어서 아는 것과 만들어서 아는 것은 다릅니다. 컴포넌트를 배치하고 트래픽을 흘려서 실제로 동작하는지 확인해 보세요.
이 강의에 물어보기
하루 5번이 강의 본문만 근거로 답합니다. 강의에 없는 내용은 지어내지 않습니다.