병렬 실행과 샤딩으로 대규모 워크로드 확장하기

병렬 실행과 샤딩의 역할 차이, 샤드 키 설계, 일관성 모델, 리밸런싱과 운영 트레이드오프를 정리한다.

2026-08-14 · 최초 발행 2025-12-24

작업을 나누는 방식과 데이터를 나누는 방식

대규모 데이터와 트래픽에서는 단일 노드의 처리 지연과 병목이 먼저 한계가 된다. 병렬 실행은 하나의 작업을 독립 가능한 부분 작업으로 나누어 여러 스레드, 프로세스, 노드에서 동시에 처리하는 메커니즘이다. 입력을 분할하고, 각 실행 단위가 처리한 결과를 집계하는 흐름으로 대기 시간을 줄이고 처리량을 늘린다.

샤딩은 데이터 세트를 샤드라는 독립 파티션으로 나누고, 각 파티션을 별도 노드나 클러스터에 배치하는 수평 파티셔닝 방식이다. 샤드 키가 요청의 라우팅을 결정하며, 샤드 간에 데이터와 트래픽을 분산한다. 각 샤드의 독립성이 보장될수록 확장성과 격리성이 높아진다.

둘은 같은 문제가 아니다. 샤딩은 데이터가 어디에 놓일지를 결정하는 구조이고, 병렬 실행은 작업을 어떻게 수행할지를 정하는 방식이다. 샤딩된 구조 위에서 병렬 실행을 적용하면 성능을 더 끌어올릴 수 있다. 샤딩 없이도 멀티코어와 벡터화를 활용한 단일 노드 병렬 실행은 가능하지만, 다중 노드 확장은 샤딩의 영역이다.

분산 처리의 설계 지점

샤드 키는 균등한 분포, 낮은 스큐(skew), 예측 가능한 라우팅을 만족하도록 해시, 범위, 하이브리드 방식으로 설계한다. 스케일 아웃·인이나 키 변경에 따른 재샤딩에서는 데이터 이동 비용을 줄일 장치가 필요하다.

실행 계층은 코디네이터와 워커로 나뉜다. 코디네이터는 파티션 매핑과 작업 스케줄링을 담당하고, 워커는 부분 작업을 실행한다. 아이들 워커를 줄이고 데이터 지역성을 활용하며 백프레셔(backpressure)를 제어해야 한다.

작업 큐에서는 라운드로빈 또는 가중치 스케줄링을 사용하고, 성능 피드백에 따라 작업을 동적으로 재할당할 수 있다. CPU, 메모리, IO, 네트워크 쿼터를 격리하고 오토스케일링 정책 및 워크로드 클래스를 함께 관리한다.

트랜잭션 경계도 샤드 구조에 맞춰야 한다. 단일 샤드 트랜잭션에는 로컬 ACID를 적용할 수 있지만, 다중 샤드 트랜잭션에는 2PC/3PC 또는 Saga 보상 트랜잭션이 필요하다. Strong, Bounded Staleness, Eventual 일관성 수준은 지연과 가용성 사이의 트레이드오프로 다뤄야 한다.

장애 대응에서는 리더-팔로워 또는 멀티리더 샤드 복제, 실패 감지, 자동 페일오버가 필요하다. 샤드 스플릿·머지와 재밸런싱을 준비하고, 핫샤드는 키 솔팅이나 버킷 프리픽스로 완화한다.

요청이 워커와 샤드 사이를 흐르는 경로

분할 계산/파티션 조회작업 등록워커 할당/데이터 지역성 고려부분처리('트랜잭션/락/타임아웃')집계/리듀스/정렬실패 감지보상 트랜잭션 또는 '2PC중단/회복'모니터링/백프레셔/오토스케일입력 요청파티셔닝/샤드 매핑코디네이터/스케줄러워커부분 결과최종 결과재시도/리밸런싱운영 제어면

입력 요청은 분할 키 계산과 파티션 조회를 거쳐 적절한 샤드에 매핑된다. 코디네이터는 작업 큐에 등록된 요청을 워커에 배정하고, 데이터 지역성을 고려해 부분 처리를 병렬로 수행한다. 이후 부분 결과를 집계·리듀스·정렬해 최종 결과를 만든다.

워커 실패 시에는 재시도하고, 샤드가 과열되면 리밸런싱을 수행한다. 트랜잭션 충돌에는 지수적 백오프를 적용한다. 단일 샤드에서는 Strong 일관성을 권장하며, 다중 샤드 작업은 2PC 또는 Saga로 보상 처리를 설계한다.

병렬 실행과 샤딩의 운영상 차이

항목 병렬 실행 샤딩
성능 다중 스레드/프로세스로 지연 단축, 집계 비용 존재 수평 확장으로 처리량 선형 증가 기대, 네트워크 오버헤드
확장성 단일 노드 한계(코어/메모리) 영향 노드 추가로 수평 확장, 재샤딩 비용 고려
일관성 단일 노드 메모리 일관성, 락 경합 관리 샤드 간 글로벌 일관성 어려움, 프로토콜 필요
안정성 프로세스 격리 수준, 로컬 장애 영향 제한 노드/네트워크 장애 대비 복제·페일오버 필수
운영 편의 배치·단일 노드 튜닝 중심 라우팅/복제/리밸런싱 등 분산 운영 복잡성

워크로드에서 만나는 적용 방식

대규모 OLTP 트래픽에서는 사용자 ID 기반 해시 샤딩으로 로그인·주문 API를 확장할 수 있다. Vitess/MySQL, YugabyteDB, CockroachDB 등은 샤드 라우팅 자동화에 활용된다.

분석 및 ETL 처리에서는 Spark/Flink가 파일 파티션별 병렬 읽기와 map-reduce 집계를 수행한다. 스큐 키를 탐지한 뒤 Salting과 Adaptive Query Execution을 적용해 분산 균형을 맞춘다.

메시징과 스트림 처리에서는 Kafka 파티션 및 Consumer Group으로 수평 처리를 구성하고, 리밸런스 처리 전략을 세운다. Exactly-once가 필요하면 트랜잭션 프로듀서·소비자와 idempotent sink를 적용한다.

캐시와 검색 인덱스는 Redis Cluster 또는 Elasticsearch 샤드로 읽기·쓰기 부하를 나눈다. 핫키에는 샤딩 프리픽스를 적용하고 레플리카 리더 선호도를 조정한다.

키 설계부터 관측까지 함께 관리하기

샤드 키는 높은 카디널리티와 균등 분포, 라우팅 예측성을 확보하는 방향으로 정한다. 시간 기반 키는 버킷화해 핫샤드를 막을 수 있다. 다만 조인과 범위 쿼리는 불리해질 수 있고, 애플리케이션 레벨 리듀스가 필요해질 수 있다.

트랜잭션은 단일 샤드에서 끝나도록 우선 설계하고, 다중 샤드에서는 Saga 기반의 업무적 보상을 설계한다. 2PC는 강한 일관성의 대가로 높은 지연과 락 보유를 수반하며, Saga는 비동기 보상 복잡도를 높인다.

재샤딩에는 버킷, 즉 가상 샤드 레이어를 두어 데이터 이동을 줄이고 온라인 재밸런싱을 적용한다. 대신 버킷 메타데이터 관리 비용과 라우팅 경유 비용이 늘어난다.

샤드별 QPS, 99p 지연, 핫키 분포, 리밸런스율은 대시보드로 관리하고 SLO 기반 오토스케일링에 연결한다. 레플리카가 과도하면 비용이 증가하고, 부족하면 페일오버 시간이 증가한다.

보안과 거버넌스에서는 샤드 단위 암호화 키, RBAC, 데이터 소유권 및 DLP 정책, 감사 로그 중앙화를 적용한다. 지역별 데이터 거주를 위한 규제 샤드 분리는 운영 복잡도를 높일 수 있다.

PostgreSQL 파티셔닝과 Python 병렬 처리

PostgreSQL 14+의 파티셔닝 테이블에서는 사용자 ID 해시 기반 파티션을 구성할 수 있다.

-- 전제: 사용자 ID 해시 기반 16버킷 파티션
CREATE TABLE orders (
  id bigserial PRIMARY KEY,
  user_id bigint NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now(),
  payload jsonb
) PARTITION BY HASH (user_id);

-- 16개 파티션 생성
DO $$
BEGIN
  FOR i IN 0..15 LOOP
    EXECUTE format(
      'CREATE TABLE orders_p%1$s PARTITION OF orders FOR VALUES WITH (MODULUS 16, REMAINDER %1$s);',
      i
    );
  END LOOP;
END$$;

-- 파티션 프루닝 유도 쿼리
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 123456;

CPU 중심 작업은 Python 멀티프로세싱으로 샤드 단위 실행을 구성할 수 있다.

from concurrent.futures import ProcessPoolExecutor, as_completed

def process_batch(batch):
    # CPU 바운드 로직 또는 외부 IO 최소화
    return sum(x * x for x in batch)

if __name__ == "__main__":
    data = list(range(1_000_000))
    shard_size = 50_000
    shards = [data[i:i+shard_size] for i in range(0, len(data), shard_size)]
    results = []
    with ProcessPoolExecutor() as ex:
        futures = [ex.submit(process_batch, s) for s in shards]
        for f in as_completed(futures):
            results.append(f.result())
    print(sum(results))

CPU 바운드 작업에는 멀티프로세싱을, IO 바운드 작업에는 비동기 또는 스레드를 우선 적용한다. 파티션 키를 포함한 프라이머리 키와 인덱스를 구성해 파티션 프루닝을 확보한다.

성능과 비용에서 기대할 수 있는 변화

샤드 수 또는 워커 수가 늘어나면 처리량은 선형적 증가를 기대할 수 있다. 이상적인 경우 N배이며, 실효는 0.6~0.85×N 배다.

P95 지연은 30~70% 단축될 수 있고, 병렬화·파이프라이닝·지역성 최적화를 적용하면 추가 개선 여지가 있다. 샤드 격리는 단일 장애의 영향 반경을 줄이고 폭주를 막으며, MTTR 단축에도 연결된다.

필요한 샤드만 수평 확장하면 비용/성능 비율을 개선할 수 있다. 반면 과도한 복제는 비용을 상승시킨다. 신규 설계에서는 버킷 기반 샤딩, Saga 우선 전략, 데이터 지역성·백프레셔·오토스케일링을 포함한 코디네이션 레이어를 함께 검토해야 한다.

병렬 실행샤딩데이터 파티셔닝분산 처리확장성