Redis学习笔记(五) 基于Redis 3.0的集群

目录

虽然我们搭建了一个主从架构,但是每个 Redis 都要保存相同的数据,这样容易造成水桶效应。而且主从架构频繁 TCP 连接断开也可能会对服务器和网络带来很大负担。 如果我们使用的是 Java 客户端 Jedis 中的 ShardedJedisPool,那么在增加新的 Redis 服务器之后,以前保存在其他 Redis 服务器上的数据就有可能访问不到。(因为 ShardedJedisPool 它是采用 hash 算法来分布 Redis 的 Key,当我们增加 Redis 服务器之后,整个 hash 计算出来的结果已经是不一样了。) Redis 3.0 版本最大的更新就是支持集群。接下来开始搭建 Redis 集群。

预热(Redis cluster 架构)

新特性:

① 节点自动发现 ② slave->master 选举,集群容错 ③ Hot resharding:在线分片 ④ 集群管理:cluster xxx ⑤ 基于配置(nodes-port.conf)的集群管理 ⑥ ASK 转向 / MOVED 转向机制

架构细节

① 领导者选举过程是集群中所有 master 参与,如果半数以上 master 节点与 master 节点通信超过 (cluster-node-timeout),认为当前 master 节点挂掉。

② 什么时候整个集群不可用(cluster_state:fail):当集群不可用时,所有对集群的操作都不可用,收到 (error)CLUSTERDOWN The cluster is down 错误。

具体情况:

a: 如果集群任意 master 挂掉,且当前 master 没有 slave,集群进入 fail 状态。也可以理解成集群的 slot 映射 [0-16383] 不完整时进入 fail 状态。

b: 如果集群超过半数以上 master 挂掉,无论是否有 slave,集群进入 fail 状态。

准备环境搭建

首先使用前面准备的 3 个 Redis 配置文件来搭建一个集群。

修改每个配置文件

开启集群:

cluster-enabled yes
cluster-config-file nodes-<port>.conf

nodes-6379.conf 不需要我们去维护,Redis 会主动去维护该文件。

因为 3 个都是 master 节点,所以需要关闭 slaveof 参数。

启动 Redis

redis-server /etc/redis.6379.conf
redis-server /etc/redis.6380.conf
redis-server /etc/redis.6381.conf

上面只是单独的启动了 3 个 Redis 实例,下面开始创建集群。

集群的创建

集群的创建需要一个 redis-trib.rb 脚本,所以需要一个 Ruby 的环境:

yum -y install zlib ruby rubygems
gem install redis

进入 Redis 的安装包目录下面的 src,执行命令:

./redis-trib.rb create --replicas 0 192.168.247.101:6379 192.168.247.101:6380 192.168.247.101:6381

--replicas 0 表示指定从库的数量。

注意

  • 这里不要使用 127.0.0.1,否则使用 Jedis 的时候会无法连接
  • Redis 实例不能有数据,因为集群之后每个数据都是根据插槽 (slots) 来计算的,不然会报 [ERR] Node 192.168.247.101:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or containssome key in database 0.

因为 Redis 集群之后每次设置值的时候都要先计算 key 的值获取该 key 的插槽值,然后在设置到集群中对应插槽值的 Redis 实例中。

用 Redis 客户端连接试试,如果没有换一个 key 试试,在 set 和 get 的时候都报错:

(error) MOVED 6918 192.168.247.101:6380

表示 test 的插槽值是 6918,查看上面的集群信息可以看到 6918 在 6380 这个实例上(提示也是这么说的)。那么用 6380 连接试试,这个时候设置成功了。

难道每次在 set 的时候都要重新打开一个 Redis 客户端吗?redis-cli 提供了一个参数 -c

redis-cli -c

指定了这个参数之后,redis-cli 会根据插槽值做一个重定向,连接到指定的 redis 实例上面。获取下刚刚在 6380 中插入的一个 key,从 6379 重定向了 6380,并且获取到了 test 的值。

综上所述,Redis 集群的数据是根据插槽值来设置进具体的节点中的。但是如果这个 key 的插槽值不是在当前 redis 实例的话,它就需要进行重定向。这样一次操作就变成了 2 次。这就是 Redis 3.0 里面所说的 ASK 转向 / MOVED 转向机制。 通过 cluster nodes 命令可以查看集群信息。

当在执行 set test test 的命令时,Redis 的执行步骤:

① 接受命令 set test test ② 通过 key (test) 计算出插槽值,然后根据插槽值找到对应的节点 ③ 重定向到该节点,执行命令

整个 Redis 提供了 16384 个插槽,也就是说集群中的每个节点分得的插槽数总和为 16384。redis-trib.rb 脚本实现了将 16384 个插槽平均分配给了 N 个节点。

如果有一部分插槽数没有指定,那这部分插槽数对应的 key 就不能使用了。

插槽数是怎么计算的

key的有效部分使用CRC16算法计算出哈希值,再将哈希值对16384取余,得到插槽值。

有效部分:如果 key 里面包含了一对 {},并且 {} 里面是有值的,那么有效部分就是 {} 里面的内容。例如 key_{test} 有效部分就是 test。

节点的新增和删除

再添加一个配置文件 6382。这个时候 6382 是没有加入集群的,通过 redis-trib.rb 脚本来添加:

./redis-trib.rb add-node 192.168.247.101:6382 192.168.247.101:6379

表示将新加入的 6382 节点添加至 6379 这个集群中来。

添加成功后查看集群信息。虽然 6382 已经添加进集群了,但是 6382 没有分配的插槽值,这个时候它没有什么用,所以接下来就要给它分配插槽值了。

通过命令重新分配插槽数

./redis-trib.rb reshard 192.168.247.101:6382

插槽的转移方式:

  • all:从其他每个拥有插槽节点中随机抽取几个到接收插槽的节点
  • done:从指定 id 的节点中转移插槽到接收插槽的节点,可以是多个,以 done 结束

最后输入 yes 确定。到了这里节点已经新增成功了。

这个插槽数还是很重要的。

删除节点

下面开始删除刚刚添加上来的 6382 节点。如果删除节点的话,要先把 6382 节点上面的插槽数给转移到别的节点上面,然后通过 redis-trib.rb 脚本来删除节点,不然这个插槽数就没有了,对应的 key 也失效。

开始转移插槽数:

./redis-trib.rb reshard 192.168.247.101:6382

这和刚刚新增节点分配插槽数一样,只是相反了一下。

然后就可以开始删除了:

./redis-trib.rb del-node 192.168.247.101:6382 c713d1ff80ad4973ead7ec947f5d60de82580cc1

最后面那个是 6382 在集群中的 ID。节点已经删除成功。

高可用集群

用 3 个 master 节点创建了一个集群,但是如果其中的一个 master 宕机了,那么它对应插槽值的 key 全部都会失效,集群就不可用。Redis 集群为我们提供了故障机制。

故障机制

① 集群中的每个节点都会定期的向其它节点发送 PING 命令,并且通过有没有收到回复判断目标节点是否下线

② 集群中每一秒就会随机选择几个节点,然后选择其中最久没有响应的节点发送 PING 命令

③ 如果一定时间内目标节点都没有响应,那么该节点就认为目标节点疑似下线

④ 当集群中的节点超过半数认为该目标节点疑似下线,那么该节点就会被标记为下线

⑤ 当集群中的任何一个节点下线,就会导致插槽区有空档,不完整,那么该集群将不可用

⑥ 如果该下线的节点使用了主从模式,那么该节点 (master) 宕机后,集群会将该节点的从库 (slave) 提升为 (master) 继续完成集群服务。该节点是一个高可用的节点。

创建主从集群

复制 3 个配置文件,修改成各自的 port。接下来就可以开始了。

启动 6 个 Redis 实例,开始创建集群,指定从库数量 1:

./redis-trib.rb create --replicas 1 192.168.247.101:6379 192.168.247.101:6380 192.168.247.101:6381 192.168.247.101:6479 192.168.247.101:6480 192.168.247.101:6481

启动成功了。(因为上次是直接关机的,没有一个一个删除节点,所以启动的时候报错了,把那个 node-[port].conf 的文件全部删除了就 OK 了。)

这是集群的信息。下面看下它的故障性能是怎样的。来个普通的 get set 试试,不错,一切都是 OK 的。

这个 key 设置到了 6380 上面,那么看下 6380 的 Slave (6480) 有没有数据。虽然有这个 key,但是获取的时候却需要重定向。只能知道它有 key,却获取不到数据,只能重定向到 6380 上面获取。

先把这个 Slave 干掉,看下对集群有没有影响。干掉之后,提示这个节点已经不能用了。试试看集群现在的使用情况,还是能在那里愉快的运作,一个 master 的 slave 挂掉对它真是一点影响都没有。

恢复 6480 节点,看下 6480 的恢复情况。恢复情况良好,并且集群信息提示也是连接状态。使用必杀技干掉 6480 的 master 试试。干掉之后要等它们讨论一会,slave 才会顶上变成 master。

slave 6480 已经变身成 master 了,并且插槽数也已经转移。集群还是杠杠的,而且 6380 上面的数据都已经到 6480 上面了。重新连接之后,6380 已经变成 slave,恢复不过来了。

注意

① 多键的命令操作(如 MGET、MSET),如果每个键都位于同一个节点,则可以正常支持,否则会提示错误

② 集群中的节点只能使用 0 号数据库,如果执行 SELECT 切换数据库会提示错误

#redis #高可用 #集群