官方文章:
本文永久地址:
本文档是Redis集群的简单介绍,没有涉及复杂难懂的分布式概念的赘述,只是提供了从用户角度来如何搭建测试以及使用的方法,若你打算使用并深入了解Redis集群,推荐阅读完本章节后,仔细阅读一章。本教程试图提供最终用户一个简单的关于集群和一致性特征的描述。
请注意,本教程基于Redis 3.0或更高的版本。如果你计划部署集群,那么我们建议你从阅读这个文档开始。
Redis集群基础
Redis集群定义
Redis集群提供一种redis运行方式,能将数据自动分片到多个Redis节点,且Redis集群在分区期间也提供一定的可用性,即在某些节点发生故障或无法通信时,集群也能正常工作,但当大面积节点故障,如大多数master都不可用时,集群就不能使用了。
从实用性角度,Redis集群提供一下功能:
·自动切割数据到多个Redis节点
·小部分节点故障或者不可达时,集群能正常工作
Redis集群TCP端口
Redis集群中每个节点都需侦听两个TCP端口,6379端口用于客户端通信,客户端通信端口加上10000(两个端口总是相差10000),如16379专用于集群总线(Cluster bus),用于节点间二进制协议的节点间通信。各节点使用集群总线故障检测,配置更新,故障转移授权等。客户端不能使用集群总线端口。
请注意,为了让Redis群集正常工作,需要为每个节点配置2个端口(必须):
·客户端端口(默认6379)需要对所有客户端和集群节点开放,因为集群节点需要通过该端口进行密钥迁移(keys migrations)
·集群总线端口(客户端端口+ 10000)对集群所有节点开放即可
集群总线使用不同的二进制协议(不同于客户端与Redis使用的协议)进行节点到节点的数据交换,此协议可以减少带宽和处理时间
Redis集群和Docker
目前,redis集群并不支持地址和端口都被重新映射的NAT环境。
Docker使用一种端口映射技术:运行在docker容器内部的程序可能使用的端口和客户端程序使用的端口不同,这对于同一服务器中同时使用相同端口运行多个容器很有用
为了使Docker与Redis Cluster兼容,您需要使用Docker的主机联网模式。请查看的--net=host选项以获取更多信息。
Redis集群数据分片
Redis集群不使用一致性哈希,而是一种不同的分片形式,其中每个键在概念上都是我们称之为哈希槽的部分。
Redis集群中有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。
集群中每个节点负责哈希槽的的一个子集,如一个有3点歌节点的Redis集群:
·节点A包含从0到5500的哈希槽
·节点B包含从5501到11000的哈希槽
·节点C包含从11001到16383的哈希槽
如此便能方便地添加和删除集群中的节点。如我想添加一个新节点D,我需要将节点A,B,C中的一些哈希槽移动到D;同样,想删除节点A,则可以移动由A服务的散列槽到B和C.当节点A将为空时,我可以将它从群集中彻底删除。
由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态
若多个key都属于一个哈希槽,那么集群能通过单个命令(或者一个完成的事务或Lua脚本执行)同时操作这些key,我们可以使用“哈希标签”(Hash tags)强制让多个key分配到同一哈希槽。“哈希标签”在中有介绍,简单来说就是若key含有大括号”{}”,则只有大括号中的字符串会被哈希,如“this{foo}和another{foo}”这两个key一定会被分配到同一个哈希槽,然后在具有多个间的命令中作为参数一起使用。
Redis集群主从模式
为了保证在部分主master节点挂掉或不能与大多数节点通信时保持可用性,Redis集群使用主从模式,保证每个哈希槽都有1个到N个副本。
上述例子我们有节点A,B,C三个,若节点B故障,集群则不再可用,且会丢失从5501到11000哈希槽。然而若每个master都有一个slave,也就是说A,B,C三个master节点就会分别有A1,B1,C1三个slave节点,即使B节点挂掉,集群也不会受影响,B1复制了B的数据,此时集群将提升B1作为新master继续正常工作。但B和B1同时挂掉,redis集群肯定就不可用了。
Redis集群对一致性的保证
Redis集群不能保证强一致性,在架构中这意味着在某些情况下,可能会丢失向客户端返回“确认写成功”的数据。
丢失写入数据的第一个原因就是redis异步复制数据,也就是说:
·客户端写入数据到master B
·master B向客户端返回“写入成功”
·master B传输数据到slave:B1,B2,B3
由上可知,B在回复客户端之前不等待B1,B2,B3的写入完成确认,因此这会对redis造成严重的延迟损失,就像客户端写入了数据,master B确认写入,但在B将数据复制到任何一个slave前就已经挂掉,那写入将永久丢失,因为被提升为新master的任何slave都没有收到该数据。
这与大多数每秒刷新数据到磁盘的数据库配置类似,也可以通过强制数据库在回复客户端前先刷新数据到磁盘来提高一致性,但这种做法性能很差,这种方式就是让Redis集群在使用同步复制。
到底使用哪种方式,要根据应用场景在性能和一致性之间有一个权衡。
若真的需要,redis集群也能通过“WAIT”指令实现同步复制,这使得写入丢失的可能行大大降低,但并不意味着使用同步复制Redis集群就是强一致性了,如master还未来得及同步数据到任一slave就挂掉,或者被提升为新master没有收到同步过来的数据,都会导致不一致性的发生。使用“WAIT”只能相对的提高一致性。
还有一个值得注意的场景发生在网络故障时,当客户端与少数节点(至少一个master节点)正常通信,但与其他大多数节点网络不通。以A,B,C,A1,B1,C1 三主三从组成的6个节点集群为例,另外一个客户端称之为Z1。网络故障产生网络分区后,可能A,C,A1,B1,C1在一个分区,而B和Z1在另一个分区。此时B仍然能接收写入请求,Z1仍然能写入B。若分区在很短时间能恢复,集群将继续正常运行,但若网络太久未恢复,网络分区持续时间太长,B1会在大多数节点都能通信的分区里面被提升为master,master B在网络恢复后,会成为master B1的新slave,且丢弃Z1发送给B的写操作,然后从B1重新复制数据。
节点间网络通信断开有一个时间限制,若断开时间超过了大多数节点能容忍的长度,那就会有一个新的master被选举出来。
这个时间被称之为节点超时(node timeout),这个时间对集群非常重要,当达到节点超时时间,master被认为已经下线,会有新master被选举出来。同样,在节点超时后,若master仍然不能联系到其他master,它将进入错误状态,并停止接收写入请求。
Redis集群参数配置
先介绍redis.conf中集群的参数配置,后面会有集群部署示例。
·cluster-enabled yes/no #Redis集群开关。Yes表示他是集群中一个节点,no表示他是一个普通单一的redis实例
·cluster-config-file nodes-6379.conf #配置被称之为“集群配置文件”,但此配置文件是集群节点自动维护,不需要人工干预,每次集群配置有所变动,都会自动更新到此文件,以便节点重启时能重新加载配置。该文件列出集群中其他节点的状态,持久化的参数选项等等
·cluster-node-timeout 15000 #单位毫秒,集群节点能够失联的最大时间,超过该时间,该节点就会被认为故障。Master超过此时间不可达,它的slave节点就会选举出新master替代之。另外,任何节点超过此时间没有与大部分master通信将停止接收任何请求
·cluster-slave-validity-factor 10 #用于限定slave与master的失联时长的倍数。若设置0,无论slave与master失联多久,slave都会尝试升级为master(只要slave无法与master通讯,都会尝试故障转移,Cluster有选举机制,有可能会被否决),若设置为正数,则失联的最大时长为(cluster-node-timeout * cluster-slave-validity-factor),超过此时间slave不具备故障转移资格,无法被提升为master。如cluster-node-timeout为5s,cluster-slave-validity-factor为10,,当slave与master失联超过50s后,slave就不再具备成为master的资格。注意,如果此参数不为0,可能出现由于某master节点失联却没有slave能顶上的情况,从而导致集群不能正常工作,此时,只有等到原来的master重新回归到集群,集群才恢复正常。
·cluster-migration-barrier 1 #此参数保证Redis集群中不会出现裸奔的master节点,即保证每个master节点都有slave节点。只有当一个master节点至少拥有给定数量个处于正常工作中的slave节点时,才会分配slave节点给集群中孤立的master节点。这个给定数量就是cluster-migration-barrier。给定数量是1意味着一个slave节点只有在其master节点另外至少还有一个正常工作的slave节点的情况下才会被分配(副本迁移)。那些分配后仍然剩余migration barrier个slave节点的master节点才会触发副本迁移,而不是分配前有migration barrier个slave节点的master节点会触发节点分配!!有关教程,请参阅本文档副本迁移部分
·cluster-require-full-coverage yes #在部分key所在的节点不可用时,如果此参数设置为”yes”(默认值), 则整个集群停止接受操作;如果此参数设置为”no”,则集群依然为可达节点上的key提供读操作(集群是否需要所有的slot都分配给在线节点才能正常访问)
创建和使用Redis集群(主要两步骤:创建实例和连接实例创建集群)
注意:手动部署一个redis集群,最主要是在学习各种操作。
若想尽快启动并运行一个集群,可以直接看下一节“使用create-cluster脚本直接创建Redis群集”
要创建一个redis集群,首要任务就是以集群模式运行几个空Redis实例,必须以集群模式运行,才能使Redis实例具有集群节点功能、支持集群节点指令。
以下是最小redis集群配置文件:
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
Redis集群正常运行至少需要3个master,对于第一次测试,建议启动一个由三主三从组成的六节点群集:
1、创建6个以redis实例端口名称的目录:
mkdir -pv /usr/local/cluster-test/700{0..5}
2、在每个端口号目录下创建配置文件redis700{0..5}.conf,内容为上面最小redis集群配置,注意修改端口号
3、启动6个redis集群节点,建议启动6个终端分别启动:
cd /usr/local/cluster-test/7000 ; redis-server /usr/local/cluster-test/7000/redis7000.conf & cd /usr/local/cluster-test/7001 ; redis-server /usr/local/cluster-test/7001/redis7001.conf & cd /usr/local/cluster-test/7002 ; redis-server /usr/local/cluster-test/7002/redis7002.conf & cd /usr/local/cluster-test/7003 ; redis-server /usr/local/cluster-test/7003/redis7003.conf & cd /usr/local/cluster-test/7004 ; redis-server /usr/local/cluster-test/7004/redis7004.conf & cd /usr/local/cluster-test/7005 ; redis-server /usr/local/cluster-test/7005/redis7005.conf &
由于每个实例都是新启动的,nodes.conf还没有自动生成,所以会发出此信息,且生成nodes.conf,都会为自己分配一个新的ID:
10390:M 31 May 14:33:58.078 * No cluster configuration found, I'm f219739744e2fca6f2e6e6c75c9c1f2caa95b05b
作为此节点在整个集群中的唯一标识,生成的ID将一直被各节点使用,节点使用ID来区分其他节点而非IP+PORT,这个ID在节点的整个生命周期内都不会改变,除非节点被移除集群。这个ID我们称之为节点ID(Node ID)。
4、此时6个集群模式的Redis实例已经运行,可以开始创建Redis集群。Ruby脚本“redis-trib”是一个可以让我们非常方便地创建Redis集群的命令行工具,该脚本用于创建新集群,状态检查,或给集群重新分片:
yum install rubygems ruby gem install redis #若提示ruby版本过低,请查看 https://www.cnblogs.com/erbiao/p/9117018.html 。另外,若你的redis版本是4.0.X,强烈建议安装低版本的redis版本库,否则reshard时会出现语法错误。具体请查看:https://www.cnblogs.com/erbiao/p/9138604.html redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 #“--replicas 1”表示每个master节点需要一个slave节点。其他参数就是需要加入这个集群的redis实例的地址。执行后,redis-trib会提供一些建议的配置。
... ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered. #表示有16384个哈希槽可用
显示如上信息表示集群创建完成
5、查看nodes.conf信息
cat /usr/local/cluster-test/7001/nodes.conf
06c5baf38ae368bbe8b7fef037d42026ab385590 127.0.0.1:7001@17001 myself,master - 0 1527753131000 2 connected 5461-10922
fdc0c79f9f782b218dad297b48bde32aa0acc31f 127.0.0.1:7005@17005 slave 3fbbebd7bd822dfada7db5bde983534ff2906d4f 0 1527753131000 6 connected
b88654ae05706a771d6f23d8877e890f99dba315 127.0.0.1:7004@17004 slave 06c5baf38ae368bbe8b7fef037d42026ab385590 0 1527753131000 5 connected
41ec008599691424cdf8cdf9f3dc26ab1eef7050 127.0.0.1:7003@17003 slave f219739744e2fca6f2e6e6c75c9c1f2caa95b05b 0 1527753131600 4 connected
f219739744e2fca6f2e6e6c75c9c1f2caa95b05b 127.0.0.1:7000@17000 master - 0 1527753131000 1 connected 0-5460
3fbbebd7bd822dfada7db5bde983534ff2906d4f 127.0.0.1:7002@17002 master - 0 1527753130599 3 connected 10923-16383
vars currentEpoch 6 lastVoteEpoch 0
使用脚本create-cluster创建redis集群
上述的创建方法比较繁琐,“utils/create-cluster/create-cluster”此脚本能简化创建步骤,此脚本会调用其他redis集群命令,所以最好在utils/create-cluster中执行,或者修改脚本文件也是可以的。
脚本“utils/create-cluster/create-cluster”有默认参数,可以根据需要重新定义。
如要启动一个三主三从6节点的Redis集群:
1、命令默认生成6个集群模式的Redis实例,端口30001 ~ 30006
cd utils/create-cluster/ ./create-cluster start
2、连接各个实例,创建Redis集群
cd utils/create-cluster/ ./create-cluster create
[OK] All 16384 slots covered. #表示创建成功
3、默认生成的文件都在当前目录,包括持久化AOF文件,日志文件,RDB数据文件(手动也会生成)
[root@hd4 create-cluster]# ll
总用量 92
-rw-r--r-- 1 root root 2518 5月 31 16:27 30001.log
-rw-r--r-- 1 root root 3683 5月 31 16:27 30002.log
-rw-r--r-- 1 root root 3683 5月 31 16:27 30003.log
-rw-r--r-- 1 root root 4739 5月 31 16:27 30004.log
-rw-r--r-- 1 root root 4739 5月 31 16:27 30005.log
-rw-r--r-- 1 root root 4739 5月 31 16:27 30006.log
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30001.aof
-rw-r--r-- 1 root root 0 5月 31 16:20 appendonly-30002.aof
-rw-r--r-- 1 root root 0 5月 31 16:20 appendonly-30003.aof
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30004.aof
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30005.aof
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30006.aof
-rwxrwxr-x 1 root root 2321 2月 3 00:39 create-cluster
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30001.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30002.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30003.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30004.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30005.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30006.rdb
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30001.conf
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30002.conf
-rw-r--r-- 1 root root 799 5月 31 16:27 nodes-30003.conf
-rw-r--r-- 1 root root 799 5月 31 16:27 nodes-30004.conf
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30005.conf
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30006.conf
-rw-rw-r-- 1 root root 1308 2月 3 00:39 README
4、其他
(1) 清除集群数据,会删除生成的所有文件,包括aof文件,日志文件,rdb文件,nodes.conf文件
cd utils/create-cluster/ ./create-cluster clean
(2) 停止集群
cd utils/create-cluster/ ./create-cluster stop
(3) create-cluster其他用法:
[root@hd4 create-cluster]# create-cluster
Usage: /usr/bin/create-cluster [start|create|stop|watch|tail|clean]
start -- Launch Redis Cluster instances.
create -- Create a cluster using redis-trib create.
stop -- Stop Redis Cluster instances.
watch -- Show CLUSTER NODES output (first 30 lines) of first node.
tail <id> -- Run tail -f of instance at base port + ID.
clean -- Remove all instances data, logs, configs.
clean-logs -- Remove just instances logs.
(4) 解析create-cluster脚本,我们可以根据需求修改脚本
a. PORT=30000 #集群端口从30000开始,生成“NODES”个实例
b. TIMEOUT=2000 #指定cluster-node-timeout
c. NODES=6 #生成节点数量
d. REPLICAS=1 #表示每个master需要一个slave
玩转Redis集群
现阶段Redis集群的一个问题就是现成的客户端库比较少。现知道的有如下几种:
· Ruby实现,是redis-rb的简单封装,实现了与集群交互的基础功能
· Python实现的redis-rb-cluster接口,支持大部分redis-py功能,正处于活跃开发状态
· 支持Redis集群,处于活跃状态
· java实现,最近添加了对集群的支持
· 提供C#支持(且对大多数.NET语言都适用,VB,F#等)
· 提供对Node.js和io.js的支持,它是支持pipelining和集群的thunk/promise-based客户端
· 集群工具,使用Redisgo库实现的go语言客户端,通过结果聚合实现MGET/MSET
·redis-cli github中的redis不稳定分支,-c选项提供了基础的集群管理功能
以下是redis-cli工具的检测测试:
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
redis-cli实现了集群客户端最基础的功能:将客户端请求正确重定向到正确节点。如上例中,写入和读取时会自动切换到不同节点。逻辑实现比较严谨的客户端可以缓存哈希槽到节点的映射关系,让客户端直接连接到正确的节点,只有集群配置发生改变时才刷新映射关系,如故障转移后,或者增加/删除节点。
使用“redis-rb-cluster”编写示例程序
在说明如何操作Redis集群前,如故障切换或重新分片,先创建一个示例程序了解Redis集群与客户端是怎么交互的。
我们通过一个例子,让部分节点故障或重新分片等,来了解这实际运作中,redis集群是如何处理的。如果这期间没有客户端对集群发起写操作,将不益于我们了解情况
这节通过2个例子来演示redis-rb-cluster的基础用法,下面是第一个例子,源码在redis-rb-cluster目录下的example.rb文件中。
1 require './cluster'
2
3 if ARGV.length != 2
4 startup_nodes = [
5 {:host => "127.0.0.1", :port => 7000},
6 {:host => "127.0.0.1", :port => 7001}
7 ]
8 else
9 startup_nodes = [
10 {:host => ARGV[0], :port => ARGV[1].to_i}
11 ]
12 end
13
14 rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)
15
16 last = false
17
18 while not last
19 begin
20 last = rc.get("__last__")
21 last = 0 if !last
22 rescue => e
23 puts "error #{e.to_s}"
24 sleep 1
25 end
26 end
27
28 ((last.to_i+1)..1000000000).each{|x|
29 begin
30 rc.set("foo#{x}",x)
31 puts rc.get("foo#{x}")
32 rc.set("__last__",x)
33 rescue => e
34 puts "error #{e.to_s}"
35 end
36 sleep 0.1
37 }
程序做的事情非常简单: 它不断地以foo<number>为键,number为值,使用SET命令向数据库设置键值对:
·SET foo0 0
·SET foo1 1
·SET foo2 2
·And so forth…
代码中的每个集群操作都使用一个begin和rescue代码块(block)包裹着,因为我们希望在代码出错时,将错误打印到终端上面, 而不希望应用因为异常(exception)而退出。
代码的第七行是代码中第一个有趣的地方,它创建了一个Redis集群对象, 其中创建对象所使用的参数及其意义如下:第一个参数是记录了启动节点的startup_nodes列表, 列表中包含了两个集群节点的地址。第二个参数指定了对于集群中的各个不同的节点, Redis 集群对象可以获得的最大连接数,第三个参数timeout指定了一个命令在执行多久之后,才会被看作是执行失败。
启动列表中并不需要包含所有集群节点的地址,但这些地址中至少要有一个是有效的:一旦redis-rb-cluster成功连接上集群中的某个节点时,集群节点列表就会被自动更新, 任何真正的的集群客户端都应该这样做。
现在,程序创建的Redis集群对象实例被保存到rc变量里面,我们可以将这个对象当作普通Redis对象实例来使用。
在十一至十九行,我们先尝试阅读计数器中的值,如果计数器不存在的话, 我们才将计数器初始化为0:通过将计数值保存到Redis的计数器里面,我们可以在示例重启之后,仍然继续之前的执行过程,而不必每次重启之后都从foo0开始重新设置键值对。为了让程序在集群下线的情况下, 仍然不断地尝试读取计数器的值, 我们将读取操作包含在了一个 while 循环里面, 一般的应用程序并不需要如此小心。
二十一至三十行是程序的主循环,这个循环负责设置键值对,并在设置出错时打印错误信息。程序在主循环的末尾添加了一个sleep调用,让写操作的执行速度变慢,帮助执行示例的人更容易看清程序的输出。执行example.rb程序将产生以下输出:
ruby ./example.rb
1
2
3
4
5
6
7
8
9
^C (I stopped the program here)
这个程序并不是十分有趣, 稍后我们就会看到一个更有趣的集群应用示例, 不过在此之前, 让我们先使用这个示例来演示集群的重新分片操作。
集群重新分片
现试试对集群进行重新分片操作。执行重新分片的过程中,让example.rb程序处于运行状态,这样你就会看到,重新分片并不会对正在运行的集群程序产生任何影响,你也可以考虑将example.rb中的sleep调用删掉,从而让重新分片操作在近乎真实的写负载下执行;重新分片操作基本上就是将某些节点上的哈希槽移动到另外一些节点上面,和创建集群一样,重新分片也可以使用redis-trib程序来执行
执行命令可开始一次重新分片操作:
redis-trib.rb reshard 127.0.0.1:7000
指定集群中一个节点地址即可,“redis-trib”就会自动找到集群中其他节点。
目前redis-trib只能在管理员的协助下完成重新分片,要让redis-trib自动将哈希槽移动到另一节点目前无法完成。另外,移动哈希槽目前只能以数量进行移动,不能以百分比进行移动,就是说指定redistrib移动50%哈希槽到另一节点是不行的,必须指定数字。
此命令以问题的方式开启一次重新分片(reshard)。
第一个问题,你想重新分片多少个哈希槽:
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots: (0 slots) slave
replicates 5055b631a9b310417fa75948a5e473e2e2e1cfee
M: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 1000
上面程序一直在运行,此时已经累计不少key了,尝试移动1000个哈希槽。
此时出现第二个问题,接收这些哈希槽的节点ID是多少:
What is the receiving node ID?a466e88499423858c5f53de9be640500d9fb3e5b
接下来“redis-trib”需知道要把这1000个哈希槽移动到哪个节点,即哪个目标节点负责接收这些哈希槽。此时我想移动到127.0.0.1:7000这个节点。指定节点要使用节点ID,且指定的节点必须是master。节点信息已经打印在屏幕。也可通过以下命令找到指定节点ID:
redis-cli -p 7000 cluster nodes |grep 127.0.0.1:7000
a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000@17000 myself,master - 0 1528103629000 1 connected 0-5460
可看到目标节点为:a466e88499423858c5f53de9be640500d9fb3e5b
接下来第三个问题,指定从哪些节点来移动keys到目标节点:
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots. #从其他每个master上取一些哈希槽移动到目标节点,选择“all”
Type 'done' once you entered all the source nodes IDs. #指定要从哪些节点取哈希槽,输入各节点ID,以回车键分隔,完成后输入“done”指示节点指定完成
Source node #1:d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
Source node #2:5055b631a9b310417fa75948a5e473e2e2e1cfee
Source node #3:done
接下来会列出要移动的哈希槽信息,然后提示是否执行重新分片计划:
Do you want to proceed with the proposed reshard plan (yes/no)?yes
确认后你将看到redis-trib移动的哈希槽的信息,移动的key也会打印出来。
检查一下哈希槽分布情况(平均分布的时,三个master平均能分配到16384/3个哈希槽):
redis-trib.rb check 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:0-5961,10923-11421 (6461 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
S: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots: (0 slots) slave
replicates 5055b631a9b310417fa75948a5e473e2e2e1cfee
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:11422-16383 (4962 slots) master
1 additional replica(s)
M: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots:5962-10922 (4961 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
如何编写重新分片脚本?
如果需要经常对集群执行重新分片,可以构建一些自动功能,但目前无法redis-trib自动重新平衡集群,检查整个集群节点上的key分布、智能地迁移哈希槽,该功能将在未来添加。
reshard也可不用交互模式执行,如以下命令行:
redis-trib.rb reshard --from--to --slots --yes :
一个更有趣的程序
前面使用的示例程序example.rb它只是不断地对集群进行写入,并不检查写入结果是否正确。如集群可能错误地将example.rb发送的所有SET命令都改成了SET foo 42,但由于example.rb并不检查写入后的值,所以它不会意识到集群实际上写入的值是错误的。因此,redis-rb-cluster 项目包含了一个名为consistency-test.rb的示例应用,这个应用比起example.rb有趣得多:它创建了多个计数器(默认1000个),并通过发送INCR命令来增加这些计数器的值。
在增加计数器值的同时,consistency-test.rb还执行以下操作:每次使用INCR命令更新一个计数器时,应用会记录下计数器执行INCR命令之后应该有的值。比如如果计数器的起始值为0,而这次是程序第50次向它发送INCR命令,那么计数器的值应该是50。
在每次发送INCR命令之前,程序会随机从集群中读取一个计数器的值,并将它与自己记录的值进行对比,看两个值是否相同。
也就是说,这个程序是一个一致性检查器(consistency checker):若集群在执行INCR命令的过程中,丢失了某条INCR命令,又或者多执行了某条客户端没有确认到的INCR命令,那么检查器将察觉到这一点:在前一种情况中,consistency-test.rb记录的计数器值将比集群记录的计数器值要大;而在后一种情况中,consistency-test.rb记录的计数器值将比集群记录的计数器值要小。
运行consistency-test程序将产生类似以下的输出:
$ ruby consistency-test.rb
925 R (0 err) | 925 W (0 err) |
5030 R (0 err) | 5030 W (0 err) |
9261 R (0 err) | 9261 W (0 err) |
13517 R (0 err) | 13517 W (0 err) |
17780 R (0 err) | 17780 W (0 err) |
22025 R (0 err) | 22025 W (0 err) |
25818 R (0 err) | 25818 W (0 err) |
结果展示了执行的读写和错误(由于系统不可用而没有接受的查询发生的错误)的数量.
若程序察觉了不一致的情况出现,它将在输出行的末尾显式不一致的详细情况。比如,如果我们在consistency-test.rb运行的过程中,手动修改某个计数器的值:
$ redis 127.0.0.1:7000> set key_217 0
OK
(in the other tab I see...)
94774 R (0 err) | 94774 W (0 err) |
98821 R (0 err) | 98821 W (0 err) |
102886 R (0 err) | 102886 W (0 err) | 114 lost |
107046 R (0 err) | 107046 W (0 err) | 114 lost |
在我们修改计数器值的时候,计数器的正确值是114(执行了114次INCR命令),因为我们将计数器的值设成了0, 所以consistency-test.rb会向我们报告说丢失了114个INCR命令。
这个程序作为测试程序很有意思,所以我们用这个程序来测试故障恢复
测试故障转移
在执行本节操作的过程中,请一直运行consistency-test程序。要触发一次故障转移,最简单的办法就是令集群中的某个master节点进入下线状态。首先用以下命令列出集群中的所有master节点:
redis-cli -p 7000 cluster nodes | grep master
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
通过命令输出得知端口号为7000、7001和7002的节点都是master节点, 然后我们可以通过向端口号7002的master节点发送DEBUG SEGFAULT命令,让这个master节点崩溃:
redis-cli -p 7002 debug segfault
Error: Server closed the connection
现在,切换到运行着consistency-test的标签页,可以看到consistency-test在7002下线之后的一段时间里将产生大量的错误警告信息:
18849 R (0 err) | 18849 W (0 err) |
23151 R (0 err) | 23151 W (0 err) |
27302 R (0 err) | 27302 W (0 err) |
... many error warnings here ...
29659 R (578 err) | 29660 W (577 err) |
33749 R (578 err) | 33750 W (577 err) |
37918 R (578 err) | 37919 W (577 err) |
42077 R (578 err) | 42078 W (577 err) |
从consistency-test的这段输出可以看到,集群在执行故障转移期间,总共丢失了578个读命令和577个写命令,但是并没有产生任何数据不一致。这听上去可能有点奇怪,因为在教程的开头我们提到过Redis使用的是异步复制,在执行故障转移期间,集群可能会丢失写命令。但是在实际上,丢失命令的情况并不常见,因为Redis几乎是同时执行将命令回复发送给客户端,以及将命令复制给slave节点这两个操作,所以实际上造成命令丢失的时间窗口是非常小的。不过,尽管出现的几率不高,但丢失命令的情况还是有可能会出现的,所以我们对Redis集群不能提供强一致性的这一描述仍然是正确的。现在, 让我们使用cluster nodes命令,查看集群在执行故障转移操作之后,主slave节点的布局情况:
redis-cli -p 7000 cluster nodes
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected
现在masters运行在 7000, 7001 和 7005端口上. 原来的master 7002现在变成了一个7005的一个slave节点.
命令的输出看起来有点复杂,其实他非常的简单,含义如下:
·节点ID
·IP:端口
·标志: master, slave, myself, fail, …
·如果是个slave节点, 这里是它的master节点的NODE ID
·集群最近一次向节点发送 PING 命令之后, 过去了多长时间还没接到回复。.
·节点最近一次返回 PONG 回复的时间。
·节点的配置版本号(configuration epoch):详细信息请参考Redis集群规范 。
·本节点的网络连接情况:例如 connected 。
·节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为5960至10921的哈希槽。
手动故障转移
有时在master没有任何问题时强制故障转移是很有必要的,如想升级master的Redis进程,一般会先故障转移,将其降级为slave再进行升级操作,以此降低对整个集群的影响。
Redis集群使用“CLUSTER FAILOVER”来手动执行故障转移,该命令必须在要故障转移的master的一个slave上执行。
相较于真实的master故障然后故障转移,手动故障转移是比较安全的,因为手动故障转移时客户端的切换是在确保新的master完全复制了故障的旧的master数据的前提下发生的,所以避免了数据的丢失。
手动执行故障转移时,slave日志如下:
# Manual failover user request accepted.
# Received replication offset for paused master manual failover: 347540
# All master replication stream processed, manual failover can start.
# Start of election delayed for 0 milliseconds (rank #0, offset 347540).
# Starting a failover election for epoch 7545.
# Failover election won: I'm the new master.
其基本过程如:客户端不再连接旧的master,已经连接了的会被锁住,同时旧master向slave发送复制偏移量,slave得到偏移量后开始故障转移,然后新master通知旧master更新配置,成为新master的slave,此时还连接在旧master上的客户端解锁后会被重定向到新master。
添加一个新节点
基本过程是:添加一个空节点,然后移动一些数据给他。有两种情况:若新增的是master,从集群的其他节点中转移部分数据给它;若新增的是slave,则告诉它从一个已知的节点中同步复制集。
添加master节点的情况
1、添加一个空的redis实例节点,此处添加一个运行在端口7006的实例
2、将空实例添加到现有集群中:
redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
>>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster.
[OK] New node added correctly.
可以看到.使用addnode命令来添加节点,第一个参数是新节点的地址,第二个参数是任意一个已经存在的节点的IP和端口
redis-trib脚本只是给发送“”消息给节点,这也可以手动地通过客户端发送,但redis-trib在发送之前会检查集群的状态,所以,还是用redis-trib脚本来操作集群会比较好。
3、查看集群状态:
redis-cli -c -p 7006 cluster nodes
406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005@17005 slave a466e88499423858c5f53de9be640500d9fb3e5b 0 1528264952479 7 connected
bde6fc14465ecdbc71c6630edb5f9a3ab0c45cf0 127.0.0.1:7006@17006 myself,master - 0 1528264951000 0 connected
5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001@17001 slave 9b1d9c3e7bbcc955afce649f439cd2d094957313 0 1528264951000 8 connected
9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003@17003 master - 0 1528264950000 8 connected 5962-10922
a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000@17000 master - 0 1528264951578 7 connected 0-5961 10923-11421
b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004@17004 slave d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 0 1528264952078 3 connected
d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002@17002 master - 0 1528264951477 3 connected 11422-16383
redis-trib.rb check 127.0.0.1:7006
>>> Performing Cluster Check (using node 127.0.0.1:7006)
M: bde6fc14465ecdbc71c6630edb5f9a3ab0c45cf0 127.0.0.1:7006
slots: (0 slots) master
0 additional replica(s)
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
S: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots: (0 slots) slave
replicates 9b1d9c3e7bbcc955afce649f439cd2d094957313
M: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots:5962-10922 (4961 slots) master
1 additional replica(s)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:0-5961,10923-11421 (6461 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:11422-16383 (4962 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
与其他master相比,新节点有两点区别:
·新节点未包含任何数据,因为哈希槽不是自动分配的,需要手工分配到新节点
·由于新节点还未包含哈希槽,当集群中某个slave想提成为master时,他不会参与选举过程
4、重新分配哈希槽,然后检查哈希槽分布:
redis-trib.rb reshard 127.0.0.1:7006 redis-trib.rb check 127.0.0.1:7006
>>> Performing Cluster Check (using node 127.0.0.1:7006)
M: bde6fc14465ecdbc71c6630edb5f9a3ab0c45cf0 127.0.0.1:7006
slots:0-1615,5962-7201,11422-12661 (4096 slots) master
0 additional replica(s)
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
S: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots: (0 slots) slave
replicates 9b1d9c3e7bbcc955afce649f439cd2d094957313
M: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots:1616-1990,7202-10922 (4096 slots) master
1 additional replica(s)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:2365-5961,10923-11421 (4096 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:1991-2364,12662-16383 (4096 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
添加slave副本节点的情况
添加一个slave副本节点有两种方法。
不管使用哪种方法都得先启动一个空Redis实例节点。
第一种方法:启动一个Redis空实例,后使用 redis-trib脚本的“--slave”选项:
redis-trib.rb add-node --slave 127.0.0.1:7007 127.0.0.1:7000
第一个参数是新slave节点的地址;
第二个参数是任意一个已经存在的节点的IP和端口,不需要指定此slave要添加为哪个master副本节点,redis-trib会在拥有最少slave节点的master节点中随机选一个作为新增节点的master节点
输出信息:
...
Automatically selected master 127.0.0.1:7006
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.
...
Configure node as replica of 127.0.0.1:7006.
...
也可以直接指定新增slave节点的master节点:
redis-trib.rb add-node --slave --master-id a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7008 127.0.0.1:7003
如此,便指定了新的slave节点是哪个master节点的副本集。
第二种方法:启动一个Redis空实例,后将新节点以一个空master节点的形式加入到集群,然后再用“”将其转换为slave节点。此方法也适用于给从slave节点更换master节点。
如,集群中master节点127.0.0.1:7002承载的哈希槽范围是1991-2364,12662-16383,节点ID是d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
1、将空实例添加到现有集群中:
redis-trib.rb add-node 127.0.0.1:7009 127.0.0.1:7000
2、连接空实例,执行复制进程:
redis-cli -p 7009 CLUSTER REPLICATE d1ce7d9db6086c41f13ef0ce3753f82a3bfc
3、认证信息:
redis-cli -p 7000 cluster nodes |grep slave |grep d1ce7d9db6086c41f13ef0ce3753f82a3bfc
b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004@17004 slave d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 0 1528271555000 11 connected
5837a7c77a04b5100222dca1d226e4980764a97f 127.0.0.1:7009@17009 slave d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 0 1528271555500 11 connected
可看到127.0.0.1:7004和127.0.0.1:7009都是127.0.0.1:7002的slave复制节点。
删除节点
使用“del-node”指令删除slave节点:
redis-trib.rb del-node 127.0.0.1:7000 ``
第一个参数是任意一个节点的地址
第二个节点是你想要移除的节点ID
使用“del-node”指令删除master节点:
1、必须先将哈希槽全部迁移至别的master节点
2、在使用“del-node”指令删除master节点
另外的一个方法是先将master故障转移降级为slave,且将另外的slave提升为master后再删除节点。显然,这样并没有减少master节点数量,最终还是只能先迁移哈希槽然后执行删除动作
Slave副本节点的迁移(Replicas migration)
Redis集群中给slaveslave节点更换master节点只需执行指令:
CLUSTER REPLICATE
在特定场景下,无需管理员协助,自动将一个slave节点从当前master节点切换到另一个master节点的自动重新配置过程叫副本迁移(Replicas migration)。
注意;更多副本迁移的详细介绍在中,此处只做简单介绍
在某些情况下,将slave节点迁移到另一个master节点下的原因通常是the Redis Cluster is as resistant to failures as the number of replicas attached to a given master
例如如果master节点和slave复制节点同时故障,那么一个master节点仅有一个slave复制节点的集群将不能再正常运行,因为没有其他Redis实例拥有故障master和slave节点所承载的哈希槽。虽然网络分区会造成一些节点被隔离,但其他情况也会导致集群故障,如硬件或者软件的故障导致节点宕机,此情况一般不会所有节点同时故障。再比如,集群中每个master节点都有一个slave节点,凌晨4点时一个slave节点被kill掉,然后master节点在凌晨6点被kill掉,这样依然会导致集群不能工作。
为提高系统可用性,可为每个master添加额外的slave复制节点,但会增加成本。副本迁移功能可以只为部分master节点增加更多的slave复制节点。如集群中有10个master节点,每个有一个slave复制节点,共20个实例,然后可以将一些master的slave增加至3个。
当集群中存在一个master节点有多个slave节点,但另一个master节点没有slave节点时,复制集迁移功能会在master的多个slave节点中找一个节点,给没有slave节点的master节点做复制集。如当凌晨4点一个slave节点挂掉,另一个slave节点将会代替它成为该master节点的slave节点,然后如果凌晨5点master节点挂掉还能有一个slave节点可以升级为master,此时集群仍可以正常运行。
因此副本迁移功能总结为:
·为了让副本迁移功能生效,需要在集群任意master节点上多添加几个slave节点
·在必要时,集群找到拥有最多slave节点的master,从其中挑选一个slave,进行副本迁移
·redis.conf中“cluster-migration-barrier”参数用于配置副本迁移。此参数保证Redis集群中不会出现裸奔的master节点,即保证每个master节点都有slave节点。只有当一个master节点至少拥有给定数量个处于正常工作中的slave节点时,才会分配slave节点给集群中孤立的master节点。这个给定数量就是 cluster-migration-barrier。给定数量是1意味着一个slave节点只有在其master节点另外至少还有一个正常工作的slave节点的情况下才会被分配(副本迁移)。那些分配后仍然剩余migration barrier个slave节点的master节点才会触发副本迁移,而不是分配前有migration barrier个slave节点的master节点会触发节点分配!!
升级Redis集群中的节点版本
升级slave节点版本
只需要停止slave节点并使用新版本的Redis重新启动在相同端口即可。在节点关闭期间客户端连接过来会被重定向到另外可用的slave节点上。
升级master节点版本
相对复杂,建议的流程是:
1、使用指令“CLUSTER FAILOVER”手动触发故障转移,使master节点变成slave节点
2、按照升级slave节点的方式升级该节点
3、手动触发故障转移,让它变成新的master节点
将Redis数据迁移到Redis集群
一般,数据迁移发生在两种情况下:
·原来的数据存在于一个Redis单实例
·原来的数据存在于一个分片环境中,数据存储在多个节点
两种情况迁移都很方便,重要的细节是程序是否使用多键(multiple keys)操作,以及怎样使用多键操作。有以下三种情况:
·没有操作多个key(包括操作多个key的指令、事务、lua脚本)。所有key都独立操作
·操作了多个key(包括操作多个key的指令、事务、lua脚本),但这些key都有相同的哈希标签,比如这些被同时操作的key:SUNION{user:1000}.foo {user:1000}.bar
·操作了多个key(包括操作多个key的指令、事务、lua脚本),这些key没有特别处理,也没有相同标签。
第三种情况redis集群无法处理,需修改程序:不要使用多个key,或者给这些key加上相同的哈希标签,然后就可以迁移啦!
前面两种情况可以直接做迁移,且迁移的流程一样。
假设数据现被切割并保存在N个master节点存储(N=1时就是没有分片的情况),则须以以下步骤将数据迁移至Redis集群
1、停止所有连接Redis集群的客户端程序。如果有自动在线迁移的方案,也许就不需要停止了。
2、使用指令“BGREWRITEAOF”让所有master节点产生AOF文件,等待文件创建完成
3、修改AOF文件名称为aof-1, aof-2...aof-N以区别
4、创建N个master节点,且开启实例的appendonly选项。Slave复制节点可晚些再添加
5、停止新Redis集群所有节点,将前面保存的aof-1, aof-2...aof-N,aof-1给第一个节点,aof-2给第二个节点,以此类推
6、重启所有节点,可能会有警告提示说依据配置部分key不应当存储在此节点
7、使用指令“redis-trib.rb fix”让集群依据哈希槽自动迁移数据
8、使用指令“redis-trib.rb check”检查集群状态是否正常
9、重启客户端程序,检测数据读写
除此之外还可使用指令“redis-trib.rb import”从外部实例中导入数据到redis集群。该命令将源实例中的数据移动到目标Redis集群(该指令会把源实例中的数据都删除)。若源实例版本是Redis2.8,导入过程可能会比较长,因为2.8版本还未实现数据迁移的连接缓存,所以建议把源实例Redis版本先升级到3.x,再进行迁移。