Search

Redis (Replication, Cluster, Sentinel)

주제
Database
날짜
2023/03/09

Redis란?

REmote DIctionary Server

개요

인메모리 기반 key-value 구조의 저장소이다.
가장 인기 있는 key-value 기반 저장소이다. (key-value 저장소 랭킹)
key-value 구조이기 때문에 쿼리를 사용할 필요가 없다.
Single Threaded이다.
한 번에 하나의 명령만 처리할 수 있다.
중간에 처리 시간이 긴 명령어가 들어오면 그 뒤에 명령어들은 모두 앞에 있는 명령어가 처리될 때까지 대기가 필요하다.
하지만 get, set 명령어의 경우 초당 10만 개 이상 처리할 수 있을 만큼 빠르다.
최적화된 C 코드로 작성되었다.
주요 Redis 사용 사례
캐싱
세션 관리
pub/sub
순위표

장점

빠른 성능

데이터를 디스크 또는 SSD에 저장하는 대부분의 데이터베이스와는 달리 모든 Redis 데이터는 서버의 주 메모리(RAM)에 저장된다.
Redis와 같은 인메모리 데이터베이스는 디스크에 액세스해야 할 필요를 없앰으로써 검색 시간으로 인한 지연을 방지하고 CPU 명령을 적게 사용하는 좀 더 간단한 알고리즘으로 데이터에 액세스할 수 있다.
일반적으로 작업을 실행하는 데 1밀초 미만이 소요된다.

인메모리 데이터 구조

다양한 데이터 유형에 매핑되는 키를 저장할 수 있다.
데이터 유형
String
기본 데이터 유형.
텍스트 또는 이진 데이터.
최대 크기 - 512MB
List of Strings, Sets of unordered Strings
문자열이 추가된 순서대로 유지된다.
List
Array 형식의 데이터 유형.
처음과 끝에 데이터 삽입, 삭제는 빠르지만 O(1), 중간에 데이터를 삽입, 삭제하는 것은 느리다 O(N).
Sorted Sets
점수에 따라 정렬된다.
Hashes
필드와 값 목록을 저장.
HyperLogLogs
데이터 세트에서 고유한 항목을 센다.

다양성과 사용 편의성

Redis는 개발과 운영을 좀 더 쉽고 빠르게 수행할 수 있는 여러 도구를 제공한다.
Pub/Sub
메시지를 채널에 게시하며, 채널에서 구독자에게 전달된다.
채팅과 메시징 시스템에 매우 적합하다.
TTL Key
특정 기간 후에 자동 삭제되는 Time To Live 값을 설정할 수 있다.
데이터베이스를 불필요한 데이터로 채우지 않도록 하는데 유용하다.
원자성 카운터
경합 상태가 일관성 없는 결과를 생성하지 않도록 한다.
Lua
강력하지만 간단한 스크립트 언어이다.

복제 및 지속성

Redis는 master-slave 아키텍처를 사용하며 비동기식 복제를 지원하여 데이터가 여러 slave 서버에 복제될 수 있다.
이렇게 할 경우 주 서버에 장애가 발생하는 경우 요청이 여러 서버로 분산될 수 있으므로 향상된 읽기 성능과 복구 기능을 모두 제공할 수 있다.
Redis는 안정성을 제공하기 위해 특정 시점 스냅샷과 데이터가 변경될 때 마다 이를 디스크에 저장하는 Append Only File(AOF) 생성을 모두 지원한다.
스냅샷 - Redis 데이터들을 디스크로 복사
스냅샷과 AOF을 통해 장애 발생 시 Redis 데이터를 신속하게 복원할 수 있다.

많은 개발 언어 지원

Redis 개발자는 100개가 넘는 오픈 소스 클라이언트를 사용할 수 있다.
Java, Python, PHP, C, C++, C#, JavaScript, Node.js, Go …

사용 사례

캐싱

다른 데이터베이스 “앞”에 배치된 Redis는 성능이 뛰어난 인메모리 캐시를 사용하여 엑세스 지연 시간을 줄이고, 처리량을 늘리며, 관계형 또는 NoSQL 데이터베이스의 부담을 줄인다.

세션 관리

Redis는 세션 관리 작업에 매우 적합하다.
세션키에 대한 적잘한 TTL과 빠른 key-value 저장소를 사용하면 간단하게 세션 정보를 관리할 수 있다.

실시간 순위표

Sorted Set 데이터 구조를 사용하면 요소가 목록에 유지되고 점수에 따라 정렬된다.
이를 통해 손쉽게 동적 순위표를 생성하여 게임 랭킹, 좋아요를 가장 많이 받은 메시지 등 다양한 사례에 적용할 수 있다.

속도 제한

Redis는 이벤트 속도를 측정하고 필요한 경우 제한할 수 있다.
클라이언트의 API 키에 연결된 Redis 카운터를 사용하여 특정 기간 동안 액세스 요청의 수를 세고 한도가 초과되는 경우 조치를 취할 수 있다.
속도 제한기는 포럼의 게시물 수를 제한하고, 리소스 사용량을 제한하며, 스팸을 억제하는데 주로 사용된다.

대기열

Redis List 데이터 구조를 사용하면 간단한 영구 대기열을 손쉽게 구현할 수 있다.
Redis List는 자동 작업 및 차단 기능을 제공하므로 신뢰할 수 있는 메시지 브로커 또는 순환 목록이 필요한 다양한 어플에 적합하다.

채팅 및 메시징

Redis에서는 패턴 매칭과 더불어 Pub/Sub 을 지원한다.
따라서 고성능 채팅방, 실시간 코멘트 스트림 및 서버 상호 통신을 지원할 수 있다.
Pub/Sub 을 사용하여 게시된 이벤트를 기반으로 작업을 트리거할 수 있다.

Redis Replication

복제란?

Redis의 데이터를 거의 실시간으로 다른 Redis 노드에 복사하는 작업이다.
따라서 서비스를 제공하던 첫 번째 Redis 노드가 다운되더라도, 데이터를 받은 두 번째 Redis 노드가 서비스를 계속 할 수 있다.
Redis에서는 첫 번째 노드를 master라고 하고 두 번째 노드를 replica(복제)라고 합니다.
복제 기능이 없을 경우
Redis 인스턴스가 사람의 실수 또는 소프트웨어적인 문제로 다운 되었을 때 AOF 기능을 사용하고 있었고 데이터가 많이 쌓여 있다면, 인스턴스가 시작하는데 몇 분이 걸릴 수도 있다.  
다운의 원인이 하드웨어적인 문제였다면 서비스를 다시 시작하는 상당한 시간이 소요될 수 있고, 데이터를 복구하지 못할 수도 있다.
master와 replica는 물리적으로 다른 머신에 두어야 한다.

특징

Redis는 비동기 복제를 한다.
Redis master는 복제 서버를 여러 개 둘 수 있다.
복제 서버는 또 복제 서버를 둘 수 있다.
master → 복제1 → 복제2 이런 구성이 된다.
master에 많은 데이터가 있는 상태에서 복제 서버를 시작하면, 대량의 master 데이터가 복제 서버로 보내진다. 이 때에도 master는 멈추지 않고 정상적으로 요청을 처리한다. 왜냐하면 데이터를 복제 서버로 보내는(RDB 파일을 생성하는) 작업은 자식 프로세스가 처리한다.
복제 서버에게 조회 요청을 처리하도록 하는 것도 부하를 분산하는 좋은 방법이다.
특히 sort 명령같은 것들은 복제 서버에서 수행하는 것이 좋다.
master의 부하를 줄이기 위해서, AOF 쓰기나 RDB 파일 생성을 복제 서버에서 수행하는 것도 좋은 방법이다.
하지만 이런 설정을 했을 경우에 master를 자동 시작 하도록 하면 데이터가 유실 될 수 있다.

복제 방식

전체 동기화(full synchronization)

Redis 2.8.18 부터는 RDB 파일을 디스크에 만들지 않고 복제하는 기능을 제공
복제 순서
1.
master는 자식 프로세스를 시작해 백그라운드로 RDB파일에 데이터를 저장.
2.
데이터를 저장하는 동안 master에 새로 들어온 명령들은 처리 후 복제버퍼에 저장.
3.
RDB 파일 저장이 완료되면, master는 파일을 복제 서버에게 전송.
4.
복제 서버는 파일을 받아 디스크에 저장하고, 메모리로 로드.
5.
master는 복제버퍼에 저장된 명령을 복제 서버에게 전송.
master가 다운 되면 복제 서버는 1초에 한 번씩 master에 Connect 요청을 보낸다.
master가 살아나면 복제 서버에 복제 순서에 따라 Sync를 한다.
복제 서버가 여러 개 일때도 RDB 파일은 하나만 생성한다.

부분 동기화(Partial resynchronization)

부분 동기화 기능은 Redis 버전 2.8 부터 제공
master와 복제 서버는 각 서버의 run idreplication offset을 가지고 있다.
master와 복제 서버간 네트워크가 끊어지면 master는 복제 서버에 전달할 데이터를 backlog-buffer에 저장한다.
다시 연결되었을 때 backlog-buffer가 넘치지 않았으면 run idoffset을 비교해서 그 이후 부터 동기화를 한다.
이것을 부분 동기화라고 한다.
Backlog-buffer 크기는 repl-backlog-size 파라미터로 설정한다.
네크워트 단절 시간이 길어져 master의 backlog-buffer가 넘치면 다시 연결되었을 때 전체 동기화를 한다.
master나 복제 서버 중 한쪽이 재시작 했을 경우에도 전체 동기화를 한다.

master: 디스크를 사용하지 않는 동기화 (Diskless Replication)

Redis 버전 2.8.18 부터 디스크를 사용하지 않는 동기화 기능을 제공
이 기능은 Redis를 캐시 용도로 사용할 경우 또는 master가 설치된 머신의 디스크 성능이 좋지 않을 경우 이용할 수 있다.
디스크를 사용하지 않는 것은 master만 적용된.
복제 서버는 받은 데이터를 RDB 파일에 저장한다.
master의 자식 프로세스가 RDB 데이터를 소켓을 통해서 복제 서버에게 직접 쓰는 방식이다.
redis.conf(Master) 파라미터를 통해 설정한다.
repl-diskless-sync no or yes, default no
디폴트는 no, yes로 하면 디스크를 사용하지 않고 동기화가 된다.
여러 복제 서버에서 요청이 들어올 경우, 기본적으로 첫 번째 복제 서버의 소켓에 데이터를 전송하고, 완료되면, 다음 복제을 처리한다.
몇 개 복제 서버를 한 번에 처리할 수 있도록 요청을 기다리는 옵션.
redis.conf(Master) 파라미터를 통해 설정한다.
첫 번째 요청이 온 후 5초 동안 다른 복제 서버의 요청을 기다렸다가, 요청이 오면 같이 처리한다.
즉, 5초 안에 3개 복제 서버에서 동기와 요청이 왔다면 이는 병렬로 처리할 수 있다.
즉시 처리하도록 하려면 0으로 설정.

복제(Replica): 디스크를 사용하지 않는 동기화 (repl-diskless-load)

이 기능은 버전 6.0부터 사용할 수 있다
복제 서버에서 디스크를 사용하지 않는 동기화이다.
즉, 복제 서버에 RDB 파일을 생성하지 않는다.
redis.conf(Replica) 파라미터를 통해 설정한다.
repl-diskless-load disabled/on-empty-db/swapdb, default disabled 세 가지.
disabled
diskless를 사용하지 않는다.
on-empty-db
복제 서버에 데이터(키)가 없을 경우에 적용, 데이터가 있으면 RDB 파일을 생성해서 복제한다.
swapdb
복제 서버에 데이터(키) 여부와 상관없이 diskless로 동작한다.
이 경우 만약의 사태에 대비해서 기존 데이터를 메모리(RAM)에 보존한다.
복제가 성공하면 RAM에 보존한 데이터는 지운다.
복제가 실패하면 RAM에 보존한 데이터로 복구한다.
이 경우 기존 데이터 + 새 데이터 만큼 메모리(RAM)이 필요하므로 충분한 메모리가 있어야 한다.

복제 서버는 읽기 전용

Redis 버전 2.6 부터 복제 서버는 디폴트로 읽기 전용이다
redis.conf 파라미터를 통해 설정한다.
replica-read-only yes or no, default yes
복제 서버에 데이터를 입력 했어도 master와 Resync 되면 복제 서버에 입력된 데이터는 사라진다.

Redis Cluster

주요 기능

자동 장애 조치(Automatic Failover)
샤딩 (sharding)
데이터를 분산 저장

동작 방식

master 1,2,3 이 있다면 데이터는 3개중에 하나에 저장되며, client 가 데이터 읽기 요청시 저장된 곳이 아닌 다른 master에 요청 했다면 저장된 master 정보를 알려주며, 클라이언트는 전달받은 master 정보에 다시 요청해서 데이터를 받아와야 한다.
해당 부분은 Redis Cluster 를 지원하는 라이브러리에서 다 해준다.
slave 가 죽어서 복제 노드가 없는 master가 생길시 다른 master 노드에 여유분이 있다면 해당 노드로 빈자리를 채울 수 있다.
사용자가 개입하지 않고 Redis Cluster가 알아서 다 해준다.
해시 슬롯을 이용해 데이터를 샤딩한다.
해시 슬롯
CRC-16 해시 함수를 이용해 key를 정수로 변환하고 해당 정수값을 16,385로 모듈 연산한 값.
해시 함수는 CRC16 function을 사용한다.
cluster는 총 16384개의 해시 슬롯이 있으며 각 master 노드에 자유롭게 할당 가능.
Redis 노드가 3개 일 경우, 1번 노드는 0-5460, 2번 노드는 5461-10922, 3번 노드는 10923-16383 슬롯을 가지게 된다.
슬롯을 노드에 할당하는 것은 Redis Cluster가 한다.
master가 죽을 경우 master의 slave는 gossip Protocol을 통해 master의 죽음을 파악하고, slave중 하나가 master로 failover된다.
→ 중단없는 서비스 제공.
failover가 발생한다면, slave가 master로 승격할 때까지, 문제가 발생한 master로 할당된 슬롯의 키는 사용할 수 없다.
기존 master가 다시 살아나면 새로운 master의 slave가 된다.
gossip Protocol
각 Redis는 다른 Redis들과 직접 연결하여 상태 정보를 통신.

특징

Redis Cluster에서는 별도의 Sentinel이 필요하지 않다.
Sentinel보다 더 발전된 형태이다.
최소 3개의 master 노드가 있어야 구성 가능하다.
Sentinel이 노드를 감시했지만, Cluster에서는 모든 노드가 서로 감시한다.
Multi-master, Multi-slave 구조이다.
1000대의 노드까지 확장 가능하다.
모든 데이터는 master 단위로 샤딩되고 slave 단위로 복제된다.
master 마다 최소 하나의 slave를 두는 것을 추천한다.
slave가 하나도 없을 때 master 노드가 작동이 안되면 해당 데이터 유실이 발생한다.
노드를 추가/삭제할 때 운영 중단 없이 Hash slot을 재구성할 수 있다.
키 이동 시에 해당 키에 잠시 락이 걸릴 수 있다.
과반수 이상의 노드가 다운되면 cluster가 깨진다.
Replication은 Async방식으로 이루어지기 때문에 Data 정합성이 깨질 수 있는데 이때 나중에 master가 된 Data를 기준으로 정합성을 맞춘다.
Redis Cluster는 2개의 포트가 필요하다.
클라이언트를 위한 포트, 노드 간 통신 버스 포트
gossip Protocol은 Redis Client가 이용하는 Port번호보다 10000높은 port 번호를 이용한다.

Redis Cluster 제한 사항

Redis 버전 3.0 이상에서 클러스터를 사용할 수 있다.
기본적으로 멀티 키 명령(operation)을 수행할 수 없다.
예를 들어, MSET key1 value1 key2 value2, SUNION key1 key2, SORT 이런 명령은 클러스터에서 사용할 수 없다.
하지만 hash tag를 사용하면 사용할 수 있다.
Hash tag는 키의 일부를 {}로 감싸는 것이다.
예를 들어, {user001}.following과 {user001}.followers는 같은 슬롯에 저장된다.
클러스터 모드에서는 DB 0번만 사용할 수 있다.
멀티 키 명령에 대해서
Enterprise 게이트 서버를 사용하면 멀티 키 명령을 사용할 수 있다.

Redis Sentinel

운영중 예기치 않게 master가 다운되었다면, 관리자가 이를 감지해서 slave를 master로 올리고 클라이언트들이 새로운 master에 접속할 수 있도록 해 주어야 한다.
Sentinal은 master와 slave를 감시하고 있다가 master가 다운되면 이를 감지해서 관리자의 개입없이 자동으로 slave를 master로 올려준다.

주요 기능

모니터링 Monitoring
Sentinal은 Redis master, slave들을 제대로 동작하는지 지속적으로 감시한다.
자동 장애조치 Automatic Failover
Sentinal은 master가 예기치 않게 다운되었을 때 slave를 새로운 master로 승격시켜 준다.
그리고 slave가 여러 대 있을 경우 이 slave들이 새로운 master로 부터 데이터를 받을 수 있도록 재 구성하고, 다운된 master가 재 시작했을 때 slave로 전환되어 새로운 master를 바라볼 수 있도록 한다.
알림 Notification
Sentinal은 감시하고 있는 Redis 인스턴스들이 failover 되었을 때 Pub/Sub으로 Application(client)에게 알리거나 shell script로 관리자에게 이메일이나 SMS로 알릴 수 있다.

동작 방식

Sentinal 인스턴스 과반 수 이상이 master 장애를 감지하면 slave 중 하나를 master로 승격시키고 기존의 master는 slave로 강등시킨다.
slave가 여러개 있을 경우 slave가 새로운 master로부터 데이터를 받을 수 있도록 재구성된다.

failover 감지 방법

SDOWN : Subjectively down (주관적 다운)
Sentinal에서 주기적으로 master에게 보내는 PING과 INFO 명령의 응답이 3초(down-after-milliseconds 에서 설정한 값) 동안 오지 않으면 주관적 다운으로 인지한다.
Sentinal 한 대에서 판단한 것으로, 주관적 다운만으로는 장애조치를 진행하지 않는다.
ODOWN : Objectively down (객관적 다운)
설정한 quorum 이상의 Sentinal에서 해당 master가 다운되었다고 인지하면 객관적 다운으로 인정하고 장애 조치를 진행한다.
Quorum
Redis 장애 발생시 몇 개의 Sentinel이 특정 Redis의 장애 발생을 감지해야 장애라고 판별하는지를 결정하는 기준 값.
보통 Redis의 과반 수 이상으로 설정한다.

failover시 주의 사항

get-master-addr-by-name
master, slave 모두 다운되었을 때 Sentinel에 접속해 master 서버 정보를 요청하면 다운된 서버 정보를 리턴한다.
따라서 INFO Sentinel 명령으로 마스터의 status를 확인해야 한다.
slave -> master 승격 안되는 경우
slave다운 → master다운 → 다운된 slave 재시작되면 이 서버는 master로 전환되지 않는다.
slave의 redis.conf에 자신이 복제로 되어 있고, Sentinel도 복제라고 인식하고 있기 때문이다.
해결책은 slave가 시작하기 전에 redis.conf에서 slaveof를 삭제하는 것이다.
Failover Timeout 만큼 write 연산 실패
데이터량에 따른 최적의 Failover Timeout값을 찾고 sentinel.conf에 적용해야 한다.

특징

Sentinel은 1차 복제만 master 후보에 오를 수 있다.
복제 서버의 복제 서버는 불가능
1차 복제 서버 중 replica-priority 값이 가장 작은 서버가 master에 선정된다.
0으로 설정하면 master로 승격 불가능하고 동일한 값이 있을 땐 엔진에서 선택한다.
안정적 운영을 위해 3개 이상의 Sentinal을 만드는 것을 권장하는데, 서로 물리적으로 영향받지 않는 컴퓨터나 가상 머신에 설치되는 것이 좋다.
Sentienl은 내부적으로 Redis 의 Pub/Sub 기능을 사용해서 서로 정보를 주고 받는다.
Sentienl + Redis 구조의 분산 시스템은 Redis가 비동기 복제를 사용하기 때문에 장애가 발생하는 동안 썼던 내용들이 유지됨을 보장할 수 없다.
Sentinel을 Redis와 동일한 Node에 구성해도 되고, 별도로 구성해도 된다
클라이언트는 주어진 서비스를 담당하는 현재 Redis master의 주소를 요청하기 위해 Sentinel에 연결한다.
장애 조치가 발생하면 Sentinel은 새 주소를 알려준다.

정리

Cluster를 사용하자
Sentinel은 One Master 구조이기 때문에 데이터 사이즈가 커지면 Scale Up을 해야하는데, Cluster는 Scale Out이 가능하다.
추후 확장성을 위해 Cluster를 사용하는 것이 좋다.