RedisCluster是redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求,当遇到单机内存、并发等瓶颈时,可使用此方案来解决这些问题

一、分布式数据库概念:

1、分区

分布式数据库把整个数据按分区规则映射到多个节点,即把数据划分到多个节点上,每个节点负责整体数据的一个子集,比如我们库有900条用户数据,有3个redis节点,将900条分成3份,分别存入到3个redis节点
Redis系列(七) 高可用之集群 - 图1

2、分区规则:

常见的分区规则哈希分区和顺序分区,redis集群使用了哈希分区,顺序分区暂用不到,不做具体说明;
rediscluster采用了哈希分区的“虚拟槽分区”方式(哈希分区分节点取余、一致性哈希分区和虚拟槽分区),其它两种也不做介绍,有兴趣可以百度了解一下。

3、虚拟槽分区(槽:slot)

RedisCluster采用此分区,所有的键根据哈希函数(CRC16[key]&16383)映射到0-16383槽内,共16384(2^14)个槽位,每个节点维护部分槽及槽所映射的键值数据
哈希函数: Hash()=CRC16[key]&16383 按位与槽与节点的关系如下,
槽与节点的关系:
Redis系列(七) 高可用之集群 - 图2
槽、键、数据关系(16384个槽位中的每个槽位都可以存多个键):
Redis系列(七) 高可用之集群 - 图3
补充: redis用虚拟槽分区原因:解耦数据与节点关系,节点自身维护槽映射关系,分布式存储

4、redisCluster的缺陷:

a、键的批量操作支持有限,比如mset, mget,如果多个键映射在不同的槽,就不支持了。
b、键事务支持有限,当多个key分布在不同节点时无法使用事务,同一节点是支持事务。
c、键是数据分区的最小粒度,不能将一个很大的键值对映射到不同的节点。
d、不支持多数据库,只有0,select 0。
e、复制结构只支持单层结构,不支持树型结构。

二、集群环境搭建-手动篇:

1、在/usr/local/bin/clusterconf目录,

Redis系列(七) 高可用之集群 - 图4
6389为6379的从节点,6390为6380的从节点,6391为6381的从节点

2、分别修改6379、 6380、 7381、 6389、 6390、 6391配置文件

  1. port 6379 //节点端口
  2. cluster-enabled yes //开启集群模式
  3. cluster-node-timeout 15000 //节点超时时间(接收pong消息回复的时间)

cluster-config-file /usrlocalbin/cluster/data/nodes-6379.conf 集群内部配置文件,其它节点的配置和这个一致,改端口即可

3、配置完后,启动6个redis服务

命令:

  1. cd /usr/local/bin/clusterconf/data
  2. cat nodes-6379.conf //查看6379节点ID值

Redis系列(七) 高可用之集群 - 图5
也可以这样查看:6379>cluster nodes
Redis系列(七) 高可用之集群 - 图6
分别启动redis6379、6380、6381、 6389、 6390、 6391节点

  1. ./redis-server clusterconf/redis6379.conf &
  2. ./redis-server clusterconf/redis6380.conf &
  3. ./redis-server clusterconf/redis6381.conf &
  4. ./redis-server clusterconf/redis6389.conf &
  5. ./redis-server clusterconf/redis6390.conf &
  6. ./redis-server clusterconf/redis6391.conf &

4、各节点启动后,使用cluster meet ip port与各节点握手,是集群通信的第一步

Redis系列(七) 高可用之集群 - 图7

5、握手成功后,使用cluster nodes可以看到各节点都可以互相查询到

Redis系列(七) 高可用之集群 - 图8

6、节点握手成功后,此时集群处理下线状态,所有读写都被禁止

Redis系列(七) 高可用之集群 - 图9

7、使用cluster info命令获取集群当前状态

Redis系列(七) 高可用之集群 - 图10

8、redis集群有16384个哈希槽,要把所有数据映射到16384槽,需要批量设置槽

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

  1. redis-cli -h 192.168.152.128 -p 6379 cluster addslots {0...5461}
  2. redis-cli -h 192.168.152.128 -p 6380 cluster addslots {5641...10922}
  3. redis-cli -h 192.168.152.128 -p 6381 cluster addslots {10923...16383}

9、分配完槽后,可查看集群状态

Redis系列(七) 高可用之集群 - 图11

10、然后再查看cluster nodes,查看每个节点的ID

Redis系列(七) 高可用之集群 - 图12

11、将6389,6390,6391与 6379,6380,6381做主从映射

  1. 127.0.0.1:6389> cluster replicate af2c99b58aa8a0a8fd82ba594654ce9264ffb9bc
  2. 127.0.0.1:6390> cluster replicate 2d6e6deb9512324336754b7b3fdf86032445c77c
  3. 127.0.0.1:6391> cluster replicate 61bd9fbbd3c154da318b502b86b1ee6516b82c17

注意: 在生产环境我们一般采用以下自动模式安装

三、集群环境搭建-自动安装模式

1、安装ruby

a、在/usr/local新建目录:ruby
下载链接:https://pan.baidu.com/s/1kWsf3Rh 密码:n3pc
从这个链接下载 ruby-2.3.1.tar.gz 和 redis-3.3.0.gem
tar -zxvf ruby-2.3.1.tar.gz
Redis系列(七) 高可用之集群 - 图13
b、分别执行下面指令:

  1. cd ruby-2.3.1
  2. ./configure -prefix=/usr/local/rubyWorkspace
  3. make && make install //过程会有点慢,大概5-10分钟

c、在redis-3.3.0.gem所在目录下执行

  1. yum install gem//安装gem
  2. gem install -l redis-3.3.0.gem

2、启动节点

备好6个节点,(注意不要设置requirepass)将/usr/local/bin/clusterconf/data的config-file删除;依次启动6个点:

  1. ./redis-server clusterconf/redis6379.conf &
  2. ./redis-server clusterconf/redis6380.conf &
  3. ./redis-server clusterconf/redis6381.conf &
  4. ./redis-server clusterconf/redis6389.conf &
  5. ./redis-server clusterconf/redis6390.conf &
  6. ./redis-server clusterconf/redis6391.conf &

如果之前redis有数据存在,flushall清空;(坑:不需要cluster meet …)
此时启动的节点还是没有关联的,

3、自动安装

进入cd /usr/local/bin, 执行以下:1代表从节点的个数

  1. ./redis-trib.rb create --replicas 1 192.168.1.111:6379 192.168.1.111:6380 192.168.1.111:6381 192.168.1.111:6389 192.168.1.111:6390 192.168.1.111:6391

./redis-trib.rb create —replicas num ip:port ip:port …
其中num表示每个主节点有多少个从节点,后面接的节点数必须是num的整倍数。这一步,主从分配、握手、槽位分配等都在这一步自动完成。
补充
1、只有主节点可读写,从节点不可以。
2、主节点死后,从节点变成主节点。

4、查看节点信息

进入其中一个节点之后执行指令:cluster nodes
Redis系列(七) 高可用之集群 - 图14

5、集群健康检测:

a、如果有问题
redis-trib.rb check 192.168.1.111:6379 (注:redis先去注释掉requirepass,不然连不上)
如此出现了这个问题,6379的5798槽位号被打开了:
Redis系列(七) 高可用之集群 - 图15
b、解决如下:
Redis系列(七) 高可用之集群 - 图16
6379,6380,6381的有部分槽位被打开了,分别进入这几个节点,执行
6380:>

  1. cluster setslot 1180 stable
  2. cluster setslot 2998 stable
  3. cluster setslot 11212 stable

其它也一样,分别执行修复完后:
Redis系列(七) 高可用之集群 - 图17
此时修复后的健康正常。
c、如果停掉6379节点,过会6389变成主节点
Redis系列(七) 高可用之集群 - 图18
重新启动6379会变成6389的从节点
Redis系列(七) 高可用之集群 - 图19

缺点:批量设置的时候,不同键/值对可能是设置在补同节点上。
mset aa bb cc dd,批设置对应在不同的solt上,
Redis系列(七) 高可用之集群 - 图20
注意:
使用客户端工具查询和修改时要加-c,否则操作不了
Redis系列(七) 高可用之集群 - 图21

请求路由重定向
我们知道,在redis集群模式下,redis接收的任何键相关命令首先是计算这个键CRC值,通过CRC找到对应的槽位,再根据槽找到所对应的redis节点,如果该节点是本身,则直接处理键命令;如果不是,则回复键重定向到其它节点,这个过程叫做MOVED重定向
Redis系列(七) 高可用之集群 - 图22

6、设置密码

集群正常启动后(在启动前设置会有问题比较麻烦),在每个redis.conf里加上

  1. masterauth 12345678
  2. requiredpass 12345678

当主节点下线时,从节点会变成主节点,用户和密码是很有必要的,设置成一致

三、节点之间的通信

1、节点之间采用Gossip协议进行通信,Gossip协议就是指节点彼此之间不断通信交换信息

Redis系列(七) 高可用之集群 - 图23
当主从角色变化或新增节点,彼此通过ping/pong进行通信知道全部节点的最新状态并达到集群同步

2、Gossip协议

Gossip协议的主要职责就是信息交换,信息交换的载体就是节点之间彼此发送的Gossip消息,常用的Gossip消息有ping消息、pong消息、meet消息、fail消息
Redis系列(七) 高可用之集群 - 图24

a、meet消息: 用于通知新节点加入,消息发送者通知接收者加入到当前集群,meet消息通信完后,接收节点会加入到集群中,并进行周期性ping pong交换
b、ping消息: 集群内交换最频繁的消息,集群内每个节点每秒向其它节点发ping消息,用于检测节点是在在线和状态信息,ping消息发送封装自身节点和其他节点的状态数据;
c、pong消息, 当接收到ping meet消息时,作为响应消息返回给发送方,用来确认正常通信,pong消息也封闭了自身状态数据;
d、fail消息: 当节点判定集群内的另一节点下线时,会向集群内广播一个fail消息,后面会讲到。……

3、消息解析流程

所有消息格式为:消息头、消息体,消息头包含发送节点自身状态数据(比如节点ID、槽映射、节点角色、是否下线等),接收节点根据消息头可以获取到发送节点的相关数据。

消息解析流程:
Redis系列(七) 高可用之集群 - 图25

4、选择节点并发送ping消息:

Gossip协议信息的交换机制具有天然的分布式特性,但ping pong发送的频率很高,可以实时得到其它节点的状态数据,但频率高会加重带宽和计算能力,因此每次都会有目的性地选择一些节点; 但是节点选择过少又会影响故障判断的速度,redis集群的Gossip协议兼顾了这两者的优缺点,看下图:
Redis系列(七) 高可用之集群 - 图26
节点选择的流程可以看出消息交换成本主要体现在发送消息的节点数量和每个消息携带的数据量。

流程说明:
A、选择发送消息的节点数量:集群内每个节点维护定时任务默认为每秒执行10次,每秒会随机选取5个节点,找出最久没有通信的节点发送ping消息,用来保证信息交换的随机性,每100毫秒都会扫描本地节点列表,如果发现节点最近一次接受pong消息的时间大于cluster-node-timeout/2 则立刻发送ping消息,这样做目的是防止该节点信息太长时间没更新,当我们宽带资源紧张时,在可redis.conf将cluster-node-timeout 15000 改成30秒,但不能过度加大
B、消息数据:节点自身信息和其他节点信息

四、集群扩容

这也是分布式存储最常见的需求,当我们存储不够用时,经常要考虑扩容。
扩容步骤如下:
A、准备好新节点
Redis系列(七) 高可用之集群 - 图27
B、加入集群,迁移槽和数据
Redis系列(七) 高可用之集群 - 图28

模拟新增节点步骤

1)、同目录下新增redis6382.conf、redis6392.conf两文件,修改端口,其他配置不变
启动两个新redis节点

  1. ./redis-server clusterconf/redis6382.conf & //(新主节点)
  2. ./redis-server clusterconf/redis6392.conf & //(新从节点)

2)、新增主节点

  1. ./redis-trib.rb add-node 192.168.1.111:6382 192.168.1.111:6379

6379是原存在的主节点,6382是新的主节点

3)、添加从节点

  1. redis-trib.rb add-node --slave --master-id 03ccad2ba5dd1e062464bc7590400441fafb63f2 192.168.1.111:6392 192.168.1.111:6379

–slave,表示添加的是从节点
–master-id 03ccad2ba5dd1e062464bc7590400441fafb63f2表示主节点6382的master_id
192.168.1.111:6392,新从节点
192.168.1.111:6379集群原存在的旧节点

4)、新主节点重新分配solt

  1. redis-trib.rb reshard 192.168.1.111:6382 //为新主节点重新分配solt

How many slots do you want to move (from 1 to 16384)? 1000 //设置slot数1000
What is the receiving node ID? 464bc7590400441fafb63f2 //新节点node id
Source node #1:all //表示全部节点重新洗牌
新增完毕!

五、集群减缩节点:

集群同时支持新增节点也支持节点下线。

1、下线的流程如下:

Redis系列(七) 高可用之集群 - 图29
流程说明:
A、确定下线节点是否存在槽slot,如果有,需要先把槽迁移到其他节点,保证整个集群槽节点映射的完整性;
B、当下线的节点没有槽或本身是从节点时,就可以通知集群内其它节点(或者叫忘记节点),当下线节点被忘记后正常关闭。

删除节点也分两种:一种是主节点6382,一种是从节点6392。

2、删除从节点

在从节点6392中,没有分配哈希槽,执行

  1. ./redis-trib.rb del-node 192.168.1.111:6392 7668541151b4c37d2d9

有两个参数ip:port 和节点的id。 从节点6392从集群中删除了。

3、删除主节点步骤

a、迁移哈希槽

  1. ./redis-trib.rb reshard 192.168.1.111:6382

Redis系列(七) 高可用之集群 - 图30
问我们有多少个哈希槽要移走,因为我们这个节点上刚分配了1000 个所以我们这里输入1000

b、最后节点下线
./redis-trib.rb del-node 192.168.1.111:6382 3e50c6398c75e0088a41f908071c2c2eda1dc900
此时节点下线完成……

六、故障转移和恢复

1、故障转移:

redis集群实现了高可用,当集群内少量节点出现故障时,通过故障转移可以保证集群正常对外提供服务。
当集群里某个节点出现了问题,redis集群内的节点通过ping pong消息发现节点是否健康,是否有故障,其实主要环节也包括了 主观下线和客观下线;

2、主观下线

主观下线指某个节点认为另一个节点不可用,即下线状态,当然这个状态不是最终的故障判定,只能代表这个节点自身的意见,也有可能存在误判;
Redis系列(七) 高可用之集群 - 图31
下线流程:
A、节点a发送ping消息给节点b ,如果通信正常将接收到pong消息,节点a更新最近一次与节点b的通信时间;
B、如果节点a与节点b通信出现问题则断开连接,下次会进行重连,如果一直通信失败,则它们的最后通信时间将无法更新;
C、节点a内的定时任务检测到与节点b最后通信时间超过cluster_note-timeout时,更新本地对节点b的状态为主观下线(pfail)

3、客观下线

客观下线指真正的下线,集群内多个节点都认为该节点不可用,达成共识,将它下线,如果下线的节点为主节点,还要对它进行故障转移。
假如节点a标记节点b为主观下线,一段时间后节点a通过消息把节点b的状态发到其它节点,当节点c接受到消息并解析出消息体时,会发现节点b的pfail状态时,会触发客观下线流程;
当下线为主节点时,此时redis集群为统计持有槽的主节点投票数是否达到一半,当下线报告统计数大于一半时,被标记为客观下线状态。
Redis系列(七) 高可用之集群 - 图32

4、故障恢复:

故障主节点下线后,如果下线节点的是主节点,则需要在它的从节点中选一个替换它,保证集群的高可用;转移过程如下:
Redis系列(七) 高可用之集群 - 图33
1、资格检查:检查该从节点是否有资格替换故障主节点,如果此从节点与主节点断开过通信,那么当前从节点不具体故障转移的资格;
2、准备选举时间:当从节点符合故障转移资格后,更新触发故障选举时间,只有到达该时间后才能执行后续流程;
3、发起选举:当到达故障选举时间时,进行选举;
4、选举投票:只有持有槽的主节点才有票,会处理故障选举消息,投票过程其实是一个领导者选举(选举从节点为领导者)的过程,每个主节点只能投一张票给从节点,
当从节点收集到足够的选票(大于N/2+1)后,触发替换主节点操作,撤销原故障主节点的槽,委派给自己,并广播自己的委派消息,通知集群内所有节点。