정보창고 정보창고

Consistent Hashing의 Virtual Node가 서버 증설 시 데이터 쏠림을 줄이는 방식

읽는 시간 약 10분

분산 시스템의 해결사 가상 노드의 역할과 중요성

현대적인 대규모 서비스는 여러 서버를 연결하여 방대한 데이터를 처리합니다. 이때 각 데이터를 어느 서버에 저장하고 불러올지 결정하는 방식이 매우 중요하며, 이를 데이터 분산이라고 합니다.

가장 직관적인 방법은 데이터의 해시값을 서버 개수로 나눈 나머지를 이용하는 방식입니다. 그러나 서버가 추가되거나 제거되면 나누는 기준 자체가 달라져 많은 데이터의 저장 위치를 다시 계산해야 하는 문제가 발생합니다. 이를 완화하기 위해 등장한 방식이 일관된 해싱(Consistent Hashing)이며, 여기서 데이터 분포를 더욱 균일하게 만드는 핵심 요소가 가상 노드(Virtual Node)입니다.

가상 노드는 물리 서버 한 대를 여러 개의 논리적인 지점으로 표현하여 해시 링 곳곳에 배치하는 기법입니다. 물리 서버가 링의 한 지점만 담당할 때보다 여러 구간을 나누어 담당하므로 특정 서버에 데이터가 집중될 가능성을 줄일 수 있습니다.

일관된 해싱과 데이터 쏠림의 관계 이해하기

일관된 해싱은 해시 공간의 양 끝이 연결된 원형의 링 구조를 가정합니다. 서버와 데이터 키는 각각 해시 함수를 거쳐 링 위의 특정 위치에 배치되며, 데이터는 일반적으로 자신의 위치에서 시계 방향으로 가장 먼저 만나는 서버에 할당됩니다.

이 방식은 서버가 추가되거나 제거되더라도 전체 데이터를 다시 배치하지 않고 영향을 받은 일부 구간만 이동시킨다는 장점이 있습니다. 하지만 물리 서버마다 링 위의 위치를 하나씩만 가진다면 서버 사이의 간격이 불규칙하게 만들어질 수 있습니다.

어떤 서버는 매우 넓은 구간을 담당하고 다른 서버는 작은 구간만 담당하면서 데이터와 요청이 특정 서버로 몰리는 현상이 발생할 수 있습니다. 서버를 추가하더라도 새로운 서버가 인접한 일부 구간만 넘겨받기 때문에 전체 부하가 기대만큼 균등해지지 않을 수도 있습니다.

여기서 가상 노드가 등장합니다. 물리 서버 한 대를 여러 개의 가상 노드로 표현해 링 전체에 분산하면, 각 서버가 서로 떨어진 여러 구간을 담당하게 됩니다. 하나의 넓은 구간에 부하가 몰리더라도 동일한 물리 서버가 모든 부하를 떠안지 않게 되어 전체 분포가 더 균일해집니다.

가상 노드를 활용한 데이터 쏠림 방지 메커니즘

균일한 구간 분산

물리 서버마다 여러 개의 가상 노드를 생성해 해시 링 곳곳에 배치합니다. 물리 서버는 한 대지만 논리적으로는 여러 위치를 점유하므로, 특정 구간의 크기가 우연히 커지더라도 그 영향이 여러 서버로 나뉠 가능성이 높아집니다.

가상 노드를 사용한다고 데이터가 정확히 같은 양으로 나뉘는 것은 아닙니다. 다만 서버마다 하나의 지점만 사용하는 방식보다 통계적인 편차를 줄이는 데 유리합니다.

서버 증설 시 일부 구간만 이동

새로운 물리 서버가 추가되면 해당 서버를 나타내는 가상 노드들이 링에 배치됩니다. 새 가상 노드의 이전 구간을 담당하던 기존 서버는 해당 범위의 데이터만 새로운 서버로 전달합니다.

따라서 모든 데이터를 처음부터 다시 해싱하거나 전체 서버 사이에서 재배치할 필요가 없습니다. 새 서버가 맡게 된 일부 토큰 범위만 이동시키므로 증설 과정의 네트워크와 디스크 부하를 줄일 수 있습니다. Cassandra에서도 여러 토큰 범위를 한 노드에 배정하는 가상 노드를 사용하며, 신규 노드가 여러 기존 노드로부터 데이터를 스트리밍받을 수 있도록 설계되어 있습니다.

서버 성능에 따른 가중치 적용

구현 방식에 따라 고성능 서버에는 더 많은 가상 노드나 더 넓은 토큰 범위를 할당하고, 저성능 서버에는 적은 범위를 맡기는 가중치 전략을 사용할 수 있습니다.

다만 모든 분산 시스템이 가상 노드 개수만으로 서버별 가중치를 조절하는 것은 아닙니다. 시스템마다 토큰, 파티션, 슬롯을 관리하는 방식이 다르므로 실제 제품의 리밸런싱 정책을 먼저 확인해야 합니다.

실제 운영 환경에서의 활용 사례

Apache Cassandra는 하나의 물리 노드가 여러 토큰 범위를 담당하는 가상 노드 방식을 지원합니다. 이를 통해 새 노드를 추가할 때 여러 기존 노드로부터 데이터 범위를 전달받을 수 있으며, 클러스터를 점진적으로 확장하는 데 활용합니다.

반면 Redis Cluster는 전통적인 가상 노드 기반의 일관된 해싱 링을 사용하지 않습니다. 전체 키 공간을 16,384개의 고정된 해시 슬롯으로 나누고, 각 마스터 노드에 슬롯 범위를 할당합니다. 서버를 추가하거나 제거할 때는 가상 노드가 아니라 해시 슬롯과 해당 슬롯에 속한 키를 다른 노드로 이동합니다.

두 방식은 표현은 다르지만, 데이터를 작은 논리 단위로 나누어 일부 단위만 재배치한다는 공통점이 있습니다. 따라서 특정 제품을 설명할 때는 가상 노드와 해시 슬롯을 같은 개념으로 단정하지 않고, 해당 시스템이 실제로 사용하는 분산 단위를 구분해야 합니다.

가상 노드 설정 시 고려해야 할 전문가의 조언

가상 노드 개수를 무조건 늘린다고 항상 좋은 결과가 나오는 것은 아닙니다. 가상 노드가 많아지면 데이터 분포를 세밀하게 조정하기 쉬워지지만 다음과 같은 비용도 함께 증가할 수 있습니다.

  • 노드와 토큰 범위에 관한 메타데이터 증가
  • 장애 복구 시 연결되는 데이터 스트리밍 대상 증가
  • 복제본 배치와 가용성 계산의 복잡도 상승
  • 리밸런싱 과정에서 관리해야 하는 데이터 구간 증가

모든 시스템에 공통으로 적용할 수 있는 최적의 가상 노드 개수는 없습니다. 현재 Apache Cassandra의 운영 권장 문서에서는 num_tokens: 16을 설정 예시로 제시하며, 토큰이 많아지면 확장은 유연해지지만 더 많은 노드와 데이터를 공유하게 되어 가용성 측면의 비용이 생길 수 있다고 설명합니다.

따라서 100개나 256개와 같은 특정 숫자를 무조건 적용하기보다 데이터 크기, 서버 수, 복제 계수, 장애 복구 시간과 리밸런싱 부하를 함께 측정해 결정해야 합니다.

흔한 오해와 사실 관계 바로잡기

첫째, 가상 노드를 사용하면 데이터가 완벽하게 1/N로 나뉜다는 생각은 정확하지 않습니다. 가상 노드는 데이터와 요청의 분포 편차를 줄이는 방식이지, 각 서버가 항상 동일한 저장 용량과 처리량을 사용하도록 보장하는 장치는 아닙니다.

데이터 키 자체가 특정 값에 몰려 있거나 일부 키의 접근 빈도가 지나치게 높다면, 토큰 범위가 균등해도 요청 부하는 한쪽으로 치우칠 수 있습니다. 가상 노드는 데이터 범위의 불균형을 완화하지만 인기 키로 인한 핫스팟까지 자동으로 해결하지는 않습니다.

둘째, 가상 노드를 추가하면 모든 데이터를 다시 이동해야 한다는 생각도 사실과 다릅니다. 가상 노드의 소유권이 변경되면 영향을 받은 토큰 범위의 데이터는 리밸런싱해야 하지만, 전체 데이터셋을 다시 배치할 필요는 없습니다.

비용 효율적인 시스템 설계를 위한 팁

클라우드 환경에서는 서버 사양과 처리 능력이 서로 다를 수 있습니다. 시스템이 가중치 배치를 지원한다면 고성능 서버에 더 넓은 데이터 범위를 맡기고 저성능 서버의 범위를 줄여 자원 성능에 맞게 부하를 분배할 수 있습니다.

다만 가상 노드의 개수만으로 실제 부하가 정확히 결정되는 것은 아닙니다. 데이터 크기와 키별 요청 빈도, 복제본 수, 읽기·쓰기 패턴이 모두 다르기 때문입니다. 서버별 저장 용량뿐 아니라 요청 수, 지연 시간과 네트워크 사용량도 함께 모니터링해야 합니다.

읽기 성능을 높이기 위한 복제본 배치와 일관성 수준은 가상 노드와 구분해서 설계해야 합니다. 가상 노드는 데이터 범위의 소유권을 나누는 장치이고, 복제 정책은 동일한 데이터를 어느 장애 도메인에 몇 개 저장할지를 결정하는 별도의 문제입니다.

자주 묻는 질문과 답변

가상 노드 개수를 변경하면 이미 저장된 데이터가 모두 유실되나요?

아니요, 데이터가 유실되지 않습니다. 다만 가상 노드의 위치가 바뀌면 데이터의 주인(Owner)이 바뀔 수 있으므로, 바뀐 규칙에 따라 일부 데이터를 새로운 서버로 이동시키는 데이터 리밸런싱 작업은 필요합니다.

가상 노드 정보를 저장하는 별도의 데이터베이스가 필요한가요?

시스템마다 다르지만, 보통 각 클라이언트나 프록시 서버가 가상 노드 정보를 메모리에 캐싱하여 사용합니다. 별도의 데이터베이스보다는 클러스터의 메타데이터나 ZooKeeper와 같은 코디네이션 서비스를 통해 정보를 공유하는 방식을 사용할 수 있습니다.

서버를 삭제할 때는 어떻게 처리하나요?

서버를 삭제할 때는 해당 서버가 담당하던 가상 노드를 링에서 제거합니다. 이후 그 범위를 맡게 되는 다른 서버로 데이터를 안전하게 이전해야 합니다. 단순히 서버를 먼저 종료하기보다 데이터 스트리밍과 복제 상태를 확인한 뒤 제거하는 것이 안전합니다.

가상 노드는 물리 서버를 논리적으로 여러 지점에 분산하여 데이터 범위가 특정 서버에 집중될 가능성을 줄이는 기술입니다. 핵심은 물리 서버를 실제로 여러 대로 나누는 것이 아니라, 하나의 서버가 해시 공간의 여러 구간을 담당하도록 만드는 데 있습니다.

서비스가 성장하면서 서버를 추가해야 한다면 가상 노드를 무조건 많이 만드는 것보다, 현재 시스템의 데이터 분포와 리밸런싱 비용을 측정하는 과정이 먼저입니다. 적절하게 설계된 가상 노드는 서버 증설 시 이동해야 하는 데이터의 범위를 제한하면서도 전체 클러스터의 부하를 더욱 균형 있게 유지하는 데 도움을 줍니다.

정보창고

함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.