集群

6379:对外服务 16379:节点间通信

数据分布方案:

Redis集群使用了虚拟槽分区:

slot 0-3276 --> node1
slot 3277-6553 --> node2
slot 6554-9830 --> node3
slot 9831-13107 --> node4
slot 13108-16383 --> node5
stateDiagram-v2
  state RedisCluster {
    direction LR
    slot0 --> node1
    node1 --> data1
    slot3277 --> node2
    node2 --> data2
  }
  keys --> CRC16(key)&16383
  CRC16(key)&16383 --> RedisCluster

使用这种方案带来的特点:

由于数据分布于不同的节点, 所以集群功能相比单机有如下限制:

集群方案

AKF

批注 2020-06-23 084747

集群搭建

节点准备:

# 节点端口
port 6379
#  开启集群模式
cluster-enabled yes
#  节点超时时间,单位毫秒
cluster-node-timeout 15000
#  集群内部配置文件
cluster-config-file "nodes-6379.conf"

节点握手:

cluster meet 127.0.0.1 6380
...

节点建立握手之后集群还不能正常工作 需要为各个节点分配slot:

redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
...

作为一个完整的集群,每个负责处理槽的节点应该具有从节点,保证当它出现故障时可以自动进行故障转移:

使用cluster nodes命令查看集群节点

让其他节点做复制:

cluster replicate 41f2232cc928fb61c8a201b7d1cc1e57f029752e
...

使用redis-cli:

redis-cli --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6380 127.0.0.1:6379 --cl
uster-replicas 1

redis-cluster原理

redis cluster 有固定的 16384 个 hash slot,集群中的每个node平均分配得到一定的slot

使用一致性哈希实现

优点:

缺点:

更倾向于 作为缓存,而不是数据库用

Redis Cluster 方案提供了一种重定向机制,当客户端把一个键值对的操作请求发给一个实例时,如果这个实例上并没有这个键值对映射的哈希槽,那么,这个实例就会给客户端返回下面的 MOVED 命令响应结果,这个结果中就包含了新实例的访问地址

节点间的通信

节点彼此不断通信交换信息,一段时间后所有的节点都会知道集群完整的信息

redis 维护集群元数据采用了gossip协议,所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点

但是元数据的更新有延时,可能导致集群中的一些操作会有一些滞后

gossip协议

stateDiagram-v2
  节点1 --> 新节点: meet
  新节点 --> 节点1: pong
  节点1 --> 节点2: ping
  节点2 --> 节点1: pong
  节点1 --> 节点3: fail
  节点1 --> 节点4: fail
  节点1 --> 新节点: fail

节点选择

节点的定时任务每次会随机找出几个最久没通信的节点,如果找出来的节点最后通信时间大于一定阈值,则会向这些节点发送 ping 消息

集群伸缩

数据槽点迁移:

(1) 准备新节点 (2) 加入集群 (3) 迁移slot到新节点

迁移slot到新节点的内部过程:

在执行数据迁移时,向目的节点发送迁移数据以及读取回复结果都是同步的,这个同步写和同步读的过程,会阻塞源节点正常处理请求

stateDiagram-v2
   slot[0,5461] --> 6379
   slot[5462,10922] --> 6380
   slot[10923,16383] --> 6381

  6379 --> 6382: move slots
  6380 --> 6382: move slots
  6381 --> 6382: move slots
graph
  下线节点 --> A{是否持有slot}
  A --> |y| 迁移slot到其他节点
  A --> |n| 通知其他节点忘记下线节点
  迁移slot到其他节点 --> 通知其他节点忘记下线节点
  通知其他节点忘记下线节点 --> 关闭节点
stateDiagram-v2
  6381 --> 6379:slots
  6381 --> 6380:slots
  6381 --> 6385:slots

扩容:

收缩:

...

请求路由

请求重定向:

sequenceDiagram
  客户端 ->> 节点1: 发送键命令
  节点1 ->> 节点1: 计算键所在的节点
  alt 指向自身
    节点1 ->> 节点1: 执行命令
  end
  alt 指向别的节点
    节点1 -->> 客户端: MOVED 节点2
    客户端 ->> 节点2: 发送键命令
  end
int slot = keyHashSlot((char*)key->ptr, sdslen(key->ptr));
clusterNode *node = getNodeBySlot(slot);

当访问的key的CRC16结果不是本节点时,集群节点就会返回一个MOVED错误,并提供实际节点

127.0.0.1:6379> set hello2 value1
(error) MOVED 7486 127.0.0.1:6380

使用redis-cli时 加上-c参数支持自动重定向

ASK重定向:

例如当一个slot数据从源节点迁移到目标节点时,期间可能出现一部分数据在源节点,而另一部分在目标节点 客户端需要自行处理这种情况

ASK重定向说明集群正在进行slot数据迁移,客户端无法知道什么时候迁移完成,因此只能是临时性的重定向,客户端不会更新slots缓存

故障转移

故障恢复:故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个替换它

集群运维

集群完整性:

为了保证集群完整性,默认情况下当集群16384个槽任何一个没有指派到节点时整个集群不可用 当持有槽的主节点下线时,从故障发现到自动完成转移期间整个集群是不可用状态 为了避免这种情况 可以设置cluster-require-full-coverage为no

带宽:

集群带宽消耗主要分为:读写命令消耗+Gossip消息消耗

PUB/SUB问题:Pub/Sub功能应该避免在大量节点的集群内使用,否则会严重消耗集群内网络带宽

集群倾斜:

集群读写分离:

手动故障转移:

cluster failover命令

数据迁移(从单机导入集群):

redis-cli --cluster import命令