redis集群

 
 

redis集群

数据分区

槽的分配

Redis Cluster采用虚拟槽分区,将所有键使用哈希函数映射到编号为0~16383槽(slot)内,每个节点拥有一部分的槽 → 分布式存储

通过以下方式计算key属于哪一个slot:

slot  = CRC16(key) % 16384

每个节点只需要维护自己被分配的slot以及slot中的key-value关系

Hash Tag

在Redis Cluster中可以使用Hash Tag的方式使得具有指定形式的key保存在同一个slot中:

key{...}

各个key的命名拥有相同的Hash Tag即中括号及其中的内容{...}相同,例如:

Hash Tag 折叠源码
127.0.0.1:6380> set 1test hello
-> Redirected to slot [15801] located at 127.0.0.1:6381
OK
127.0.0.1:6381> set 2test hello
-> Redirected to slot [4971] located at 127.0.0.1:6379
OK
 
 
127.0.0.1:6380> set 1{test} hello
OK
127.0.0.1:6380> set 2{test} hello
OK
 
 
127.0.0.1:6380> set test{11
OK
127.0.0.1:6380> set test{21
-> Redirected to slot [5649] located at 127.0.0.1:6384
OK

集群伸缩

当集群中有节点上下线时,节点之间存在槽的迁移。

扩容集群

手动执行

  1. 准备节点
    准备节点的配置文件并启动
            redis-server conf/redis-XXXX.conf
  2. 加入集群
    在新节点上使用cluster meet命令使节点加入到集群中:
            cluster meet {ip}
    其中{ip}为已在集群中的任一节点。
  3. 迁移槽和数据
    迁移流程
    a)对目标节点发送cluster setslot {slot} importing {sourceNode}命令,使目标节点准备导入槽的数据。
    b)对源节点发送cluster setslot {slot} migrating {sourceNode}命令,使源节点准备导入槽的数据。
    c)源节点循环执行cluster getkeysinslot {slot} {count}命令获取{count}个属于槽{slot}的键。
    d)在源节点上执行migrate {targetIP} {targetPort} "" 0 {timeout} keys {keys ...}命令,将获得的键批量迁移到目标节点。
    e)重复执行c)和d)直至将槽中的所有键值数据迁移到目标节点
    f)在集群所有主节点执行cluster setslot {slot} node {targetNodeId}命令,通知槽分配给目标节点。
    使用redis-trib.rb进行槽重分片
           redis-trib.rb reshard host:port --from --to --slots --yes --timeout  --pipiline

使用redis-trib.rb

  1. 使用redis-trib.rb添加新节点
    redis-trib.rb add-node new_host:new_port existing_host:existing_port [--slave / --master-id ]
  2. 分配槽
    redis-trib.rb reshard host:port --from --to --slots --yes --timeout --pipeline
    从多个source node分配出n个slots到一个target node

收缩集群

与集群扩容相反,先将要下线节点的槽迁移到其他节点,再将节点下线cluster forget {NodeId},然后删除该节点的节点配置文件和数据文件

或使用redis-trib.rb del-node将该节点剔出集群(被执行的节点会被shutdown),然后删除该节点的节点配置文件和数据文件


请求重定向

MOVED重定向(长期性)

集群中的节点接收到客户端的键相关命令后计算键对应的槽,若槽在该节点则执行命令,若不在则返回MOVED重定向告知正确节点,客户端对该槽的命令长期地发送给正确节点

  1. 计算槽
  2. 槽节点查找

ASK重定向(临时性)

集群中的节点接收到客户端的键相关命令后计算键对应的槽,若槽不在可返回ASK重定向告知正确节点,客户端的下一命令请求发给正确节点,且发送前先发送ASKING命令

区别

MOVED重定向说明键对应的槽已指定到新的节点,需要客户端更新slots缓存

ASK重定向说明集群中正在进行槽和数据迁移,客户端不更新本地slots缓存


故障转移与failover机制

故障发现

  • 主观下线(pfail):某个节点认为另一个节点不可用

集群内节点之间定期发送ping消息,若一个节点在一定时间(cluster-node-timeout)内与另一节点通信一直失败,则认为该节点存在故障

不同节点对同一节点通信状况不同,可能导致误判

  • 客观下线(fail):集群内多个节点认为一个节点不可用

多个主节点对一个节点判断主观下线。通过Gossip消息传播,集群内节点不断收到故障节点的下线报告,并更新内部下线报告链表。若有大于槽主节点数量一半的节点判断主观下线,则进行客观下线,向集群广播fail消息

下线报告的有效期限为cluster-node-timeout * 2,超过期限则会被删除

故障恢复

故障节点客观下线后,若该节点为持有槽的主节点,则在它的从节点中选出一个升为主节点

  1. 资格检查:若从节点与主节点断线时间超过一定时间(cluster-node-timeout * cluster-slave-validity-factor)则不具备故障转移资格
  2. 准备选举时间:延迟触发机制,达到故障选举时间后(failover_auth_time)从节点才能继续发起选举,延迟越低的从节点复制偏移量越大,则优先级越高
  3. 发起选举:从节点到达故障选举时间后,更新配置纪元,在集群内广播选举消息
  4. 选举投票:持有槽的所有主节点包括故障节点投票,若从节点得票数大于总投票数的一半+1(N/2 + 1)则可升为主节点
  5. 替换主节点:从节点升为主节点,将主节点的槽迁移到从节点,向集群广播该从节点接管故障节点的消息

故障转移时间

  • 主观下线(pfail)识别时间 = cluster-node-timeout
  • 主观下线状态消息传播时间 ≤ cluster-node-timeout / 2
  • 从节点转移时间 ≤ 1000ms
  • 故障转移时间failover-time ≤ cluster-node-timeout + cluster-node-timeout / 2 + 1000

自动failover → 故障转移

当主节点故障时,其从节点升为主节点 → 自动回复集群的可用性

  1. 故障发现
  2. 故障恢复

手动failover → 手动故障转移

人为执行从节点接管主节点 → 集群的可运维操作

CLUSTER FAILOVER [ FORCE | TAKEOVER ]

FORCE:当master已不可用,无法通知到,则可以强制执行(直接从第四步开始),直接进行进行故障恢复的选举步骤(手动切换)

TAKEOVER:不需要集群达成共识,即不需要进行集群内的选举(直接到第五步)

TAKEOVER会生成新的configEpoch,可能与其他实例的冲突


其他

读写分离

  • 维护主节点的可用从节点列表
    cluster slaves {nodeId}        查看nodeId对应主节点的所有从节点信息
  • 针对读命令维护请求节点路由
  • 从节点新建连接开启readonly状态

集群倾斜

数据倾斜

  • 槽分配不均
    redis-trib.rb rebalance {ip}
    重新平衡主节点上的槽
  • 不同槽对应键数量差异过大
  • 集合对象包含大量元素
  • 内存相关配置不一致

请求倾斜

手动故障转移

在从节点执行cluster failover命令发起转移流程:

  1. 从节点通知主节点停止处理客户端请求
  2. 主节点发送对应从节点延迟复制的数据
  3. 从节点接收数据使复制偏移量一致
  4. 从节点立即发起投票选举,成功后断开复制成为主节点,广播消息
  5. 原来的主节点更新配置成为从节点,解除请求阻塞
  6. 原来的主节点成为从节点后向新的主节点发起全量复制

数据迁移

redis-trib.rb import

redis-migrate-tool

相关推荐