Redis Cluster

How Redis-Cluster works?

集群


多个Redis节点协同工作,Redis节点之间两两通信
客户端可连接至任意Redis节点

键分布模型


Redis集群共持有16384(2^14)个槽位,每个槽位仅可存在于一个Redis主节点上
单个Redis主节点可含有任意数量的槽位(0-16384)个
所有的槽位(0-16383)必须被分配
每个Redis从节点持有其主节点一样的槽位
集群通过哈希一致性算法计算键所属槽位 CRC16("key") mod 16384

客户端请求示例

客户端不存储槽位信息

1
2
3
4
Client => A: GET foo
A => Client: -MOVED 9 127.0.0.1:9003
Client => C: GET foo
C => Client: "bar"

客户端存储槽位信息

1
2
3
4
Client => A: CLUSTER HINTS
A => Client: ... a map of hash slots -> nodes
Client => C: GET foo
C => Client: "bar"

高可用


Redis节点间通过Ping-Pong的方式检测状态
当半数以上节点认定某从节点失效时,仅标记失效状态,不做后续处理,当从节点重新上线,移除失效标记
当半数以上节点认定某主节点失效时,在其所拥有的从节点中发起选举,当选者成为新的主节点
当集群中超过半数的Redis主节点可用,且失效的主节点有可用的从节点,整个Redis集群就是可用的

主从复制

异步复制

  1. 客户端向主节点发送一条写命令
  2. 主节点执行写命令,并向客户端返回命令回复
  3. 主节点将刚刚执行的写命令复制给它的从节点

最终一致性

Redis 集群不保证数据的强一致性

  1. 主节点写,从节点读 ——读到的数据是旧数据
  2. 网络分区时间足够长——从节点替换主节点,原主节点写操作丢失

局限性

Redis集群在多个Redis节点之间进行数据共享
Redis集群不支持“multi-key”操作,即执行的命令需要在多个Redis节点之间移动数据
比如Set类型的并集、事务操作、M命令等,除非这些key属于同一个slot,即Cluster不能对多个slots操作

Key hash tags

Redis提供了hash tags操作,允许为key自定义一个tag,在存储时,Redis会依据key的tag计算出slot

1
2
SET {hello}world value
SET {hello}kugou value

1
2
3
4
5
6
7
8
9
10
11
// Radix.v2
// Slot returns the cluster slot the given key will fall into, taking into
// account curly braces within the keys as per the spec.
func Slot(key string) uint16 {
if start := strings.Index(key, "{"); start >= 0 {
if end := strings.Index(key[start+1:], "}"); end > 0 {
key = key[start+1 : start+1+end]
}
}
return CRC16([]byte(key)) % NumSlots
}

扩容


启动一个新的Redis节点D并与集群节点建立连接
迁移槽位7至新的Redis节点D
在7号槽位迁移过程中,当C收到操作7的命令时,会产生-ASK 转向
在7号槽位迁移完成后,当C收到操作7的命令时,会产生-MOVED 转向