Hangilog

샤딩과 파티셔닝

2025-05-31 at Database category

DB에도 한계가 있다!

우리가 데이터를 저장하고 관리하기 위해 사용하는 DB에는 한계가 있습니다. 즉, 무한히 데이터를 적재할 수 없습니다. 데이터가 많아지면 많아질수록 데이터의 삽입, 조회, 인덱싱 등의 모든 연산 과정에서 성능이 저하됩니다. 결국 하나의 거대한 DB는 전체 시스템의 병목 지점으로 전락할 것입니다.

이런 상황을 막기 위해 우린 비대한 DB를 나눠야 합니다. DB를 분할하는 방법에는 대표적으로 샤딩(Sharding)과 파티셔닝(Partitioning)이 있습니다. 두 방법 모두 데이터를 여러 조각으로 나눈다는 점에서는 비슷하지만, 나누는 목적과 운영 방식에는 꽤 큰 차이가 있습니다.

얼핏 보면 단순히 테이블이나 DB를 여러 개로 쪼개는 일처럼 보입니다. 하지만 실제로는 어떤 기준으로 나눌지, 나눈 데이터를 어떻게 조회할지, 여러 조각에 걸친 트랜잭션은 어떻게 다룰지까지 함께 고민해야 합니다. 잘못 나누면 오히려 쿼리가 더 복잡해지고, 장애가 발생했을 때 원인을 찾기 어려운 구조가 될 수 있습니다.

이번 글에서는 샤딩과 파티셔닝이 무엇인지, 두 방법은 어떤 차이가 있는지, 그리고 어떤 상황에서 선택해야 하는지 정리해보겠습니다.

DB를 키우는 두 가지 방향

DB 성능을 개선하는 가장 직관적인 방법은 더 좋은 장비를 사용하는 것입니다. CPU, 메모리, 디스크 성능을 높이면 하나의 DB가 더 많은 요청을 처리할 수 있습니다. 이런 방식을 스케일 업(Scale Up)이라고 합니다.

하지만 스케일 업에는 명확한 한계가 있습니다. 장비 성능을 무한히 높일 수 없고, 일정 수준을 넘어가면 비용 대비 성능 향상도 점점 작아집니다. 무엇보다 하나의 DB에 모든 요청이 몰리는 구조 자체는 그대로 남아 있습니다. 결국 DB 한 대가 감당할 수 있는 한계에 도달하면 더 이상 버티기 어렵습니다.

반대로 DB를 여러 대로 늘려 부하를 분산하는 방식도 있습니다. 이를 스케일 아웃(Scale Out)이라고 합니다. 읽기 요청이 많다면 Replication을 통해 읽기 전용 DB를 늘릴 수 있고, 데이터 자체가 너무 많다면 데이터를 여러 곳에 나눠 저장할 수도 있습니다.

여기서 중요한 것은 단순히 DB를 여러 대로 늘리는 것만으로 문제가 해결되지는 않는다는 점입니다. 데이터를 나누지 않은 채 서버만 늘린다면 모든 서버가 같은 데이터를 들고 있어야 하고, 쓰기 요청의 병목은 여전히 남게 됩니다. 데이터의 양 자체가 문제라면 결국 데이터를 어떻게 나눌 것인지가 핵심이 됩니다.

파티셔닝이란?

파티셔닝(Partitioning)은 하나의 큰 테이블을 여러 개의 작은 단위로 나누는 방식입니다. 이때 나뉜 각각의 조각을 파티션(Partition)이라고 부릅니다. 사용자는 여전히 하나의 테이블을 조회하는 것처럼 사용할 수 있지만, DB 내부에서는 여러 파티션 중 필요한 범위만 접근하도록 최적화할 수 있습니다.

예를 들어 주문 데이터가 orders 테이블 하나에 계속 쌓이고 있다고 생각해보겠습니다. 서비스가 성장하면서 주문 데이터가 수천만 건, 수억 건으로 늘어나면 특정 기간의 주문만 조회하는 쿼리도 점점 느려질 수 있습니다. 이때 주문일을 기준으로 월별 파티션을 만들면, 2025년 5월 주문을 조회할 때 전체 테이블을 모두 뒤지지 않고 2025년 5월 파티션만 확인할 수 있습니다.

즉, 파티셔닝은 큰 테이블을 관리 가능한 작은 단위로 나누고, 필요한 데이터만 효율적으로 접근하기 위한 방법입니다.

수평 파티셔닝

수평 파티셔닝(Horizontal Partitioning)은 행(Row)을 기준으로 데이터를 나누는 방식입니다. 테이블의 컬럼 구조는 그대로 유지하고, 어떤 행을 어떤 파티션에 저장할지만 결정합니다.

예를 들어 주문 테이블을 주문일 기준으로 나눈다면 다음과 같이 생각할 수 있습니다.

파티션 저장되는 데이터
orders_2025_01 2025년 1월 주문
orders_2025_02 2025년 2월 주문
orders_2025_03 2025년 3월 주문

이 방식은 특정 범위의 데이터만 자주 조회할 때 효과적입니다. 오래된 데이터를 별도로 보관하거나 삭제하기에도 좋습니다. 예를 들어 3년이 지난 로그 데이터를 삭제해야 한다면 거대한 테이블에서 조건에 맞는 행을 하나씩 삭제하는 대신, 오래된 파티션 자체를 제거할 수 있습니다.

수직 파티셔닝

수직 파티셔닝(Vertical Partitioning)은 컬럼(Column)을 기준으로 데이터를 나누는 방식입니다. 하나의 테이블에 너무 많은 컬럼이 있거나, 자주 사용되는 컬럼과 거의 사용되지 않는 컬럼이 섞여 있을 때 활용할 수 있습니다.

예를 들어 회원 테이블에 기본 정보와 프로필 이미지, 자기소개, 설정값 등이 모두 들어있다고 생각해보겠습니다. 로그인이나 권한 확인에서는 회원의 식별자, 이메일, 비밀번호 정도만 필요할 수 있습니다. 반면 프로필 이미지는 특정 화면에서만 필요합니다. 이때 자주 사용하는 기본 정보와 상대적으로 덜 사용하는 프로필 정보를 분리하면, 대부분의 조회에서 불필요한 데이터를 읽지 않아도 됩니다.

테이블 포함하는 데이터
members id, email, password, name
member_profiles member_id, image_url, introduction

물론 수직 파티셔닝은 무조건 좋은 방법이 아닙니다. 함께 조회해야 하는 컬럼을 너무 잘게 나누면 오히려 조인이 늘어나고, 애플리케이션 코드가 복잡해질 수 있습니다. 따라서 실제 조회 패턴을 기준으로 나눌 필요가 있습니다.

파티셔닝 방식

파티셔닝에는 여러 방식이 있습니다. 어떤 기준으로 데이터를 나누느냐에 따라 성능과 관리 방식이 달라집니다.

Range Partitioning

Range Partitioning은 특정 값의 범위를 기준으로 데이터를 나누는 방식입니다. 날짜, 숫자, 연속적인 범위를 가진 값에 자주 사용됩니다.

대표적인 예시는 날짜 기준 파티셔닝입니다.

파티션 조건
orders_2024 ordered_at < 2025-01-01
orders_2025_01 2025-01-01 <= ordered_at < 2025-02-01
orders_2025_02 2025-02-01 <= ordered_at < 2025-03-01

Range Partitioning은 특정 기간의 데이터를 조회할 때 효과적입니다. 하지만 특정 범위에 데이터가 몰리면 일부 파티션만 과도하게 커질 수 있습니다. 예를 들어 최근 주문 데이터만 폭발적으로 증가한다면 최근 월 파티션에 대부분의 쓰기 요청이 몰릴 수 있습니다.

List Partitioning

List Partitioning은 미리 정해진 값 목록을 기준으로 데이터를 나누는 방식입니다. 지역, 카테고리, 상태값처럼 값의 종류가 명확할 때 사용할 수 있습니다.

예를 들어 사용자 데이터를 지역별로 나눌 수 있습니다.

파티션 조건
users_seoul region = 'SEOUL'
users_busan region = 'BUSAN'
users_etc 그 외 지역

값의 의미가 명확하기 때문에 이해하기 쉽다는 장점이 있습니다. 하지만 값의 종류가 자주 바뀌거나 특정 값에 데이터가 몰리면 관리가 어려워질 수 있습니다.

Hash Partitioning

Hash Partitioning은 특정 컬럼의 해시 값을 기준으로 데이터를 나누는 방식입니다. 데이터가 특정 범위나 값에 몰리지 않도록 비교적 균등하게 분산할 수 있습니다.

예를 들어 사용자 ID를 기준으로 4개의 파티션에 데이터를 나눈다면 다음과 같이 생각할 수 있습니다.

partition = hash(user_id) % 4

Hash Partitioning은 데이터를 균등하게 분산하기 좋습니다. 하지만 범위 조회에는 약합니다. user_id의 해시 값을 기준으로 나눴다면, 특정 가입 기간의 사용자를 조회할 때 어떤 파티션에 데이터가 있는지 직관적으로 알기 어렵습니다.

결국 파티셔닝 방식은 조회 패턴과 데이터 증가 패턴을 함께 고려해 선택해야 합니다. 단순히 데이터를 균등하게 나누는 것보다, 실제 서비스에서 어떤 조건으로 데이터를 자주 조회하는지가 더 중요합니다.

샤딩이란?

샤딩(Sharding)은 데이터를 여러 DB에 나누어 저장하는 방식입니다. 각각의 DB를 샤드(Shard)라고 부릅니다. 파티셔닝이 보통 하나의 DB 내부에서 테이블을 나누는 방식이라면, 샤딩은 데이터를 여러 DB 서버에 분산한다는 점이 핵심입니다.

예를 들어 사용자 데이터를 사용자 ID 기준으로 4개의 DB에 나눠 저장한다고 생각해보겠습니다.

샤드 저장되는 데이터
shard_0 hash(user_id) % 4 = 0
shard_1 hash(user_id) % 4 = 1
shard_2 hash(user_id) % 4 = 2
shard_3 hash(user_id) % 4 = 3

이렇게 나누면 하나의 DB가 모든 데이터를 저장하지 않아도 됩니다. 쓰기 요청도 여러 DB로 분산할 수 있습니다. 즉, 샤딩은 단일 DB의 저장 용량과 쓰기 처리량 한계를 넘기 위한 스케일 아웃 전략입니다.

다만 샤딩은 파티셔닝보다 훨씬 운영 난이도가 높습니다. 데이터가 여러 DB에 흩어져 있기 때문에 애플리케이션은 어떤 데이터가 어떤 샤드에 있는지 알아야 합니다. 또한 여러 샤드에 걸친 조회, 조인, 트랜잭션은 단일 DB에서 처리할 때보다 훨씬 어려워집니다.

샤드 키

샤딩에서 가장 중요한 결정은 샤드 키(Shard Key)를 무엇으로 선택할지입니다. 샤드 키는 데이터를 어떤 샤드에 저장할지 결정하는 기준입니다.

예를 들어 사용자 ID를 샤드 키로 선택하면, 특정 사용자의 데이터는 항상 같은 샤드에 저장되도록 만들 수 있습니다. 사용자의 주문, 장바구니, 알림처럼 사용자 단위로 조회되는 데이터가 많다면 사용자 ID는 좋은 샤드 키가 될 수 있습니다.

하지만 샤드 키를 잘못 선택하면 데이터가 고르게 분산되지 않습니다. 이를 데이터 쏠림(Skew)이라고 합니다. 예를 들어 지역을 샤드 키로 선택했는데 대부분의 사용자가 서울에 몰려 있다면, 서울 샤드만 과도하게 커지고 다른 샤드는 거의 사용되지 않을 수 있습니다.

좋은 샤드 키를 선택하기 위해서는 다음과 같은 기준을 고려해야 합니다.

  • 데이터가 여러 샤드에 고르게 분산되는가?
  • 자주 조회하는 조건에 포함되는가?
  • 하나의 요청이 여러 샤드를 건드리지 않도록 도와주는가?
  • 시간이 지나도 특정 샤드에 데이터가 몰리지 않는가?

결국 샤드 키는 단순히 유니크한 값을 고르는 문제가 아닙니다. 서비스의 조회 패턴과 데이터 증가 방향을 함께 고려해야 합니다.

샤딩의 어려움

샤딩은 강력한 방법이지만, 그만큼 많은 복잡도를 가져옵니다. 특히 기존에 단일 DB를 전제로 작성했던 코드와 운영 방식이 그대로 동작하지 않을 가능성이 큽니다.

Cross-Shard Query

데이터가 여러 샤드에 흩어져 있으면, 하나의 쿼리로 모든 데이터를 쉽게 조회하기 어렵습니다. 예를 들어 전체 사용자 중 최근 1시간 동안 가입한 사용자를 조회해야 한다면 모든 샤드를 확인해야 할 수 있습니다.

단일 DB에서는 간단한 쿼리였던 작업이 샤딩 이후에는 다음과 같은 과정으로 바뀔 수 있습니다.

  1. 모든 샤드에 쿼리를 보낸다.
  2. 각 샤드의 결과를 모은다.
  3. 애플리케이션에서 정렬하거나 집계한다.
  4. 필요한 만큼 잘라서 응답한다.

즉, DB가 해주던 일을 애플리케이션이나 별도의 집계 시스템이 대신해야 할 수 있습니다.

Join과 Transaction

샤딩된 환경에서는 조인도 어려워집니다. 서로 다른 샤드에 있는 데이터를 조인하려면 네트워크를 넘나들며 데이터를 가져와야 합니다. 성능도 떨어지고 구현도 복잡해집니다.

트랜잭션도 마찬가지입니다. 하나의 DB 안에서는 트랜잭션으로 원자성을 보장하기 쉽지만, 여러 샤드에 걸친 트랜잭션은 훨씬 어렵습니다. 분산 트랜잭션을 사용할 수도 있지만, 성능과 운영 복잡도 측면에서 부담이 큽니다.

그래서 샤딩을 적용한 시스템에서는 가능하면 하나의 요청이 하나의 샤드 안에서 끝나도록 도메인을 설계하는 것이 중요합니다. 사용자 ID를 기준으로 샤딩했다면 사용자와 강하게 묶인 데이터는 같은 샤드에 저장하는 식입니다.

Rebalancing

서비스가 계속 성장하면 처음에 만든 샤드 개수로는 부족해질 수 있습니다. 이때 샤드를 추가하고 데이터를 다시 분산해야 합니다. 이를 리밸런싱(Rebalancing)이라고 합니다.

리밸런싱은 생각보다 까다롭습니다. 이미 운영 중인 데이터의 일부를 다른 샤드로 옮겨야 하고, 데이터 이동 중에도 서비스는 계속 정상적으로 동작해야 합니다. 데이터가 이동하는 동안 라우팅 정보가 꼬이면 같은 사용자의 데이터가 두 곳에 나뉘어 저장되거나, 조회가 실패할 수도 있습니다.

따라서 샤딩은 적용하는 순간뿐만 아니라, 샤드가 늘어날 미래까지 함께 고려해야 합니다.

샤딩과 파티셔닝의 차이

샤딩과 파티셔닝은 모두 데이터를 나누는 방법입니다. 그래서 두 개념이 자주 혼동됩니다. 특히 샤딩을 수평 파티셔닝의 한 종류로 설명하기도 합니다. 하지만 실무적인 관점에서는 다음과 같이 구분하는 편이 이해하기 쉽습니다.

구분 파티셔닝 샤딩
분할 위치 주로 하나의 DB 내부 여러 DB 서버
목적 큰 테이블 관리, 조회 최적화 저장 용량과 쓰기 부하 분산
애플리케이션 영향 상대적으로 작음 상대적으로 큼
운영 난이도 비교적 낮음 높음
주요 고민 파티션 기준, 파티션 관리 샤드 키, 라우팅, 리밸런싱

파티셔닝은 DB 내부 최적화에 가깝습니다. 애플리케이션 입장에서는 여전히 하나의 테이블을 조회하는 것처럼 사용할 수 있는 경우가 많습니다. 반면 샤딩은 데이터가 여러 DB에 흩어지기 때문에 애플리케이션 구조와 운영 방식에도 영향을 줍니다.

물론 실제 DB 제품이나 분산 DB 환경에서는 이 경계가 완전히 딱 떨어지지 않을 수 있습니다. 내부적으로 파티션을 여러 노드에 분산하는 DB도 있고, 샤딩을 프레임워크나 미들웨어가 대신 처리해주는 경우도 있습니다. 하지만 핵심은 같습니다. 데이터를 나누면 성능 문제를 해결할 수 있지만, 그 대가로 복잡도가 증가합니다.

언제 사용해야 할까?

파티셔닝과 샤딩은 성능 개선을 위한 강력한 도구지만, 처음부터 적용해야 하는 기술은 아닙니다. 오히려 너무 이르게 적용하면 불필요하게 시스템만 복잡해질 수 있습니다.

먼저 확인해야 할 것은 정말 데이터 분할이 필요한 상황인지입니다. 인덱스가 적절히 설정되어 있는지, 쿼리가 비효율적으로 작성되어 있지 않은지, 캐시나 읽기 복제만으로 해결할 수 있는 문제는 아닌지 먼저 확인해야 합니다.

그럼에도 테이블이 너무 커져서 관리가 어렵거나, 특정 범위의 데이터만 자주 조회하거나, 오래된 데이터를 주기적으로 삭제해야 한다면 파티셔닝을 고려할 수 있습니다. 예를 들어 로그, 주문 이력, 정산 데이터처럼 시간이 지날수록 계속 쌓이고 기간 단위로 조회되는 데이터는 파티셔닝과 잘 맞습니다.

반면 하나의 DB가 저장 용량이나 쓰기 요청을 더 이상 감당하지 못하고, Scale Up만으로 해결하기 어렵다면 샤딩을 고려할 수 있습니다. 다만 샤딩은 적용 이후 되돌리기 어렵고 운영 복잡도가 크게 증가하기 때문에, 충분한 근거와 설계가 필요합니다.

개인적으로는 다음 순서로 접근하는 것이 안전하다고 생각합니다.

  1. 쿼리와 인덱스를 먼저 점검한다.
  2. 읽기 부하가 문제라면 Replication과 Cache를 고려한다.
  3. 테이블 크기와 관리가 문제라면 Partitioning을 고려한다.
  4. 단일 DB의 저장 용량과 쓰기 처리량이 한계라면 Sharding을 고려한다.

즉, 샤딩은 가장 먼저 꺼내야 하는 카드라기보다, 다른 방법으로는 단일 DB의 한계를 넘기 어려울 때 선택하는 카드에 가깝습니다.

결론

샤딩과 파티셔닝은 모두 커진 데이터를 나누기 위한 방법입니다. 파티셔닝은 하나의 큰 테이블을 더 작은 단위로 나눠 조회와 관리를 효율적으로 만드는 방식이고, 샤딩은 데이터를 여러 DB 서버에 분산해 단일 DB의 한계를 넘기 위한 방식입니다.

두 방법 모두 성능 문제를 해결할 수 있지만, 공짜는 아닙니다. 데이터를 나누는 순간 어떤 기준으로 나눌지, 조회는 어떻게 할지, 데이터가 몰리면 어떻게 다시 나눌지 같은 새로운 문제가 생깁니다. 특히 샤딩은 애플리케이션 구조와 운영 방식에 큰 영향을 주기 때문에 충분히 신중하게 접근해야 합니다.

결국 중요한 것은 기술 이름이 아니라 문제의 원인입니다. 조회 범위가 너무 넓은 것이 문제인지, 테이블 관리가 어려운 것이 문제인지, 쓰기 부하가 단일 DB에 몰리는 것이 문제인지 먼저 파악해야 합니다. 그 다음에야 파티셔닝이 필요한지, 샤딩이 필요한지 판단할 수 있습니다.

무작정 DB를 나누는 것은 해결책이 아닙니다. 하지만 데이터가 커지는 방향과 서비스의 조회 패턴을 충분히 이해하고 있다면, 샤딩과 파티셔닝은 DB의 한계를 넘어서는 강력한 선택지가 될 수 있습니다.

출처

hangillee

Personal blog by hangillee.

다른 사람을 돕는 개발자를 꿈꿉니다.