Redis学习笔记-集群-Cluster

Cluster
前言 本文的集群是指redis3.0版本中集群(Cluster)的功能,而并非广义上的集群。本文以3主3从来讲述集群。
背景 在Cluster功能未出现之前,redis的集群环境需要依靠哨兵来做自动化监控、故障恢复。即使用了哨兵,此时的redis集群的每个数据库依然存在集群中的所有数据,从而导致集群的总数据存储量受限于存储内存最小的数据库节点,形成木桶效应。此时需要将redis水平扩容,在3.0版本之前,通常使用客户端分片来解决问题,然而,客户端分片维护起来繁琐,缺点较多。因此,redis3.0版本推出了集群(Cluster)的功能。当然,具体使用哪种方式搭建redis环境,还需依照实际环境来分析。
特性

  1. 拥有和单机实例同样的性能
  2. 网络分区后能够提供一定的可访问性以及对主数据库故障恢复的支持
  3. 支持几乎所有的单机实例支持的命令(多键操作,如MGET,如果每个键位于同一个节点中,则可以正常支持)
  4. 只能使用默认的0号数据库
配置
# 使用集群,只需要将每个数据库节点的cluster-enabled配置项打开即可 cluster-enabled yes # 集群将当前节点记录的集群状态默认存储在当前工作目录的nodes.conf文件中 # 可用下述配置项修改持久化文件的名称 cluster-config-file nodes.conf

初始化 使用redis-trib.rb初始化集群
增加节点 通过CLUSTER MEET ip prot命令来新增节点
分配插槽 新增的节点要么以从数据库运行,要么以主数据库运行
从数据库 通过CLUSTER REPLICATE命令复制每个主数据库
主数据库 【Redis学习笔记-集群-Cluster】分配插槽有两种情况:
  1. 插槽未被分配过,要分配给某个节点
  • 通过CLUSTER ADDSLOTS slot1 [slot2]...[slotN]命令分配插槽
  1. 插槽被分配过,需要移动到某个节点
  • 通过redis-trib.rb的reshard命令
迁移 redis-trib.rb的迁移过程,假设要将0号插槽从A迁移到B:
1. 在B执行CLUSTER SETSLOT 0 IMPORTING A
2. 在A执行CLUSTER SETSLOT 0 MIGRATING B
3. 执行CLUSTER GETKEYSINFSLOT 0获取0号插槽的键列表
4. 对第3步获取的每个键执行MIGRATE命令,将其从A迁移到B
5. 执行CLUSTER SETSLOT 0 NODE B来完成迁移
其中的第1步和第2步的操作是为了解决迁移过程中可能出现键临时“丢失”的问题:
在前两步操作执行完后,A和B对0插槽的键的请求,会做特殊处理:
  • 客户端向A请求0中的键时
  1. 如果尚未迁移,还在A中,则直接返回;
  2. 如果已经迁移,并不在A中了,则返回一个ASK跳转请求,告诉客户端键在B那边,去请求B;客户端首先向B发送ASKING命令,然后再重新发送之前的命令
  • 客户端向B请求0中的键时
  1. 如果之前先执行了ASKING命令,则返回键值内容(说明键确实在B节点上)
  2. 否则,返回MOVED跳转请求,重定向到A(由于是从A迁移插槽到B,所以,若没有执行ASKING请求,自然意味着,该键尚未迁移,还在A节点上)
B的命令处理流程 A的命令处理流程 是 否 是 否 之前是否收到ASKING命令 B接收到命令 正常处理并返回结果 返回MOVE请求, 重定向到A 键是否存在本节点 A接收到命令 正常处理并返回结果 返回ASK请求, 重定向到B 插槽与键名
  1. redis将键名的有效部分使用CRC16算法计算出散列值
  2. 将散列值对16834取模,将余数作为对应的插槽
键名的有效部分:
  1. 如果键名包含{符号,且在{符号后面存在}符号,并且{和}之间至少有一个字符,则有效部分是指{和}之间的内容
    • 若键名为{wlj1}:name,那么有效部分为:wlj1
  2. 如果不满足上一条规则,则整个键名为有效部分
    • 若键名为wlj1.name,则有些部分为:wlj1.name
所以可以通过设计键名来将相关键被分配到同一个节点上,以支持如MGET这样的多键命令。
插槽和节点 上文已经讲述了如何通过键名获取对应的插槽;但在实际的客户端中,我们往往关心的是键在哪个节点上,那么如何能够得到键在哪个节点上呢?实际上,只需要在某个节点上对该键执行相关命令操作,就能知道了:
# 对wlj这个键进行SET操作 redis xxx > SET foo 123456 (error) MOVED 12182 127.0.0.1:6382

返回(error) MOVED 12182 127.0.0.1:6382,redis告诉我们foo这个键在127.0.0.1:6382这台redis上,我们需要到这上面执行相关命令。
大部分语言的redis库都支持代理MOVE请求,自动重定向到对应的redis上,对开发人员来说这个过程是透明的,redis命令行也提供了-c的参数来支持命令自动重定向。
$ redis-cli -c -p 6380 redis 6380> SET foo 123456 -> Redirected to slot [12182] located at 127.0.0.1:6382 OK

故障恢复 发现故障
  1. 集群中的每个节点每个1秒钟就会随机选择5(3主3从,共6个节点)个节点,然后选择其中最久没有响应的节点发送PING命令
  2. 如果发现一定时间内目标节点没有响应,则发起PING命令的节点(A)会认为目标节点(B)疑似下线(PFAIL)
  3. 一旦A认为B疑似下线,则会在集群中传播该消息,其他节点收到消息后都会记录下这一消息
  4. 当集群中某一节点C收集到一半以上节点认为B节点疑似下线,则会认为B下线(FAIL),并在集群中传播该消息,从而使B在整个集群中下线
  5. 发现故障后,若是主数据库故障,并且有对应的从数据库,那么就会从中选取一个作为新的主数据库
恢复故障
  1. 发现其复制的主数据库的从数据库节点(A)向集群中的每个节点发送请求,要求选择自己成为主数据库
  2. 如果收到请求的节点没有选过其他人,则选择A为新的主数据库
  3. 若A发现超过一半的节点同意选择自己成为主数据库,则A成为主数据库
  4. 若有多个从数据库节点同时参选,则会出现没有任何节点当选的可能,那么每个参选节点会等待一个随机时间重新发起参选请求,进行下一轮选举,直到选举成功
  5. 从数据库当选后,会通过SLAVEOF NO ONE将自己转换成主数据库,并将旧的主数据库的插槽转换给自己负责
# 集群中如果一个至少负责一个插槽的主数据库下线,并且没有相应的从数据库可以执行故障恢复操作,那么这个集群是否进入下线状态,无法继续工作 # 该项默认为yes cluster-require-full-coverage yes

命令介绍
# 获取节点的运行ID CLUSTER NODES # 参考节点插槽分配情况 CLUSTER SLOTS # 为节点分配未分配过的插槽 CLUSTER ADDSLOTS slot1 [slot2]...[slotN]

参考 本文参考自李子骅老师的《Redis 入门指南》

    推荐阅读