어떤 노드가 실패되어 통신할 수 없을 때 연산을 지속할 수 있도록 어느 정도의 가용성을 제공한다.
그러나 마스터 노드가 사용 불가능한 경우와 같이 큰 실패에서는 클러스터가 연산을 멈춘다.
그래서 Redis Cluster를 통해 얻는 게 뭔데?
The ability to automatically split your dataset among multiple nodes.
The ability to continue operations when a subset of the nodes are experiencing failures or are unable to communicate with the rest of the cluster.
Redis Cluster TCP ports
모든 레디스 클러스터는 2개의 TCP 통신이 열려있어야 한다.
보통은 tcp 포트가 clients를 serve하는 데 쓰이지만(ex. 6379), 여기에 10000을 더한 16379 같은 포트는 cluster bus로 쓰인다.
cluster bus : 바이너리 프로토콜을 통한 node 간 통신 채널로, 실패 감지, 설정 어베이트, 자동 failover에 쓰인다.
클라이언트는 클러스터 버스 포트랑은 통신하면 안되고, 항상 normal redis command port와는 통신해야 한다. 그러나 방화벽에서는 두 포트가 모두 열려있도록 보장해야 한다. 그러지 않으면 레디스 클러스터 노드가 통신할 수 없을것이니!
레디스 클러스터에 필요한 노드들
6379같은 클라이언트 통신 포트 :
클라이언트와 통신하는 데에 쓰이고 모든 클라이언트한테 열려 있어 클라이언트가 클러스터에 도달할 수 있게 해야 한다.
그리고 모든 클러스터 노드한테도 열려 있어야 한다. 나중에 key migration 때 클라이언트 포트를 쓰거든!
클라이언트 포트에 10000을 더한 클러스터 버스 포트 :
얘는 클러스터 버스 포트라서, 모든 클러스터 노드로부터 도달 가능해야 한다.
Redis Cluster Data Sharding
Redis Cluster는 consistent hashing을 사용하지 않는다. 대신 다른 형태의 샤딩을 쓰는데, 모든 키가 hash slot이란은 데의 일부로 저장되는, 그런 샤딩을 쓴다.
16384개의 해시 슬롯이 있고, 주어진 키가 어느 해시 슬롯으로 들어갈지 계산하기 위해 CRC16(key) 의 결과를 16384로 나눈 나머지를 취한다.
Node A contains hash slots from 0 to 5500.
Node B contains hash slots from 5501 to 11000.
Node C contains hash slots from 11001 to 16383.
위처럼 모든 노드는 해시 슬럿의 일부 집합을 맡고 있다. 이를 통해 클러스터에서 노드를 쉽게 추가하거나 삭제할 수 있다.
만약에 우리가 D라는 새 노드를 추가한다고 할 때, 우리는 몇몇 해시 슬롯을 A,B,C노드에서 D노드로 옮겨야 한다. 그리고 비슷하게 A 노드를 클러스터에서 제거한다면 우리는 A에 의해 제공되던 해시 슬롯을 B와 C로 옮길 수 있다. 그러면 A노드가 비어지면서 우리는 그걸 완전히 클러스터에서 없앨 수 있다.
노드에서 노드로 해시 슬롯을 옮기는 것은 연산을 멈추지 않기 때문에 노드를 추가하거나 삭제하거나 해시 슬롯 분포 퍼센티지를 바꾸는 것은 downtime이 생기지는 않는다.
해시태그?
해시태그를 사용해서 유저는 다수의 키를 강제로 같은 해시 슬롯으로 넣을 수 있다. 기본적으로 레디스 클러스터에서는 멀티 키 명령어를 쓸 수 없다. MSET 같은 거. 근데 해시 태그를 사용하면 할 수 있다. 이건 키의 일부를 {}로 감싸는 건데, 예를 들어 {user001}.following이랑 {user001}.followers가 같은 슬롯에 저장되는 것이다. 그리고 이 여러 키가 하나의 명령어로 처리될 수 있다.
Redis Cluster master-slave model
Redis Cluster는 마스터노드의 subset이 망가졌을 때 가용성을 유지하기 위해서 master-slave model을 사용하고, 모든 해시 슬롯이 master에 있는 자기 자신 하나와 N개의 추가 복제본 (slave)를 가진다.
A,B,C 라는 클러스터 노드가 있고, B가 죽었다고 치면 우리는 5501-11000까지의 해시 슬롯을 serve할 방법이 없다.
그러나 클러스터가 생성되거나 우리가 slave 노드를 모든 마스터에 추가해준다면, 최종 클러스터는 A,B,C,A1,B1,C1으로 구성될 것이다. 이러면 B노드가 죽더라도 시스템은 지속될 수 있다. 즉, B가 죽을때 클러스터는 B1을 마스터로 승격시켜서 연산을 지속한다. 하지만 만약 B랑 B1이 동시에 죽어버리면 레디스 클러스터는 더 이상 수행할 수 없게 된다.
Redis Cluster guarantees
strong consistency를 지원하진 않는다. 다시 말해 아래 조건에서는 레디스 클러스터가 write을 잃을 수 있다. 비동기 복제본을 쓰기 때문이다. write하는 도중에 아래 상황이 발생한다고 해보자.
클라이언트가 마스터 B한테 씀
마스터 B는 OK를 클라이언트에게 응답함
마스터 B는 슬레이브인 B1, B2에 이 write을 전파한다.
즉, 마스터 B는 슬레이브가 클라이언트한테 응답하는걸 기다려 주지 않는다. 그래서 현재의 마스터인 B는 write을 알아차렸고, 그의 slave는 알아차리지 못한 상태에서 B가 죽는다면, write된 내용을 모르는 슬레이브가 마스터로 승격되는 것이다. 그러면 이 write을 영원히 잃겠지 ㅠ
이건 기존에 분산 시스템을 지원하지 않는 데이터베이스를 사용할 때에 이미 겪어봤을 텐데, 그래서 결국 지속성을 향상시키기 위해 데이터베이스에서 클라이언트에 응답하기 전에 먼저 disk에 데이터를 flush하기도 했었다. 그런데 이러면 굉장히 성능이 떨어진다. 이건 레디스 클러스터에서의 동기 복제와 맞먹는다.
Redis Cluster 101
그래서 Redis Cluster를 통해 얻는 게 뭔데?
Redis Cluster TCP ports
Redis Cluster Data Sharding
Redis Cluster는 consistent hashing을 사용하지 않는다. 대신 다른 형태의 샤딩을 쓰는데, 모든 키가 hash slot이란은 데의 일부로 저장되는, 그런 샤딩을 쓴다.
16384개의 해시 슬롯이 있고, 주어진 키가 어느 해시 슬롯으로 들어갈지 계산하기 위해 CRC16(key) 의 결과를 16384로 나눈 나머지를 취한다.
Node A contains hash slots from 0 to 5500. Node B contains hash slots from 5501 to 11000. Node C contains hash slots from 11001 to 16383.
위처럼 모든 노드는 해시 슬럿의 일부 집합을 맡고 있다. 이를 통해 클러스터에서 노드를 쉽게 추가하거나 삭제할 수 있다. 만약에 우리가 D라는 새 노드를 추가한다고 할 때, 우리는 몇몇 해시 슬롯을 A,B,C노드에서 D노드로 옮겨야 한다. 그리고 비슷하게 A 노드를 클러스터에서 제거한다면 우리는 A에 의해 제공되던 해시 슬롯을 B와 C로 옮길 수 있다. 그러면 A노드가 비어지면서 우리는 그걸 완전히 클러스터에서 없앨 수 있다.
노드에서 노드로 해시 슬롯을 옮기는 것은 연산을 멈추지 않기 때문에 노드를 추가하거나 삭제하거나 해시 슬롯 분포 퍼센티지를 바꾸는 것은 downtime이 생기지는 않는다.
해시태그?
해시태그를 사용해서 유저는 다수의 키를 강제로 같은 해시 슬롯으로 넣을 수 있다. 기본적으로 레디스 클러스터에서는 멀티 키 명령어를 쓸 수 없다. MSET 같은 거. 근데 해시 태그를 사용하면 할 수 있다. 이건 키의 일부를 {}로 감싸는 건데, 예를 들어 {user001}.following이랑 {user001}.followers가 같은 슬롯에 저장되는 것이다. 그리고 이 여러 키가 하나의 명령어로 처리될 수 있다.
Redis Cluster master-slave model
Redis Cluster는 마스터노드의 subset이 망가졌을 때 가용성을 유지하기 위해서 master-slave model을 사용하고, 모든 해시 슬롯이 master에 있는 자기 자신 하나와 N개의 추가 복제본 (slave)를 가진다.
A,B,C 라는 클러스터 노드가 있고, B가 죽었다고 치면 우리는 5501-11000까지의 해시 슬롯을 serve할 방법이 없다. 그러나 클러스터가 생성되거나 우리가 slave 노드를 모든 마스터에 추가해준다면, 최종 클러스터는 A,B,C,A1,B1,C1으로 구성될 것이다. 이러면 B노드가 죽더라도 시스템은 지속될 수 있다. 즉, B가 죽을때 클러스터는 B1을 마스터로 승격시켜서 연산을 지속한다. 하지만 만약 B랑 B1이 동시에 죽어버리면 레디스 클러스터는 더 이상 수행할 수 없게 된다.
Redis Cluster guarantees
strong consistency를 지원하진 않는다. 다시 말해 아래 조건에서는 레디스 클러스터가 write을 잃을 수 있다. 비동기 복제본을 쓰기 때문이다. write하는 도중에 아래 상황이 발생한다고 해보자.
이건 기존에 분산 시스템을 지원하지 않는 데이터베이스를 사용할 때에 이미 겪어봤을 텐데, 그래서 결국 지속성을 향상시키기 위해 데이터베이스에서 클라이언트에 응답하기 전에 먼저 disk에 데이터를 flush하기도 했었다. 그런데 이러면 굉장히 성능이 떨어진다. 이건 레디스 클러스터에서의 동기 복제와 맞먹는다.
퍼포먼스와 consistency사이에는 균형이 생긴다.