본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

일관된 해시 문서
일관된 해시
일관된 해시의 개념동작 원리구현복잡도연습 문제실무 활용
토론

일관된 해시 안내서 · 1/6

일관된 해시의 개념

데이터나 요청을 여러 서버에 나눠 담을 때 가장 먼저 떠오르는 방법은 hash(key) % N입니다. 키를 해시하고 서버 수 N으로 나눈 나머지를 서버 번호로 쓰는 방식입니다. 계산이 빠르고 분포도 고르지만, 서버 수가 바뀌는 순간 거의 모든 키의 자리가 바뀐다는 큰 약점이 있습니다. 일관된 해시(consistent hashing)는 이 문제를 풀기 위해 1997년 Karger 등이 웹 캐시 분산을 위해 제안한 방법으로, 서버가 추가되거나 빠질 때 꼭 옮겨야 하는 만큼의 키만 옮기도록 설계되어 있습니다.

mod-N 샤딩의 문제

키 10,000개를 서버 4대에 나눠 둔 캐시를 생각해 봅시다. 서버를 1대 늘려 N이 4에서 5가 되면 h % 4와 h % 5가 같은 키만 제자리에 남습니다. 해시값이 고르게 퍼져 있다면 그런 키의 비율은 약 1/(N+1)이므로, 나머지 약 N/(N+1), 곧 80%의 키가 다른 서버로 옮겨 갑니다. 서버가 10대에서 11대가 되면 약 91%가 움직입니다.

import hashlib

def h(key: str) -> int:
    return int.from_bytes(hashlib.md5(key.encode()).digest()[:8], "big")

keys = [h(f"user:{i}") for i in range(100_000)]
for n in (4, 10):
    moved = sum(k % n != k % (n + 1) for k in keys)
    print(n, "->", n + 1, f"{moved / len(keys):.0%}")
# 4 -> 5 80%
# 10 -> 11 91%

캐시라면 이것은 곧 캐시 적중률이 순간적으로 거의 0이 되고 모든 요청이 원본 데이터베이스로 몰린다는 뜻입니다. 저장소라면 전체 데이터를 다시 옮겨야 합니다. 서버 한 대가 고장 나 빠질 때도 똑같은 일이 벌어집니다.

이상적인 목표

서버가 N대에서 N+1대가 될 때 새 서버가 공평한 몫을 가지려면 전체 키의 1/(N+1)은 반드시 새 서버로 가야 합니다. 일관된 해시의 목표는 바로 이만큼만, 그리고 새 서버로만 옮기는 것입니다. 기존 서버끼리 키를 주고받는 일은 없어야 합니다. 서버가 빠질 때도 그 서버가 갖고 있던 키만 나머지 서버로 흩어지면 됩니다.

해시 링

일관된 해시의 가장 널리 알려진 형태는 해시 링입니다. 해시 값의 범위(예: 0부터 2^64 - 1까지)를 원처럼 끝과 처음이 이어진 고리로 봅니다.

  • 서버마다 이름을 해시해 링 위의 한 점(토큰, 위치)에 놓습니다.
  • 키도 같은 해시 함수로 링 위의 한 점에 놓습니다.
  • 키에서 시계 방향으로 가다가 처음 만나는 서버가 그 키의 주인입니다.
  • 가장 큰 위치를 지나면 0으로 돌아가 다시 찾습니다(감싸기, wrap-around).

이렇게 하면 서버 하나가 추가될 때 새 서버의 위치와 그 앞(반시계 방향) 서버 사이 구간의 키만 새 서버로 넘어옵니다. 링의 나머지 부분은 전혀 영향을 받지 않습니다.

0 ---- A(10) ---- B(45) ---- C(80) ---- 99, 다시 0으로
키 7  -> 시계 방향 첫 노드 A
키 33 -> B
키 52 -> C
키 91 -> 끝을 지나 처음으로 감싸서 A

가상 노드

서버마다 점을 하나만 두면 점 사이 간격이 우연에 크게 좌우됩니다. 서버 10대에 점 하나씩이면 어떤 서버는 평균의 두세 배를 맡고 어떤 서버는 거의 놀게 됩니다. 그래서 서버 하나를 A#0, A#1, ... 처럼 여러 이름으로 해시해 링 위에 여러 점을 둡니다. 이 점들을 가상 노드(virtual node, vnode)라고 부릅니다. 점이 많을수록 각 서버가 맡는 구간의 합이 평균에 가까워지고, 서버가 빠질 때 그 부하가 한 이웃이 아니라 여러 서버로 고르게 흩어집니다. 성능이 좋은 서버에 가상 노드를 더 많이 주면 가중치도 표현할 수 있습니다.

node = "cache-a"
names = [f"{node}#{i}" for i in range(4)]
print(names)  # ['cache-a#0', 'cache-a#1', 'cache-a#2', 'cache-a#3']
# each name is hashed separately: 4 points on the ring for one server

다른 방식들

링만 있는 것은 아닙니다.

  • 랑데부 해시(rendezvous hashing, HRW): 키마다 모든 서버에 대해 hash(서버, 키) 점수를 매기고 가장 높은 서버를 고릅니다. 링이 필요 없고 구현이 매우 짧지만 조회마다 서버 수만큼 계산합니다.
  • 점프 일관된 해시(jump consistent hash): 2014년 Google의 Lamping과 Veach가 발표한 방법으로, 메모리 없이 키를 0부터 N-1 사이 버킷 번호로 보냅니다. 버킷은 끝에서만 늘리거나 줄일 수 있습니다.
  • 고정 슬롯 방식: Redis Cluster는 링을 쓰지 않고 16,384개의 해시 슬롯(CRC16(key) mod 16384)을 두고 슬롯을 노드에 명시적으로 배정합니다. 일관된 해시와 목표는 비슷하지만 다른 설계입니다.

좋은 분산 방법의 조건

Karger 등의 논문은 좋은 분산 방법이 갖춰야 할 성질을 다음과 같이 정리합니다.

  • 균형(balance): 키가 노드에 고르게 나뉩니다.
  • 단조성(monotonicity): 노드가 추가될 때 키는 기존 노드에서 새 노드로만 옮겨 가고, 기존 노드끼리는 주고받지 않습니다.
  • 퍼짐(spread)과 부하(load): 클라이언트마다 알고 있는 노드 목록이 조금씩 달라도 한 키가 너무 많은 노드에 흩어지지 않고, 한 노드에 너무 많은 키가 몰리지 않습니다.

용어 정리

용어뜻
노드(node)키를 맡는 물리 서버나 샤드
링(ring)해시 값 공간을 원형으로 본 것
토큰(token)노드가 링 위에 놓인 위치
가상 노드한 노드가 링 위에 갖는 여러 점
선호 목록복제본을 둘 노드들의 순서 목록(Dynamo 용어)
리밸런싱노드 변경 후 키를 새 주인에게 옮기는 작업

언제 쓰나

노드 수가 운영 중에 바뀌고, 키를 옮기는 비용(캐시 미스, 데이터 복사)이 큰 곳에서 씁니다. 분산 캐시 클라이언트, Dynamo 계열 키-값 저장소, 세션을 같은 서버로 보내야 하는 로드 밸런서가 대표적입니다. 반대로 노드 수가 고정이거나 키를 옮기는 비용이 거의 없다면 단순한 % N으로도 충분합니다.

정리

  • hash % N은 노드 수가 바뀌면 약 N/(N+1)의 키를 옮깁니다.
  • 일관된 해시는 꼭 필요한 약 1/(N+1)만 옮기는 것을 목표로 합니다.
  • 해시 링에서는 키에서 시계 방향으로 처음 만나는 노드가 주인입니다.
  • 가상 노드는 부하 편차를 줄이고 가중치를 표현합니다.
  • 랑데부 해시, 점프 해시, 고정 슬롯 방식도 같은 문제를 다르게 풉니다.
이 장에 대해 질문이 있나요?
다음 장동작 원리

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.