Java 아키텍처·Spring 용어 사전
캐시해시 슬롯

Redis Cluster

키를 16384개 슬롯으로 나눠 노드들이 나눠 갖는다. 슬롯 단위라 노드 증감이 쉽다.

한 대의 메모리로 부족할 때 키를 여러 노드에 나눠 담는 방식.

왜 슬롯을 한 겹 두나

노드에 바로 나누면 hash(key) % 노드수 가 되는데, 노드가 하나 늘면 나누는 수가 바뀌어 거의 모든 키의 자리가 바뀐다. 3대에서 4대로 늘리는 순간 전체 재배치가 일어나고, 그것은 곧 전체 캐시 미스다. 확장하려다 장애를 부르는 셈이다.

그래서 사이에 고정된 칸을 한 겹 둔다.

키 → CRC16(key) mod 16384 → 슬롯 번호 → 그 슬롯을 가진 노드

슬롯 수 16384 는 영원히 고정이다. 노드가 늘고 줄어도 이 계산은 그대로고, 옮기는 것은 슬롯의 소유권뿐이다. 노드 3대면 대략 5461개씩 갖고, 4대가 되면 각자 조금씩 떼어 준다. 움직이는 데이터가 옮긴 슬롯만큼으로 한정된다.

키 → 슬롯은 고정하고 슬롯 → 노드만 바꾼다는 이 발상은 일관 해싱과 같은 문제의식이다. 간접 계층을 하나 두어 변화의 파급을 가둔다 — 분산 시스템에서 되풀이되는 수법이다.

클라이언트가 알아서 찾아간다

노드는 자기 슬롯이 아닌 키를 받으면 오류 대신 위치를 알려 준다.

MOVED 3999 10.0.0.2:6379    슬롯이 저 노드로 확정 이전됐다 → 지도를 갱신한다
ASK   3999 10.0.0.2:6379    이전 중이다 → 이번 요청만 저기로 보낸다

둘의 구분이 핵심이다. MOVED 는 영구적이라 클라이언트가 슬롯↔노드 지도를 고쳐 다음부터 바로 옳은 노드로 간다. ASK마이그레이션 진행 중이라는 뜻이라 지도를 고치면 안 된다 — 아직 안 옮겨진 키는 원래 노드에 있기 때문이다.

이 방식 덕분에 가운데 프록시가 없다. 프록시를 두면 단일 병목이자 단일 장애점이 되는데, 그 역할을 클라이언트에게 넘긴 설계다.

여러 키를 한 번에 못 다룬다

MGET a b c 처럼 여러 키를 묶는 명령은 키들이 같은 슬롯에 있어야 한다. 서로 다른 노드에 흩어져 있으면 한 노드가 답할 수 없기 때문이다.

user:1:profile   → 슬롯 A
user:1:orders    → 슬롯 B      ← 흩어진다

{user:1}:profile → 중괄호 안만 해싱
{user:1}:orders  → 같은 슬롯     ← 해시 태그

중괄호로 감싼 부분만 해시에 쓰는 것을 해시 태그라 한다. 함께 다뤄야 할 키를 같은 노드에 모으는 장치다. 단, 남용하면 한 노드에 데이터가 쏠려 나눈 의미가 사라진다. Lua 스크립트와 트랜잭션도 같은 제약을 받는다.

가용성

각 마스터에 복제본을 붙인다. 마스터가 죽으면 나머지 마스터들이 투표해 복제본을 승격시킨다. 그래서 마스터가 과반이어야 판단이 가능하고, 노드 수를 홀수로 잡는 이유가 이것이다.

승격 시 주의할 점은 복제가 비동기라는 것이다. 마스터가 응답한 뒤 복제본에 전달되기 전에 죽으면 그 쓰기는 사라진다. 캐시에는 대체로 괜찮지만, 분산 락처럼 정확성이 걸린 용도라면 이 창을 계산에 넣어야 한다.

면접 함정

  • "슬롯이 16384 인 건 성능 때문" → 노드끼리 주고받는 슬롯 비트맵 크기(16384비트 = 2KB)와 현실적인 노드 수를 감안한 설계값이다.
  • "클러스터면 트랜잭션이 그대로 된다" → 여러 슬롯에 걸친 명령은 쓸 수 없다.
  • "복제본이 있으니 읽기 부하를 나눠 준다" → 기본적으로 복제본은 읽기를 받지 않는다. 명시적으로 허용해야 하고 낡은 값을 볼 수 있다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 캐시·Redis — 어사이드·쓰기 전략