분산 데이터베이스의 역사

분산DB의 역사 톺아보기를 했다

Posted by Yan on June 17, 2025

분산 데이터베이스의 역사와 핵심 개념

소개: 요즘의 애플리케이션은 대용량 데이터를 처리하고 고가용성을 유지하기 위해 분산 데이터베이스 구조를 채택하는 경우가 많다. 오늘 분산 DB에 주요 내용은 데이터베이스 복제(replication)의 종류와 이슈, Oracle RAC와 같은 클러스터링 기법, 샤딩(sharding)을 통한 수평 확장, 프록시 시스템(예: MariaDB MaxScale)의 역할, 분산 DB의 일관성내결함성 개념, 그리고 MySQL Cluster의 구조 등을 포함한다.

데이터베이스 복제(Replication)

데이터베이스 복제는 한 데이터베이스의 변경 내용을 다른 데이터베이스에 복사하여 여러 곳에 데이터를 유지하는 기술이다. 이를 통해 하나의 서버 장애에도 데이터가 사라지지 않도록 하거나, 읽기 부하를 여러 DB로 분산해 성능을 높일 수 있다. 복제에는 단방향(일대다) 복제양방향(다중 마스터) 복제가 있고, 데이터 변경 전파 방식에 따라 동기식비동기식 복제로 나뉜다.

단방향 복제 (마스터-슬레이브)

  • 하나의 주 데이터베이스(마스터)에서만 쓰기 연산이 이루어지고, 그 변경사항이 하나 이상의 보조 데이터베이스(슬레이브)로 복사되는 구조
    replication1
  • 위 그림은 클라이언트의 쓰기 요청은 마스터로 보내고, 읽기 요청은 슬레이브들이 처리하도록 분산하는 구조를 보여준다.
  • 이 구성에서는 모든 데이터 변경이 한 곳에서 일어나므로 충돌 문제가 없고 구현이 비교적 간단하며, 슬레이브를 추가해 읽기 성능과 장애 대비를 향상시킬 수 있다는 장점이 있다.
  • 마스터-슬레이브 복제의 대표적인 활용 사례로는 읽기 부하 분산(예: 조회가 많은 서비스에서 슬레이브를 두어 읽기 처리)과 백업/고가용성(마스터 장애 시 슬레이브를 승격) 등이 있다.

양방향 복제 (멀티-마스터)

  • 두 대 이상의 데이터베이스가 동등한 마스터로서 서로에게 변경 내용을 복제하는 방식
    replication2
  • 각 노드가 독립적으로 쓰기 작업을 받아들이고, 그 변경을 다른 마스터들에게 전달하여 모든 마스터가 최종적으로 동일한 상태를 갖도록 한다.
  • 이 구조의 이점은 쓰기를 분산함으로써 단일 노드에 집중되는 부하를 줄이고, 여러 지역에 마스터를 두어 지연 시간을 단축하거나 고가용성을 높일 수 있다는 점.
  • 하지만, 여러 노드에서 동일 데이터에 동시 수정이 발생하면 충돌 발생 가능성이 있으며, 이를 해결하기 위한 컨플릭트 해소 절차가 필요한다. 예를 들어 두 개의 마스터가 동시에 같은 레코드를 서로 다른 값으로 수정하면 “스플릿 브레인(split brain)” 상황이 되어 데이터가 불일치하게 된다.
  • 실제 사례로 한 쪽 마스터에서는 값 10으로, 다른 쪽에서는 30으로 변경했다면 어느 값이 옳은지 모호해집니다. 이러한 문제를 피하려면 최종 기록 승리(LWW) 같은 간단한 규칙을 적용하거나, Paxos/Raft와 같은 합의 프로토콜로 한 노드를 리더로 정해 쿼럼 합의를 거쳐 커밋하는 방식으로 일관성을 보장한다. 일반적으로 멀티-마스터 복제는 구현 복잡도와 충돌 해결 부담 때문에 필요한 경우에만 신중히 사용하는 것이 권장된다.

동기식 vs 비동기식 복제

  • 복제는 데이터 변경을 얼마나 즉각적으로 다른 노드에 적용하느냐에 따라 동기식과 비동기식으로 구분된다.

동기식 복제

  • 마스터에서 트랜잭션을 커밋할 때 모든 복제 대상에게 변경사항이 기록되었다는 확인을 받은 후에 커밋을 완료하는 방식.
  • 예를 들어 두 노드 간 동기 복제에서는 한 노드에 쓰기가 발생하면 다른 노드에 해당 변경이 적용되고 확인 응답을 돌려받은 뒤에야 첫 노드의 트랜잭션이 완료된다.
  • 이러한 방식은 강한 일관성을 보장하여 한 노드 장애 시에도 데이터가 유실되지 않는 장점이 있지만, 매 쓰기마다 네트워크 왕복 지연이 추가되므로 쓰기 지연(latency) 이 늘어나는 단점이 있다.

비동기식 복제

  • 마스터가 자신의 변경을 커밋한 후에야 다른 노드로 복제를 진행한다.
  • 마스터는 일단 로컬에 쓰기 성공을 응답하고, 그 다음에 별도 스레드가 변경 로그를 슬레이브로 전송한다. 이 경우 마스터의 성능과 응답 속도는 좋아지지만, 만약 마스터가 변경을 복제하기 전에 장애가 발생하면 해당 변경이 슬레이브에 적용되지 못하고 유실될 위험이 있다.
  • 예를 들어 어떤 업데이트가 마스터에 커밋된 직후 마스터가 다운되면, 슬레이브에는 그 업데이트가 전달되지 못했기 때문에 데이터 불일치가 생기고 해당 업데이트는 사라지게 된다.
  • 비동기 복제 환경에서는 이러한 데이터 손실을 수초 이내로 줄이는 것이 목표지만, 시스템 부하나 네트워크 상태에 따라 Replication Lag(복제 지연)이 커지면 최악의 경우 수분 이상의 업데이트가 한꺼번에 잃어버릴 위험도 존재한다.
  • 따라서 세미 동기식처럼 중간 해법(일부 슬레이브 확인만 대기)이나, 멀티-마스터 구조에서는 충분히 많은 노드에 기록된 후 성공으로 간주(쿼럼)하는 기법 등으로 타협하기도 한다.

복제 활용의 장단점

  • 장점:

    • 고가용성 향상: 한 노드에 장애가 나도 다른 복제본으로 자동 페일오버 가능 (서비스 연속성).
    • 읽기 성능 확장: 슬레이브로 읽기 부하를 분산하여 스케일 아웃 가능.
    • 백업 용이: 실시간 복제로 백업을 유지하므로 데이터 복구 용이.
  • 단점:

    • 일관성 지연: 비동기 복제의 경우 마스터-슬레이브 간 데이터 반영에 지연이 발생, 일시적 불일치가 생길 수 있음.
    • 추가 지연 및 부하: 동기 복제 시 쓰기 지연 증가 및 시스템 부하 증가.
    • 복잡성 증가: 다중 마스터나 다수 슬레이브 환경에서는 충돌 해결이나 복제 지연 관리 등의 복잡성이 추가됨.
    • Replication Storm: 큰 규모의 클러스터에서, 새로운 노드 추가나 네트워크 이상으로 대량의 복제 트래픽이 발생하면 전체 시스템에 부하를 주는 복제 폭풍 현상이 발생할 수 있다. 이런 상황에서는 복제 대기열이 급격히 쌓이고 네트워크가 포화되어 응답 지연이 커지며, 심한 경우 복제 지연이 더 누적되는 악순환이 생길 수 있다. 복제 폭풍은 보통 대규모 배포 환경에서 문제 되지만, 증분 복제 조정이나 쓰로틀링으로 완화하도록 설계한다.

Split-Brain 위험

  • 멀티-마스터 구성에서는 네트워크 단절 등이 발생해 두 개 이상의 노드가 자신을 “마스터”로 잘못 인식하는 상황이 생길 수 있다.
  • 이를 스플릿 브레인이라 하며, 데이터가 서로 다른 경로에서 동시에 변경되어 데이터 불일치로 이어집니다.
  • 이러한 문제를 피하려면 리더 선출(후술)로 한 순간에는 하나의 리더만 쓰기를 허용하거나, 충돌 발생 시 최신 타임스탬프 우선과 같은 정책으로 자동 조정하는 기능이 필요하다. 하지만 자동 정책은 잘못 적용하면 데이터 손실을 야기할 수 있으므로, Paxos/Raft 같은 합의 알고리즘을 통해 다수결 합의를 거쳐 단일 결정을 내리는 방식이 많이 사용된다.
  • 예를 들어 과반수 노드의 승인을 받아야 쓰기가 확정되도록 하면, 네트워크 분할 시 소수 파티션은 더 이상 쓰기를 받지 못하게 되어 하나의 일관된 쪽만 리더 역할을 하게 된다.

요약하면, 복제는 분산 데이터베이스의 기본적인 고가용성/확장성 수단으로, 단방향 복제를 통해 손쉽게 읽기 성능을 높이거나 백업을 확보할 수 있고, 양방향 복제를 통해 여러 곳에서 쓰기를 처리할 수 있다. 다만 일관성-성능 트레이드오프(동기 vs 비동기)와 충돌 해결이라는 과제가 있으므로 시스템 요구사항에 맞게 신중히 설계해야 한다.

Oracle RAC와 공유 디스크 클러스터링

image

Oracle RAC(Real Application Clusters)

  • 오라클 데이터베이스에서 제공하는 클러스터링 솔루션으로, 여러 대의 DB 서버가 하나의 공용 데이터베이스를 동시에 액세스하는 공유 디스크(shared-disk) 구조
  • 쉽게 말해, 여러 DB 인스턴스가 동일한 저장소(SAN 등의 공용 디스크)에 있는 데이터를 공동으로 읽고 쓰면서 하나의 데이터베이스처럼 동작하는 것.
  • RAC의 목표는 인스턴스 수준의 고가용성확장성을 제공하는 것으로, 한 노드에 장애가 발생해도 다른 노드가 계속 서비스를 제공하고, 노드를 추가하여 성능을 높일 수 있다.

RAC의 장점

  • 여러 DB 인스턴스가 동시에 운영되므로 노드 중 한 곳에 장애가 생겨도 서비스가 중단되지 않고 지속된다 (자동 페일오버). 이는 무중단 운영이 중요한 은행권 등의 핵심 시스템에 유용하다.
  • 읽기 처리 능력 향상: 각 노드가 같은 데이터를 볼 수 있으므로 읽기 요청을 여러 서버로 분산해 처리할 수 있다. CPU와 메모리를 수평 확장(scale-out)하여 읽기 스루풋을 높일 수 있다.
  • 일정 범위 내에서는 노드를 추가하면 선형에 가까운 성능 향상을 얻을 수 있다고 하며, Oracle 공식 문헌상 최대 100개 노드까지 클러스터링을 지원한다.

RAC의 한계와 비용

  • 공유 저장소 병목: RAC의 근간은 모든 노드가 공동의 디스크에 읽고 쓴다는 점인데, 초대형 서비스 규모(Web-scale)에서는 이 중앙 저장소(SAN)가 성능 병목이 되고 전체 클러스터의 싱글 포인트 실패 지점이 될 수 있다.
    • Oracle은 이를 완화하기 위해 초고속 스토리지와 Cache Fusion(노드간 메모리 캐시 공유) 등의 기술을 쓰지만, 근본적으로 쓰기 작업은 한 데이터베이스 파일을 놓고 노드들이 락을 주고받으며 조정해야 하므로 노드가 늘어날수록 효율이 떨어질 수 있다. 실제로 “RAC로 노드를 많이 늘려도 결국 디스크나 락 경합 때문에 쓰기 성능은 늘어나지 않는다”는 업계 평가도 있다.
  • 복잡성 및 비용: RAC를 구축하려면 고성능의 공유 스토리지 인프라와 노드들 간의 초저지연 클러스터 네트워크(interconnect) 가 필요하며, Oracle 라이선스 비용도 매우 높다. 또한 설정과 운영이 복잡해서 전문 지식이 요구된다. 잘못 구성하면 오히려 단일 인스턴스보다 불안정해질 수 있고, 락 경합이나 캐시 동기화 문제로 지연 시간이 늘어나기도 한다.
  • 확장성 한계: RAC는 수평 확장(scale-out)의 형태이지만, 완전한 공유-낫싱 시스템만큼 자유롭게 노드를 늘려 무한 확장하는 개념과는 다르다. 일반적으로 2~4개 노드 구성으로 고가용성을 확보하는 용도로 많이 쓰이고, 노드 수가 많아질수록 오버헤드가 커지기 때문에 10개 이상의 대규모 RAC는 드물다. Oracle도 권장사항으로 애플리케이션이 RAC 환경에 최적화되어야 성능 효율을 얻을 수 있다고 말한다.
  • 하나의 거대한 머신을 이용하려면 비용이 많이 든다는 것도 문제다.

요약하면, Oracle RAC는 공유 디스크 기반 클러스터링의 대표 격으로서, 고가용성과 일정 수준의 확장성을 얻기 위한 솔루션이다. 대기업 전사 시스템처럼 다운타임을 최소화해야 하는 경우나, 단일 호스트가 감당 못 할 정도로 CPU/메모리를 늘리고 싶을 때 고려된다. 다만 구축/운영의 복잡성과 높은 비용 때문에, 웹 스타트업 등에서는 잘 사용하지 않으며 대신 이후 설명할 샤딩 같은 공유-낫싱 방식이나 클라우드 분산 DB를 선호한다. (실제로 Oracle 자체도 Web-scale 서비스에서는 RAC보다는 Data Guard (복제)나 Sharding 옵션 등을 권장하는 추세다.)

샤딩(Sharding) – 수평 분할을 통한 확장

  • 대용량 데이터를 다루는 분산 시스템에서 흔히 사용하는 수평 파티셔닝(horizontal partitioning) 기법으로, 테이블의 행(row)을 여러 분할로 나누어 각각 별도의 데이터베이스 인스턴스에 저장하는 방법이다.
  • 쉽게 말해, 하나의 거대한 DB를 여러 대의 작은 DB로 쪼개어 담는 것.
  • 각 샤드(분할된 DB 조각)는 동일한 스키마(테이블 구조)를 갖지만 저장하는 데이터 범위는 서로 다르고 중복되지 않으며, 모든 샤드의 데이터를 합치면 원래 전체 데이터집합이 된다.
  • 하나의 데이터베이스를 다수의 노드로 쪼개 분산함으로써 얻는 주된 이점은 수평 확장성비용 효율성.

  • 일반적으로 샤드를 나누는 기준으로 샤드 키(shard key)를 사용하며, 예를 들어 사용자 ID 범위나 해시값 등을 기준으로 각 샤드에 할당한다. 이렇게 하면 각 샤드는 샤드 키에 대응되는 데이터만 저장하게 된다.
  • 이때 분산키 선택이 중요한데, 주로 특정 속성 값 범위나 해시를 기준으로 균등하게 나누어 모든 샤드에 부하가 고르게 분산되도록 한다.
  • 샤딩은 데이터가 쉐어드-낫싱(shared-nothing) 구조로 완전히 분리되므로, 한 샤드의 장애가 전체 시스템을 바로 마비시키지 않고 (해당 샤드 데이터에만 영향) 확장 시에도 다른 샤드에 영향이 적다.

샤딩을 사용하는 이유와 장점

  • 무한에 가까운 수평 확장성: 단일 머신의 한계를 뛰어넘어 노드를 추가함으로써 저장용량과 처리량을 계속 늘려갈 수 있다.
  • 예를 들어 DB가 너무 커지면 더 큰 서버로 수직 확장하는 대신, 샤드를 하나 추가하고 데이터를 분산함으로써 선형적인 확장이 가능하다.
  • 저렴한 비용으로 규모 확장: 특수한 대형 장비나 고가의 범용 서버 대신, 일반 상용 하드웨어(commodity hardware) 여러 대를 조합해 대용량 데이터를 다룰 수 있으므로 가격 대비 성능이 좋다. 즉, 규모가 커질수록 비싼 고사양 한 대보다 저렴한 서버 여러 대로 구성하는 편이 경제적이다.
  • 향상된 성능: 샤딩하면 각 샤드가 전체 데이터의 일부만 담고 있으므로, 개별 쿼리의 대상 데이터 양이 줄어들어 조회 성능이 향상될 수 있다. 예를 들어 1억 건 테이블을 10개 샤드로 나누면 각 샤드는 1천만 건만 갖게 되어, 특정 샤드에 질의할 때 스캔해야 할 행 수가 줄어드는 효과가 있다. 또한 샤드들이 병렬로 쿼리를 처리하면 병렬 처리 효과도 얻을 수 있다.
  • 지리적 분산 및 레이턴시 감소: 필요에 따라 각 샤드를 다른 데이터센터나 지역에 배포할 수 있다. 예를 들어 사용자 지역별로 샤드를 분리해 해당 지역에 가까운 서버에 위치시킴으로써, 그 지역 유저들의 지연 시간을 단축할 수 있다.
  • 장애 격리: 한 샤드가 문제를 일으켜도 다른 샤드에는 영향이 덜 가므로, Fault Isolation 측면에서 유리하다. 전체 데이터의 일부분만 장애 영향권에 들기 때문에, 부분 장애로 서비스의 나머지 부분은 지속 운영할 수도 있다.

샤딩시 고려해야 할 단점과 도전과제

  • 애플리케이션 복잡도 증가: 샤딩을 도입하면 어떤 데이터가 어느 샤드에 있는지를 애플리케이션이 인지하고 처리해야 한다. 응용 단에서 샤드 키를 기준으로 쿼리 라우팅 로직을 구현하거나, 모든 쿼리에 샤드 키 조건을 포함시키는 등의 개발 부담이 생긴다. (만약 이러한 로직을 애플리케이션 대신 해주는 미들웨어나 프록시를 사용한다면 복잡도를 위임할 수는 있다.)
  • 운영 복잡성: 샤드 수가 늘어나면 관리해야 할 데이터베이스 인스턴스가 그만큼 많아지므로, 모니터링, 백업, 스키마 변경 등의 운영 작업이 복잡해진다. 예를 들어 테이블 스키마를 변경하려면 모든 샤드에 적용해야 하고, 샤드가 100개면 100개의 DB에 일관성 있게 변경을 반영해야 한다.
  • Cross-Shard 질의 제한: 샤딩 환경에서는 조인이나 글로벌 집계 등의 쿼리가 샤드를 넘어서 수행되어야 하는 경우 어려움이 있다. 한 샤드에 필요한 데이터가 모두 있으면 문제가 없지만, 여러 샤드의 데이터를 모아야 하는 질의는 애플리케이션 계층에서 병렬 질의 후 결과를 합치는 로직을 짜거나 해야 한다. (일부 분산 SQL 솔루션은 프록시가 내부적으로 해주기도 하지만 일반적으로 복잡성이 높다.)
  • 불균형 및 재샤딩 이슈: 초기에는 데이터가 균등하게 분산되도록 샤드를 나누어도, 시간이 지나면서 특정 샤드에 데이터가 몰리거나(예: 핫스팟) 부하가 치우칠 수 있다. 이 경우 샤드 재조정(resharding) 이나 분할이 필요해진다. 실시간으로 데이터를 이동시키면서 샤드 구조를 변경하는 것은 운영상 까다로운 작업이다.
  • 트랜잭션 제약: 데이터가 서로 다른 샤드에 있으면 다중 샤드 간의 ACID 트랜잭션을 보장하기 어렵다. 분산 트랜잭션(2PC)를 도입하면 성능 저하와 복잡성이 급격히 커지기 때문에, 대부분의 샤딩 환경에서는 한 트랜잭션이 가급적 단일 샤드 내에서만 일어나도록 데이터 모델을 제한한다.
  • 참조 데이터 중복: 모든 샤드에 공통으로 필요한 작은 테이블(예: 국가 코드표 등)은 각 샤드에 복제하여 저장하기도 한다. 이 경우 데이터 일관성 유지를 위해 변경 시 모든 샤드 업데이트가 필요하다.

샤딩 구현 방식

샤딩은 구현 방법에 따라 드라이버/애플리케이션 레벨프록시/미들웨어 레벨로 나눌 수 있다.

  • 애플리케이션 레벨 샤딩: 말 그대로 응용 프로그램 코드가 샤드 구분 로직을 담고 직접 각 DB 샤드에 질의를 보내는 방식.
    • 예를 들어 사용자 ID % N으로 샤드를 결정하고, 각 쿼리를 보낼 DB 커넥션을 코드에서 선택하는 식
    • 이 방식은 단순하지만 언어별로 따로 구현해야 하고, 애플리케이션이 DB 구조를 많이 알아야 하기 때문에 유지보수 부담이 있다.
  • 프록시 레벨 샤딩: 애플리케이션은 하나의 가상 DB에 질의하듯이 하면, 중간의 프록시 시스템이 쿼리를 받아 샤드로 라우팅해주는 방식.
    • 프록시가 샤드 키를 분석해 해당 샤드로 질의를 전달하고, 각 샤드의 응답을 취합해 애플리케이션에 결과를 반환.
    • 프록시 레벨의 장점은 언어 독립적으로 동작하여 모든 애플리케이션에 투명하게 샤딩을 제공할 수 있다는 점.
    • 예로 Apache ShardingSphere-ProxyVitess, MySQL Router, ProxySQL 등이 이러한 역할을 수행.

프록시 시스템과 데이터베이스 접근 (MariaDB MaxScale 등)

분산 DB 환경에서는 애플리케이션과 여러 DB 노드 사이에 데이터베이스 프록시(DB proxy) 를 두어 접근을 제어하는 경우가 있다. 프록시는 쉽게 말해 클라이언트의 요청을 받아 적절한 DB 서버로 중계해주는 중간 계층 이다. 이러한 시스템을 사용하면 애플리케이션은 마치 단일 데이터베이스에 접속하듯이 프록시에게 질의하고, 프록시가 내부적으로 부하 분산, 읽기/쓰기 분리, 장애 조치(failover) 등을 수행하여 클러스터를 투명하게 관리해준다.

MariaDB MaxScale

대표적인 예로 MariaDB MaxScale을 들 수 있다.

  • MaxScale은 MySQL/MariaDB 전용 스마트 프록시로 개발되어, 클라이언트로부터 오는 SQL 쿼리를 파싱한 후 백엔드의 여러 DB 서버로 forwarding하는 역할을 한다.
  • 예를 들어 SELECT 쿼리는 슬레이브 노드들로 보내고, INSERT/UPDATE 등 쓰기 쿼리는 마스터 노드로 보내는 식으로 규칙 기반의 라우팅을 수행할 수 있다.
  • 또한 백엔드 서버들의 상태를 모니터링해서 마스터 장애 발생 시 자동으로 새로운 마스터로 연결을 돌리는 등 고가용성 기능도 제공한다.
  • 이러한 모든 처리는 애플리케이션 레벨에서는 보이지 않기 때문에, 개발자는 여러 DB 인스턴스를 일일이 관리하는 복잡성을 느끼지 않고 하나의 논리 DB에 접속하듯 코딩하면 된다.

다른 예로 ProxySQL, MySQL Router(MySQL 공식 프록시) 등이 있으며, PostgreSQL 계열에는 PgBouncer, HAProxy 등을 조합해 사용하기도 한다. 또한 앞서 언급한 샤딩 프레임워크들도 프록시 역할을 수행하는 경우가 있다.

프록시 시스템의 주요 기능

  • 부하 분산 (Load Balancing): 다수의 DB 노드들 사이에 쿼리를 분산시켜 특정 노드에 쏠리지 않도록 한다. 예를 들어 MaxScale의 readwritesplit 모드는 쓰기는 마스터로, 읽기는 슬레이브들로 자동 분배한다.
  • 고가용성 및 장애 자동처리: 프록시는 백엔드 노드를 지속적으로 헬스 체크하여, 노드 장애 시 자동으로 연결을 다른 노드로 돌려주거나 (Failover) 장애 노드를 서비스 풀에서 제외한다. 이를 통해 클라이언트는 별도 재시도 로직 없이도 장애에 대응할 수 있다.
  • 연결 관리와 보안: 많은 애플리케이션 연결을 프록시가 받아 단일 또는 최소 연결로 백엔드에 연결하므로 DB 커넥션 자원을 효율적으로 사용할 수 있고, SQL 방화벽이나 감사(logging) 등의 정책을 프록시에서 적용할 수 있다.
  • 쿼리 라우팅 / 변환: 샤딩된 환경에서는 프록시가 샤드 키를 분석해 해당 샤드로 쿼리를 전달하거나, 특정 DB로만 보내야 하는 쿼리를 식별해 처리한다. 일부 프록시는 읽기 전용 쿼리를 캐시하거나, 지연이 큰 노드에 대한 쿼리를 지양하는 등의 스마트 라우팅을 하기도 한다.

물론 모든 경우에 프록시가 필요한 것은 아니다. 단순 Master-Slave 구조에서 클라이언트 드라이버가 여러 호스트를 지원하는 경우(예: 일부 드라이버는 읽기/쓰기 분리를 자체 지원), 혹은 어플리케이션에서 로드밸런싱 로직을 구현한 경우에는 프록시 없이도 운용이 가능하다. 하지만 이기종 언어/플랫폼 지원, 운영 정책 중앙화 등의 이유로 중간 프록시를 두는 것이 관리에 용이한 경우가 있다.

한 마디로, 프록시 시스템은 분산 데이터베이스 환경의 접착제 역할을 하여, 여러 노드로 구성된 데이터 계층을 애플리케이션에는 마치 하나의 일관된 데이터베이스처럼 보이게 해주는 매우 유용한 도구다.

분산 DB의 일관성과 내결함성 (Consistency & Fault Tolerance)

분산 데이터베이스에서는 데이터의 일관성(consistency)내결함성(fault tolerance) 을 유지하는 것이 핵심 과제다. 분산 DB에서는 어떤 노드도 전체 데이터의 완전한 사본을 갖고 있지 않지만, 각 데이터 조각은 충분한 중복 복제나 코딩으로 여러 노드에 걸쳐 저장되므로 일부 노드가 손실되어도 데이터를 잃지 않도록 한다.

Cassandra 예시

  • 예를 들어, NoSQL 분산 DB인 Apache Cassandra의 경우 데이터를 일관된 해시 분배를 통해 클러스터 노드들에 분산 저장하며, 하나의 데이터 항목을 여러 노드에 복제해 둔다.
  • 복제 계수(replication factor) 를 3으로 설정했다면, 각 데이터 행(row)은 해시 링 상에서 정의된 3개의 노드에 저장되고 모든 노드는 자신이 담당하는 데이터의 복제본들을 유지한다.
  • 그 결과, 한 노드가 다운되더라도 해당 노드에 있던 모든 데이터의 사본이 다른 두 노드에 남아 있어 데이터 손실이 니다. (Cassandra에서는 모든 복제본이 동등하며 특별한 마스터 노드가 없고, 설정한 복제 개수 만큼 각 데이터가 분산 저장된다.)
  • 따라서 4개 노드 클러스터에 복제 계수 3으로 운용하면, 겉보기에는 “각 노드가 전체 데이터의 75% 가량을 담고 있다”고 할 수 있지만, 사실은 각 데이터가 3개의 노드에 중복되어 있고 어느 하나의 25% 부분도 잃지 않도록 설계된 것이다.

CockroachDB 예시

  • CockroachDB 와 같은 NewSQL 분산 DB도 데이터를 작은 범위 단위(range) 로 쪼개어 다수 노드에 복제해 저장한다.
  • CockroachDB는 내부적으로 Raft 합의 알고리즘을 사용하여, 각 데이터 범위(range)의 복제본들 중 하나를 리더(leaseholder) 로 선출하고 그를 통해 모든 쓰기/읽기가 이루어지게 하여 강한 일관성을 보장한다.
  • 기본 설정으로 3중 복제를 하여, 한 노드(리더) 장애 시 나머지 복제본 중 하나가 새로운 리더로 자동 승계되어 계속 서비스를 제공한다.
  • CockroachDB의 경우 다수결(쿼럼) 쓰기가 기본이므로, 3개 중 2개 노드만 살아있어도 데이터베이스 운영이 가능하지만(한 노드 장애 허용), 반대로 전체 노드 중 과반수 이상이 응답 불능이면 쓰기를 중단하여 일관성을 지킨다 (CP 지향).

정리하면, 각 노드는 전체 데이터의 일부만을 저장하지만, 데이터의 각 단위는 여러 노드에 겹쳐 저장되므로 어느 한 노드가 사라져도 데이터가 보전 된다. 이러한 중복성을 확보하는 방식으로 동일한 데이터를 여러 복제본으로 단순 저장하는 방법도 있고, erasure coding(소위 패리티) 기법을 활용하는 방법도 있다.

복제(replication)

  • 가장 흔한 방식으로, 데이터를 그 자체로 여러 노드에 복사해 두는 것.
  • 복제본 수를 늘릴수록 내결함성은 높아지지만 저장 공간이 그만큼 추가로 필요하다. Cassandra, CockroachDB, MongoDB 등 대부분의 분산 DB가 이 방식을 사용한다.
  • 예를 들어 Replication Factor = 노드 수로 설정하면 모든 노드가 전체 데이터를 갖게 되어 강한 내결함성을 얻지만, 저장/갱신 비용이 많이 들 것이다.
  • Erasure Coding(패리티 분산): 일부 시스템은 데이터를 그대로 복제하지 않고, RAID 5/6와 유사하게 데이터를 조각내고 패리티 정보를 분산 저장한다.
    • 예를 들어 원본 데이터를 3조각으로 나눠 서로 다른 3노드에 저장하고, 추가로 1개 패리티 조각을 4번째 노드에 저장하면, 네 노드 중 어느 하나가 사라져도 남은 조각들로 원본을 복원할 수 있다.
    • 이 경우 각 노드가 전체 데이터의 75%만 갖는 셈이지만, 1개 노드 분량의 오버헤드(25%)로 내결함성을 확보하는 셈.
    • Erasure coding은 저장 효율은 높이지만 복원 연산이 상대적으로 복잡하고 성능에 영향이 있어, 주로 대용량 분산 스토리지(HDFS, Ceph 등)에서 사용되고 실시간 DB 트랜잭션 시스템에서는 덜 쓰인다.

결과적으로, 분산 데이터베이스는 부분적인 데이터 분산 + 중복 저장 전략으로 확장성과 내결함성을 동시에 추구한다. 추가로, CAP 이론으로 알려져 있듯이 일관성(Consistency)가용성(Availability) 사이의 트레이드오프가 존재하는데, Cassandra같이 AP 지향 시스템은 일시적 불일치를 허용하면서 모든 노드 가용성을 높인 반면 CockroachDB같이 CP 지향 시스템은 장애 시 일부 서비스 중단을 감수하더라도 일관성을 우선한다. 이러한 차이는 서비스 요구사항(강한 일관성이 필요한 금융 vs 약한 일관성 허용 가능한 소셜 피드 등)에 따라 선택하게 된다.

MySQL Cluster의 아키텍처와 특징 (NDB Cluster)

MySQL Cluster(정식 명칭: MySQL NDB Cluster)은 MySQL 데이터베이스에 내장된 분산 클러스터링 기술로, 공유-낫싱(shard 기반) 다중 마스터 구조를 갖춘 시스템이다.

  • 기본 아이디어는 여러 대의 데이터 노드(Data Node) 에 데이터를 자동으로 분할 저장하고, 여러 SQL 노드(SQL Node) 를 통해 그 데이터를 접근하는 형태이다.
  • 각 SQL 노드는 MySQL 서버 프로세스이며, 클러스터의 데이터 노드에 네트워크로 접속하여 질의를 처리한다.
  • 그리고 별도로 관리 노드(Management Node) 가 있어서 클러스터 설정과 노드 모니터링을 담당한다.
  • 이러한 구조 덕분에 MySQL Cluster는 하나의 MySQL 논리 DB를 다중 물리 노드에 분산하여 고가용성과 확장성을 얻는다.

데이터 노드들이 데이터를 분산 저장하며 서로 동기 복제로 데이터 일관성을 유지하고, SQL 노드(MySQL 서버)들은 애플리케이션의 질의를 받아 데이터 노드에 접근한다. 관리 노드는 클러스터의 구성 정보를 보관하고 각 노드의 상태를 조율한다.

  • 자동 샤딩 및 다중 마스터: MySQL Cluster는 테이블의 프라이머리 키를 해싱하여 자동으로 데이터를 수평 분할하며, 이 과정은 사용자에게 투명하게 이루어진다.
  • 클러스터의 모든 데이터 노드들은 각자 할당받은 파티션의 데이터를 관리하고, 모든 노드가 쓰기 연산을 받아들일 수 있는 멀티-마스터 구조다.
  • 즉 어떤 SQL 노드에서 INSERT를 하든, 해당 레코드가 속한 데이터 노드로 자동 전송되어 커밋되고, 그 변경이 즉시 다른 복제 노드들에도 적용된다.
  • MySQL Cluster는 이처럼 어느 노드에서의 업데이트도 실시간으로 다른 노드에 전파되므로 모든 클라이언트가 최신 데이터를 공유할 수 있다.
  • 또한 하나의 트랜잭션이 여러 샤드(데이터 노드)에 걸쳐 있어도 내부적으로 이중 페이즈 커밋(2PC) 프로토콜로 모든 관련 노드에서 원자적으로 커밋을 수행하므로 분산 트랜잭션을 지원한다.
  • 강력한 일관성과 동기 복제: MySQL Cluster의 데이터 복제는 기본적으로 동기식으로 이루어진다.
  • 노드 그룹(node group)이라는 개념으로 2개 이상의 데이터 노드를 한 묶음으로 구성하고, 동일 그룹 내 노드들끼리 데이터를 완전 미러링한다.
  • 일반적으로 2개의 복제본(2노드 그룹)을 사용하며, 한 노드 그룹 내 한 노드가 장애 시 즉시 다른 노드가 대응하여 서비스 지속이 가능하다. 커밋 시에는 두 노드에 모두 기록이 확인된 후 완료되므로 단일 노드 장애로 인한 데이터 손실이 없다.
  • 인메모리 지향 및 분산 인덱싱: MySQL Cluster의 NDB 스토리지 엔진메모리 기반으로 동작하도록 최적화되어 있다. 모든 인덱싱된 컬럼은 분산된 메모리에 저장되고 관리되며, 비인덱스 컬럼은 옵션에 따라 메모리 또는 디스크에 저장할 수 있다 (디스크에 저장해도 인메모리 캐시를 통해 I/O를 최소화). 따라서 프라이머리 키 조회와 같은 인덱스 기반 질의는 네트워크 통신만 빼면 메모리 조회 속도로 매우 빠르게 처리된다. 흔히 “모든 노드가 모든 인덱스를 가진다”는 오해가 있지만, 사실은 “각 노드가 자신이 맡은 데이터의 인덱스를 메모리에 가지고 있다”는 의미입니다. MySQL Cluster는 데이터를 파티션하면서 해당 파티션의 인덱스도 함께 분산시키므로, 모든 노드에 전체 인덱스를 복제하여 저장하는 것이 아니다다 (그렇게 하면 메모리 낭비이자 일관성 유지도 어렵겠지요). 예를 들어 4개 노드로 데이터를 분산했다면 각 노드는 1/4 데이터와 그 부분의 인덱스만 메모리에 가지고 있다. 질의 시 MySQL 서버(SQL 노드)는 어떤 데이터가 어느 노드에 있는지 파티션 해시 알고리즘으로 계산하거나 클러스터 맵을 참조하여 해당 노드로 질의를 보내기 때문에, 인덱스 룩업은 정확한 노드에서 수행되고 매우 효율적.
  • 쿼리 처리 및 조인: 분산된 데이터임에도 불구하고, MySQL Cluster를 사용할 때 애플리케이션은 일반 MySQL 질의를 사용한다. 분산된 다수의 SQL 노드가 내부적으로 협력하여 조인이나 집계 연산도 지원한다. 예를 들어 A테이블과 B테이블이 서로 다른 노드에 분산되어 있더라도, SQL 노드는 먼저 한쪽 노드에서 필요한 키를 추출하고 그 결과를 다른 노드로 보내 조인을 수행하는 방식으로 동작한다 (분산 조인). 물론 모든 조인이 이런 분산 수행을 효율적으로 처리할 수 있는 것은 아니므로, MySQL Cluster에서는 작업을 샤드 현지화(locality)하기 위해 파티셔닝 키를 잘 선택하는 것이 중요한다. 개발자가 의도적으로 관련 데이터가 같은 노드에 위치하도록 파티션 키를 설정하면 (예: 자주 조인되는 두 테이블에 동일한 파티션 키 사용), 네트워크를 가로지르는 조인을 줄여 성능을 높일 수 있다.
  • 고가용성 및 무중단 운영: MySQL Cluster는 완전한 공유-낫싱 구조싱글 포인트 실패 지점이 없도록 설계되었다. 어느 하나의 노드나 디스크에 장애가 발생해도, 다른 노드들이 복제된 데이터를 가지고 있기 때문에 서비스가 중단되지 않습니다. 또한 롤링 업그레이드(노드를 하나씩 순차 업그레이드)나 온라인 노드 추가/제거 등을 지원하여, 클러스터를 내린 상태가 아니어도 동적으로 용량 확장과 유지보수가 가능한다. 다만 메이저 스키마 변경 등은 경우에 따라 잠시 락이 걸릴 수 있다. MySQL Cluster는 이러한 특성 덕분에 통신사의 Home Location Register 같은 24x365 서비스에 오랫동안 활용되어 왔다.

요약하자면 MySQL Cluster는 MySQL의 분산 클러스터링 솔루션으로, 자동 샤딩, 동기식 복제에 의한 강한 일관성, 인메모리 성능, 멀티마스터 쓰기 처리 등을 특징으로 한다. 이는 앞서 설명한 일반적인 샤딩+복제 아키텍처를 데이터베이스 엔진 수준에서 구현한 것이며, 개발자가 특별한 샤딩 로직을 짜지 않아도 MySQL 쿼리를 통해 대용량 데이터를 처리할 수 있게 했다. 다만 운영 복잡성이 높고 메모리 자원 요구량이 많다는 단점이 있어, 반드시 필요한 경우에 사용된다. (예: 전세계적으로 분산된 사용자 데이터를 다루면서도 SQL 트랜잭션 일관성이 필요할 때.)