본문 바로가기
개발/대용량트래픽

Redis Cluster: 고성능과 고가용성을 위한 데이터 분산 전략

by 난중후니 2024. 1. 25.
728x90
반응형

Redis Cluster가 제공하는 것

  • 여러 노드에 자동적인 데이터 분산
  • 일부 노드의 실패나 통신 단절에도 계속 작동하는 가용성
  • 고성능을 보장하면서 선형 확장성을 제공

Redis Cluster 특징

1. Partitioning(파티셔닝)

  • 16384개의 해시 슬롯을 사용하여 데이터를 파티셔닝합니다.
  • 키의 해시 값에 따라 특정 슬롯에 할당됩니다.(CRC16 해시 함수를 사용)
  • 각 Redis 노드는 16384개의 해시 슬롯 중 일부를 책임집니다.
  • 클라이언트는 키의 해시 슬롯을 계산하여 해당 데이터를 저장하거나 검색할 Redis 노드를 알 수 있습니다.

1) CRC16 해시 함수
CRC16은 키를 16비트의 정수 값으로 변환하는데 0 ~ 65535 까지의 범위를 가질 수 있습니다.

2) 해시 슬롯
Redis Cluster는 16384개의 해시 슬롯을 사용합니다.
따라서, 각 키의 CRC16 해시 결과는 16384로 나누어 해당 키가 어떤 해시 슬롯에 속하는지를 결정합니다.
해시 슬롯번호 = CRC16(키) %(mod) 16384
ex) CRC 해시 함수의 결과가 20000인 키는 20000 %(mod) 16384 = 3616으로 해시 슬롯 3616에 할당됩니다.

3) 키의 할당
각 Redis 노드는 16384개의 해시 슬롯 중 일부를 책임지게 됩니다.
따라서, 특정 키가 어떤 해시 슬롯에 할당되는지를 알면, 그 키를 저장하거나 조회해야 할 Redis 노드를 알 수 있습니다.

4) 키의 재할당
노드가 추가되거나 제거될 때, 해시 슬롯의 할당이 재조정될 수 있습니다.
이는 노드 간의 데이터 이동을 초래하지만, Redis Cluster는 이러한 재조정 과정을 자동으로 관리합니다.

5) 태그를 이용한 해싱
Redis Cluster에서는 여러 키가 동일한 해시 슬롯에 할당되도록 키 이름에 태그를 사용할 수 있습니다.
태그는 키 이름에서 중괄호 '{}'로 둘러싸인 부분입니다.
ex) user:{123}:nameuser:{123}:age는 동일한 해시 슬롯에 할당됩니다.

2. 고가용성

  • 각 마스터 노드는 하나 이상의 슬레이브 노드를 가질 수 있습니다.
  • 마스터 노드에 장애가 발생하면, 해당 마스터의 슬레이브 중 하나가 새로운 마스터로 승격됩니다.
  • 이를 통해 네트워크 파티션, 장애, 재구성 시에도 서비스 중단 없이 동작을 계속할 수 있습니다.

1) Redis Cluster의 고가용성 동작

  • 마스터와 슬레이브 노드
    Redis Cluster에서 각 마스터 노드는 여러 슬레이브 노드를 가질 수 있습니다.
    슬레이브 노드는 마스터 노드의 데이터 복제본을 유지하며, 마스터 노드에 장애가 발생할 경우 대체 노드로서의 역할을 합니다.
  • 노드 감지
    Redis Cluster 노드들은 서로를 주기적으로 확인(Ping 방식, Gossip 프로토콜)하며, 노드의 상태 정보를 교환합니다.
    만약 마스터 노드가 응답하지 않는다면, 그 노드는 실패로 간주됩니다.
  • 슬레이브 승격
    마스터 노드가 실패하면, 그 마스터의 슬레이브 중 하나가 새로운 마스터로 승격됩니다.
    승격은 여러 요인을 기반으로 결정되는데, 데이터 복제의 지연 시간, 노드의 ID, 연결 상태 등이 고려됩니다.

2) 슬레이브의 마스터 승격 과정

  • 장애 인식
    다른 노드들은 주기적인 Ping 메시지를 통해 노드의 상태를 감지합니다.
    만약, 마스터 노드가 일정 시간 동안 응답하지 않으면, 그 노드는 실패로 판단됩니다.
  • 승격 대기
    슬레이브 노드들은 일정시간(기본 15초) 동안 기다린 후 자신을 마스터로 승격시키기 시작합니다.
    기다리는 시간은 node-timeout 값에 의해 조절됩니다.
  • 승격 결정
    가장 최근에 마스터 노드로부터 데이터를 복제받은 노드, 해당 조건이 동일한 경우 노드 ID가 낮은 노드가 마스터 노드로 승격됩니다.

3) 클라이언트의 동작

  • 서버 연결
    클라이언트는 일반적으로 모든 Redis Cluster 노드에 대한 정보를 갖고 있지 않을 수 있습니다.
  • MOVED 오류 처리
    클라이언트가 연결된 노드에 데이터를 요청하면, 그 노드가 요청된 데이터를 가지고 있지 않은 경우 "MOVED" 오류와 함께 올바른 노드의 주소를 반환합니다.
    클라이언트는 이 주소로 재 연결하여 요청을 다시 시도합니다.
  • 노드 정보 업데이트
    클라이언트는 Redis Cluster 내의 노드 상태와 위치 정보를 주기적으로 업데이트하여 최신 상태를 유지합니다.

3. 데이터 복구와 장애 복구

  • 마스터 노드가 실패하면 슬레이브 노드 중 하나가 자동으로 마스터로 승격됩니다.
  • Redis Cluster의 장애 복구 기능은 네트워크 분할을 포함한 다양한 시나리오에 대응할 수 있습니다.

4. 클라이언트와의 통신

  • 클라이언트는 모든 Redis 노드에 연결하지 않아도 됩니다.
  • 클라이언트가 연결된 노드가 요청한 키를 갖고 있지 않은 경우, 클라이언트는 "MOVED" 오류 메시지를 받게 됩니다.
  • 이 메시지는 올바른 노드의 주소와 함께 제공되므로, 클라이언트는 해당 주소로 재연결하여 요청을 수행 할 수 있습니다.
MOVED 오류 처리 방식
  1. MOVED 메시지 읽기
    클라이언트는 'MOVED' 메시지를 읽어 특정 키에 대한 올바른 노드의 주소를 파악합니다.
    'MOVED' 메시지는 보통 다음과 같은 형태입니다.
    MOVED hash_slot ip:port
  1. 내부 매핑 업데이트
    클라이언트는 자체적으로 클러스터 노드와 해시 슬롯에 대한 매핑을 유지합니다.
    'MOVED' 메시지를 받으면, 클라이언트는 이 매핑 정보를 업데이트해 올바른 노드에 키를 매핑합니다.
  1. 재요청
    클라이언트는 'MOVED' 메시지에서 얻은 새로운 주소로 요청을 다시 전송합니다.

* 예시) Springboot에서 Lettuce를 이용한 방법

  1. 의존성 추가
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}
  1. application.yml 추가
# application.yml
spring:
  redis:
    cluster:
      nodes:
      - node1host:port
      - node2host:port
  1. Service 생성
package com.example.rediscluster;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class RedisService {
    private final StringRedisTemplate stringRedisTemplate;

    @Autowired
    public RedisService(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }
}
  1. Controller 생성
package com.example.rediscluster;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisController {
    private final RedisService redisService;

    @Autowired
    public RedisController(RedisService redisService) {
        this.redisService = redisService;
    }

    @GetMapping("/set")
    public String set(@RequestParam String key, @RequestParam String value) {
        redisService.set(key, value);
        return "Value set successfully!";
    }

    @GetMapping("/get")
    public String get(@RequestParam String key) {
        return redisService.get(key);
    }
}

5. 안정성 및 한계점

  • Redis Cluster는 마스터 노드가 과반 수 이상 문제가 생긴다면 어떤 노드도 쓰기 작업을 수락하지 않습니다.
  • 일부 Redis 기능들, 예를 들면 multi-key operations는 클러스터 모드에서 제한될 수 있습니다. Redis Cluster는 해시슬롯 기반으로 노드에 분할되어 있기 때문입니다. 따라서, 모든 키가 같은 노드에 있는 경우 multi-key가 동작합니다.

6. 클러스터 재구성

  • Redis Cluster는 노드 장애를 감지하고 자동으로 클러스터를 재구성하는 메커니즘을 가지고 있습니다.
728x90
반응형

댓글