redis集群最少3个节点中的节点如何保证数据一致

Redis集群规范(一)
主要特性和设计原理
Redis集群目标
Redis集群是Redis集群中的一种分布式实现,它有以下目标(按照重要性进行排序):
高性能,可以线性扩展至1000个节点;没有代理,使用异步复制,在values上面没有合并操作。
可接受范围内的写安全:尽最大努力执行从客户端(和绝大多数master节点连接)发过来的所有写操作。在极端情况下,可能会有一小部分确认写会发生丢失。如果客户端只是连接了少数节点( are in a minority partition),丢失的确认写数目会更多。
可用性:当每个挂掉的master都至少有一个可达的slave,并且绝大多数master节点都是正常的时候,Redis集群还可以正常运行。通过副本迁移(replicas migration,),没有slave的master会从另外一个master(它有多个slave)得到一个slave。
实现的功能子集
Redis集群实现了所有非分布式Redis版本中对单个key操作的命令。哪些对多个key进行操作的命令(比如:set的union或intersection等),如果这些key都在一个节点上,那么Redis集群也是支持的。
Redis集群实现一个hash tags的概念,它主要用于将某些key存放在同一个节点上面;但是在手动reshading的时候,多个key的操作命令,在一段时间内可能会无法使用,不过单个key的操作还是可以正常使用的。
Redis集群不支持多个的概念,也就是说只有数据库0(SELECT命令无效)。
在Cluster集群中,client和server的角色
在Redis当中,cluster节点负责持有数据(hoding the data),获取cluster的状态(包括:将key映射到对应节点上面)。cluster节点还能够自动发现其它节点,检测到失败节点,并能够在需要的时候将slave提升为master,以确保有错误发生的时候,集群还能够继续工作。
为了执行任务,cluster的节点之间使用tcp进行连接,并使用私有的二进制协议进行信息传输(也称为:Redis Cluster Bus)。在Cluster Bus上,节点之间可以使用gossip协议对当前集群的信息进行泛洪传播,这样有利于发现新节点;可以发送ping包以确认其它节点都是正常工作的,也可以发送cluster messages来通知一种特殊事件的发生。Cluster Bus也用来在cluster节点间广播Pub/Sub消息,当有用户发送了主备切换的指令后,配合完成人工切换。
由于Cluster节点不能够代理请求,所有client也许会被重定向到其它节点上面(使用错误信息:MOVED或ASK)。理论上,client可以向集群中任何节点发送请求,期间可能获取到重定向请求,因此client不需要了解集群的状态。然而客户端可以缓存key和node的对应关系来提升性能。
Redis集群的节点之间是异步复制的,上一次故障转移成功(last failover wins)隐含着数据合并的功能。这就意味着上次被选举的master的数据集最终会被所有其它副本所替换。在数据分片之间总是存在一个丢失写的时间窗口。然而这个时间窗口对于连接到少数master的客户端和连接到多数master的客户端是很不一样的。Redis集群对连接到多数master的client发送的写,比连接到少数master的client发送的写,提供更好的成功保障。下面是一些在错误发生的时候,导致多数分片丢失确认写的场景例子:
1、一个写到达master在本地写入成功后,返回了client成功消息;但是孩没有来得及将本次写发送给slave进行复制就挂掉了;如果master过了一段时间都还没来得及恢复(其它slave提升为master了),那么,这个写就永远丢失了。这种情况应该比较少出现,master在同时回复客户端和slave的时候挂掉了。但这种情况的确是可能存在的。
2、另外一种理论上可能出现写丢失的情况是:
master因为一个分片变得不可访问
它被一个slave替换了
过了一段时间,master变得可以访问了
持有老的路由表的client可能会将数据写入到原来的master上面,直到它的路由表进行了更新。
第2种情况,一般不太可能发生;因为一个master如何在发生切换的这段时间内都无法和其它大多数master进行通信的时候,它是不会接受任何写操作的。当这个master的分片状态恢复写的时候,它也会预留一段不接受写的时间,以便通知其它节点配置发生变化了。而且这个问题发生还需要client的路由表也没有发生更新。
对某个分片的少部分master进行写操作有更大的写丢失窗口。比如:RedisCluster会丢失一定数目的只有少部分节点才完成的写操作;客户端发送的写操作,只有少数master进行了更新,而其它大多数节点都没有更新,在这个过程中如果大多数节点中有的节点发生了切换,那么这些写就可能被丢失。
总结:一个master挂了,条件是至少在NODE_TIMEOUT时间内它和绝大部分master都无法通信,所以从这个时候开始到分片修复这段时间,写不会丢失;当分片失败持续时间超过NODE_TIMEOUT,那么截至到这个时间点,在少部分node上执行的写也许会丢失;但是那些少部分的节点,如果经过了NODE_TIMEOUT后还无法连接大多数的master,那么它们会启动禁止写操作;所以,对于少部分节点变得不可达有一个最大的时间窗口(就是NODE_TIMEOUT)。经过了这个时间窗口之后,没有写会接受或丢失。
Redis Cluster在分片的少数节点上不可用。假定每一个不可用的少数节点都有一个slave,该分片的大多数节点都是可用的,那么在NODE_TIME加上几秒钟的切换时间之后(一般1,2秒钟),Redis Clusteri又会变得可用。
这就是说Redis Cluster的设计适用于集群中有部分节点失败的时候,还可以自动进行恢复。但是,它不适用于哪些要求在大多数节点都失败的情况下还能恢复集群的应用。
假设有N个Master的Redis集群,每个Master都有一个如果有一个节点挂掉了,那么集群还是可用的;如果有两个节点挂掉了,那么可用性为:1-(1/(N*2-1)),因为两个节点挂掉了的时候,第一个节点挂掉之后还剩余节点:N*2-1个,而这个节点的slave也挂掉的可能性为:1/(N*2-1).
如果有5个节点(每个节点有一个slave),那么有1/(5*2-1)=11.1%的可能性,有2个节点挂掉之后,集群不可用了。
由于Redis Cluster有副本迁移(replica migration)的功能,在现实场景下,Redis Cluster的可靠性更高,因为Redis Cluster会自动将副本迁移到没有任何副本(slave)的master上面(orphanded masters)。因此,每一次从失败中恢复之后,集群会自动调整slave的部署,以便下次更好地从失败中恢复过来。
Redis Cluster节点不会将命令发送到负责这个key的真正节点上面,而是引导client到负责给定key所在key空间的正确节点上面。最终client可以获取到最新的集群结构,那个node负责哪些key,后续,client可以直接将key转发到对应节点上面。
由于使用了异步复制,节点不会等待其它节点的确认(如果没有使用WAIT命令的话)
多个key的命令只适用于&接近&的keys上面,所以除了resharding,数据不会在在节点间迁移
一般的操作都和单机上执行一样,所以性能一般是N*单机性能。同时一个查询一般就是一个RT,因为客户端一般都会维持和节点的连接,所以延迟也和单机Redis差不多。
高性能和高可靠性;以及在可接受范围内的数据安全和可靠性的前提下,提供很好的容错性;这些就是Redis Cluster的主要目标。
为什么要避免合并操作
Redis Cluster的设计避免了相同key-value对在不同节点间的版本冲突,因为Redis数据模型不能够完全满足这个要求。在Redis中存储的数据量常常是很大的,同时数据类型一般都是比较复杂的。传输和合并各类数据值可能成为系统瓶颈,可能需要业务逻辑的参与,需要存储元数据的额外内存等等。
这里不是技术的问题,CRDTs或其他同步复制的机器可以有和Redis类似的复杂数据模型,但是它们的实际运行运行的情况和Redis Cluster不一样;Redis Cluster设计的目标是让Redis Cluster的使用和单机版本一样。
Redis Cluster主要概述
Keys分布模型
key空间被分到16384个槽中,也就决定了Redis Cluster最多有16384个master节点(建议节点数目不要超过1000)。
cluster中的每一个master处理16384个hash slot的子集。当集群没有进行重配置的时候(重配置的时候,槽会从一个节点移动另外一个节点),我们称集群处于stable状态。当集群处于stable的时候,一个hash slot只会被一个node负责处理(但是读可以通过slave进行扩充)。
通过下面的算法将key映射到对应slot(槽)中
HASH_SLOT =CRC16(key) mod 16384
CRC16的算法过程如下:
名称:XMODEM(也称之为ZMODEM或CRC-16/ACORN)
宽度:16位
Poly:1021(也就是x^16 + x^12 + x^5+1)
初始化:0000
Reflect Input Type:False
Reflect Output Type:False
Xor constant to output CRC:0000
Output for &&:31C3
Keys Hash tags
在hash slot计算中,有一个例外,它用来实现hash tags。Hash Tags是确保多个keys被分配到同一个hash槽中的方式。这个是为了在Redis Cluster上实现多个key的操作。
为了实现hash tags,一个key的hash slot计算稍稍有点不一样;如果一个key包含&{&..}&的样式,只有{和}之间的部分会用于计算hash slot。然而由于key中可能有多个{和},算法使用下面规则:
如果key包含一个{字符
并且如果有一个}字符和{对应
并且在第一个{和之后第一个}之间有一个或多个字符
这个时候将使用第一个{和之后第一个}之间的字符用来计算hash slot。
{user1000}.following和{user1000}.followers会hash到同一个槽上面,因为只有user1000用来计算hash值
foo{}{bar},将使用所有字符串进行hash值计算,因为第一个{和}之间没有字符
foo{{bar}}zap,{bar用来计算hash
foo{bar}{zap},只有bar用于计算hash
这个算法中有一个很有用的地方是,如果key以{}开头,那么整个key都会用来计算hash
Cluster节点属性
Cluster中的每一个节点都有唯一的名字。节点名字是一个160位的16机制字符串,在节点第一次启动时候生成(一般使用/dev/urandom生成)。节点将会把这个名字保存在文件当中,以后都为使用这个名字保持不变(只要管理员没有删除这个文件或没有通过CLUSTER RESET命令发送的hand reset请求到来)。
node ID是用来标识集群中每个节点的,给某个节点只改动ip,而不改动node id是可以得。cluster可以检测到IP/Port的改变,然后使用运行在cluster bus上的gossip协议来重新配置。
nodeID不是每个集群中节点的唯一信息,但是是全局一致的信息。每个节点还有如下一些信息;一些信息是关于这个节点的详细配置信息,一些(比如:节点上次发送ping的时间)是节点本地拥有的信息。
每个节点都维护了其它节点的以下信息:nodeID,节点的IP、端口,一些标志,如果某个节点的flag是slave,那它对应的master是什么?,上次给这个节点发送ping的时间,最近一次接收到ping的响应pong的时间,节点的当前配置年代(configuration epoch),连接状态,负责处理的hash slot。
具体的配置信息,可以通过命令CLUSTER NODES来获取;这个命令可以发送给任何集群中节点,这个命令会返回集群的状态,以及这个节点看到的这个集群每个节点的信息
$ redis-cli clusternodes
da534d42d8a19aebe04 127.0.0.1:6379 myself - 0
1connected 0-1364
6bfd9b1f7e7bded1dae 127.0.0.1:6380 master -
d289c575dcbc4bddd 127.0.0.1:6381 master -
Cluster bus
每一个集群节点都有一个接收其它节点连接的端口,这个端口的大小是接收客户端连接的端口大小 + 10000,比如:接收client的端口为6379,那么这个端口的大小为16379.
节点和节点之间的通信无一例外都是通过Cluster Bus来进行的,通信的协议是一个二进制协议,它由不同类型和大小的frame组成。这个二进制协议没有公开文档,因为不想让其它外部软件和Redis Cluster 节点通过这个协议进行通信。但是我们可以通过源代码来了解这个协议。
Cluster拓扑
Redis Cluster是一个完全图,每一个节点都通过tcp和其它所有节点进行连接。在N个节点的集群中,每一个节点都拥有N-1个outgoingTCP连接,N-1个incoming TCP连接。
这些TCP连接始终是保持连接的,而不是按需创建;当一个节点期望它通过Cluster Bus发出的ping有一个pong的响应,如果等待了一段时间还没收到,则认为这个节点不可达;它将会尝试和这个节点进行重连。
虽然Redis Cluster是一个完全图,但是节点间使用gossip协议和一个配置更新机制,从而避免在一般情况下,在节点间交换太多的信息。所以交换的信息不是指数级的。
Node HandShake
节点通过clusterbus port来接收连接,当接收到ping时,使用这个端口进行响应,即使ping的节点不是被信任的。但是,如果发送报文的节点被认为不是集群的一部分,这个节点发送的其它报文都会被接收节点丢弃。
有两种方式,可以让一个节点来接受另外一个节点作为集群的一部分:
如果一个节点发送了MEET消息;一个meet消息和一个ping消息是类似的,但是会让接收者接收它为集群的一部分。节点只有在系统管理员使用以下命令发送请求后才会发送MEET消息:
CLUSTER MEET ip port
如果一个节点已经被信任,信任这个节点的节点将会通过gossip消息来通知其它节点,有个节点加入到了集群中;例如:A knows B,B knows C,最终B会发送gossip消息个A,告诉C加入了集群;然后A将会降入C,并尝试连接C
通过上面的方式,只有我们向任何已经在集群中的节点发送加入消息,最终会自动形成一个完全连接的图。也就是节点可以自动发现其它节点,但是只有在系统管理员发出了命令才行(也就是系统管理员信任这个节点)。
Redirecting and resharding(重定向和重分片)
Moved重定向
Redis client可以向集群中的任何节点发送查询请求,包括slave节点。节点分析这个请求,如果这个请求是可接受的(请求只有一个key,或多个key都在同一个hash slot上面),它就会查询那个节点负责这个hash slot。
如果这个hash slot是当前结点负责,则直接处理;否则,向客户端返回一个MOVED错误,比如:
-MOVED .0.1:6381
这个错误信息包括:hash slot(3999)以及处理这个slot的ip和端口;client会根据这个错误信息,重定向到给出的节点,但是这次请求也可能失败,比如:在请求过程中,集群配置发生了变化或者上次请求的节点信息没有及时得到更新
从集群节点来看,节点都是使用ID来标识的;但是对于client来说,只暴露hash slot和IP/port之间的map关系。
client一般都会对这个hash slot和ip/port之间的关系进行缓存;
另外一种刷新hash slot和ip/port之间关系的方式是在遇到MOVED消息之后使用CLUSTER NODE或CLUSTER SLOTS命令;如果碰到了一个MOVED消息,那就说明集群的配置发生了变化,应该不止一个hash slot发生了关系变化,而是很多个,所以全量刷新一遍是最好的策略
当集群是stable的时候,最终所有client都会获得hash slots -&ip/port之间的关系,使得集群变得高效,因为clients可以直接发送消息到正确的nodes上面
client还必须能够处理ASK重定向消息
Cluster在线重配置
Redis Cluster支持在Cluster正在运行的时候增加和移除节点。增加和移除节点被抽象成同一个操作,将hash slot从一个节点转移到另外一个节点。这个机制也可以用于rebalance the cluster,增加删除节点等等
向cluster增加一个节点:一个空节点增加到集群,有些hash slot需要从其它节点迁移到新加入的节点
从cluster删除一个节点:在这个节点上面的slot需要迁移到其它节点
rebalance cluster:一些给定的节点需要在节点间进行迁移。
所有实现的核心就是hash slot的迁移;实时上,hash slot就是一个key的集合;所以在Resharding的过程中,就是把key从一个节点迁移到另外一个节点;move一个hash slot,就是迁移所有这个hash slot上面的key。
和迁移相关的CLUSTER命令有:(用于操作一个Cluster节点上的slot迁移表)
CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]
CLUSTER DELSLOTS slot1 [slot2] ... [slotN]
CLUSTER SETSLOT slot NODE node
CLUSTER SETSLOT slot MIGRATING node
CLUSTER SETSLOT slot IMPORTING node
前面两个两个命令只是简单的从一个Redis节点上分配和移除slots。分配一个槽就是告诉指定的master,它将负责存储和服务于和给定槽的内容。在槽分配之后,它们会通过gossip协议,将这个配置信息在集群中进行传播。
ADDSLOTS命令一般用于在新建集群的时候为每一个master节点分配它需要处理的hashslot的子集。
DELSLOTS命令一般用于在调试的时候来手动修改cluster的配置,一般不会使用
SETSLOT命令将某个slot分配给某个节点;否则一个slot可以被设置为两个特殊状态:MIGRATING和IMPORTING,他们都是为了进行slot迁移而引入的。
当一个slot是MIGRATING状态的时候,如果key存在的话,节点会接受这个hash slot的所有请求;否则的话,这个请求会使用-ASK重定向转移到节点迁移的目的节点上。
当一个slot是IMPORTING状态的时候,节点会接受这个hash slot的所有请求,但是前提是这个请求执行发送过ASKING命令;如果之前没有发送过ASKING命令,那么这个请求使用MOVED重定向到真正hash slot拥有的节点上。
我们举个例子来说明slot的迁移:
假定我们有两个master节点A和B,我们需要将slot 8 从A迁移到B,因此我们使用下面的命令:
向B发送命令:CLUSTER SETSLOT 8IMPORTING A
向A发送命令:CLUSTER SETSLOT 8MIGRATING B
所有其它节点将会继续指示client对于slot 8的请求都转向机器A;因此,所有还存在的key会被A处理;所有已经迁移了得key会被B处理,因为A会重定向client到B上面。
使用这种方式,我们不再在A上创建新的同时一个名为redis-trib的程序在resharding过程中会被使用,ReidsCluster配置会将slot 8中存在的key从A迁移到B上面,这个过程使用下面的命令完成:
CLUSTER GETKEYSINSLOT slot count
上面的命令将会从slot中返回count个对于每一个返回的key,redis-trib给节点A发送一个MIGRATE命令,它将将这个key从A原子的迁移到B上(两个节点上都会锁住一段时间,所以不会产生竞争)。
MIGRATE target_host target_port keytarget_database id timeout
MIGRATE将会连接target实例,发送key序列化后的版本,一旦接收到了OK之后,它会从自己dedataset里面删除这个key。从外部client来看,在任何给定的时间内,一个key只会存在在A上或B上。
在Redis Cluster中,target_database是固定为0的,但是这个命令可以用于其它情况下,而不仅仅是Redis Cluster上面。MIGRATE并优化为尽量快的,即使我们移动的是一个复杂的数据结构,比如:很长的list。但是在Redis Cluster中,在有big key存在的情况下进行cluster的重配置,不是一个明智的选择,特别是对延迟有要求的情况下。
当迁移最终完成,命令SETSLOTNODE命令会发送给参与迁移的两个节点,以便将slot置为正常的状态;同样的这个命令也会向其它节点进行发送,以便尽快进行配置更新。
ASK命令是用来告诉client,只有下一个请求要发送给给定的节点;而MOVED是告诉client,后续和这个slot相关的所有请求都发送到给定的节点;这里的ASK是必须的,因为可能当前还有slot 8的key在A上面。所以对于slot 8 都需要先从A上面查询,然后到B上面;因为只有1个slot会这么做,所以整体性能还是可以接受的。
最基本的,ASKING命令在客户端设置one-time(一次)flag,以迫使节点处理处于IMPORTING状态的查询请求。
从client的角度来看,ASK重定向的完成场景如下:
如果接收到了ASK重定向,只发送当前请求到ASK指定的节点上面,但是其它请求还是发送到原来的节点上面
使用ASKING命令发送请求到ASK指定的节点上面
还不会将slot8到B插入到本地缓存里面
一但slot迁移完成,A将会发送MOVED消息,client将会始终发送消息到B上面。注意:如果一个有bug的客户端,过早的将slot8到B插入到本地缓存,这个没有问题,因为这个client不会在发送请求前发送ASKING命令,所以,B将会使用MOVED来重定向请求到A上面。
客户端第一次连接和重定向处理
client必须要在内部缓存slot和对应node的关系,否则这个client性能会比较低
虽然client需要缓存slot和对应node的关系,但是它不需要实时去更新这个缓存,因为如果发送query到了错误的节点,可以使用重定向来进行更新
client在两种情况下,需要获取一个完整的slot和node的对应关系表:
启动的时候
获取到了MOVED请求
client可以在获取到MOVED重定向请求的时候,只更新这个slot和node的对应关系,但是这么做,很没有效率,因为通常情况是多个slot会同时进行对应关系的修改(比如:一个slave提升为master,原master server的所有slot对应的node关系都发生了变化)。
为了获取slots和node的对应关系,Reids Cluster提供了一个可选的命令:CLUSTER NODES,这个命令不需要进行解析,只提供客户端需要的slots和node的对应关系信息。
另外还有一个CLUSTER SLOTS命令,提供了一个slots范围列表,和对应的master和slave node。
多个Key操作
可以使用hash tag,client可以使用多个key的操作,比如,下面的命令:
MSET {user:1000}.name Angela{user:1000}.surname White
多个key的操作在key对应slot正在进行迁移的时候,会变得暂时不可用。但是,即使当key对应slot在迁移,如果操作的这些key都在同一个节点上面,操作还是可以顺利执行。如果操作实在不可用,Redis Cluster会向客户端发送TRYAGAIN错误,client可以稍后再试,或向客户端返回一个错误提示。
使用slave节点扩展读
一般情况下,slave节点收到请求后,会将命令重定向到(使用MOVED)key对应slot的master节点上面。但是client可以使用READONLY命令,使用slave来扩展读。
READONLY告诉Redis Cluster的slave节点,请求只对读数据感兴趣,对写不感兴趣。
当创建了只读连接之后,当请求中的key不是slave对应master节点serve的slot,则会发挥重定向给client。当这种情况发生了,client也必须按照前面提到的方法来更新slot到node的映射关系。推荐这篇日记的豆列
&&&&&&&&&&&&查看: 26411|回复: 3
Redis集群指导
主题听众收听
1.Redis集群在数据一致性方面存在什么问题?
2.Redis集群正常工作至少需要几个个主节点?
3.什么是 Redis 实例?
欢迎加入about云群 、 ,云计算爱好者群,关注
主题听众收听
本帖最后由 nettman 于
14:43 编辑
本文依据官方教程()翻译并夹杂个人理解撰写而成,如有翻译或理解错误还请指正。
本文不涉及对分布式系统概念的复杂理解,而只是Redis集群的简单介绍,同时从用户的角度来描述Redis集群的如何部署,测试和使用。如果你打算运行一个更严格的Redis集群,强烈建议你阅读更正式的规范()。
Redis的集群运行方式是将数据自动分片至多个不同节点上,在此模式上用命令处理多个键值将不受支持,因为这将导致数据在不同节点间的移动,使得Redis不能提供原有的高性能和可预测性。但是集群在分区的过程中仍可以保持一定程度的可用性,也就是说在部分节点故障或无法连接时,集群仍可以正常运行。
Redis集群采用的不是一致性hash,而是将所有key值通过一定的运算放在一个叫做hash slot的容器里。而这个运算就是模除,每个集群拥有16384个hash slot,将key值模除16384即得出该key所在的hash slot。
集群中的每个节点都存储着hash slot的一个子集,例如你的一个集群有三个节点,则:节点A包含的hash slot 从0到5500;节点B包含的hash solt从5;节点C包含的hash solt 从1。这将使集群增加和删除节点变得很容易,比如我想新添一个节点D,我只需从A,B,C节点移动一些hash slot 到D节点即可,同样如果我想删除节点A,只要把A节点上的hash solt移交到B和C节点,当A节点的所有hash solt都移动完后我就可以把A节点彻底拿掉了。除了上面的两种操作还有更改节点hash solt的比例的操作,因为这些操作都是所谓的热操作,所以在进行这些操作时集群都是可以正常运行,无需任何宕机时间的。
为了保持集群的可用性,当某些节点宕机或者失去连接时,集群采用了主从模式,在这种模式中每个节点都实现了1对N的复制。还拿上面的例子来说明,如果B节点宕机,集群将不能正常服务,因为从之间的hash slot将不能访问。如果我们的集群是主从复制模式的,集群由节点A,B,C作为主节点而节点A1,B1,C1作为从节点,当节点B宕机时,由于节点B1复制于节点B,集群选择节点B1作为新的主节点来代替节点B,这样集群就可以继续正常运行了。尽管如此,如果节点B和节点B1同时宕机,集群将不能正常运行。
一致性保证
Redis 集群不能严格的保证数据的一致性,这就意味着在实际应用中它会漏写一些数据。异步复制可能是导致此结果的首要原因,下面我们举例说明集群在进行写操作时的三个步骤:1、客户端向节点B写入数据;2、节点B返回“成功”给客户端;3、节点B将此写入操作复制给它的从节点B1、B2和B3。
值得我们注意的是节点B在将该操作复制给它的从节点B1、B2、B3之前就将“成功”返回给了客户端。这将导致一个令人望而却步的潜在问题,就是如果节点B1没有能够成功的复制该写入操作,而节点B又在这时崩溃了,更恰巧的是节点B1又被升级为主节点B(此机制在主从模式中介绍过),这样最终的结果就是该写入操作在当前的节点B(原来的节点B1)中不存在,此次写入操作就彻底的被丢掉了。
与此非常类似的情况是传统数据库为了保持数据的一致性,通常都被配置为每秒将数据写入到硬盘一次,而不是在每次返回操作结果之前把数据写入到硬盘,不这样做的原因就是因为这将严重的影响数据库的性能。
注意:Redis集群将来会支持用户显式地使用同步写机制。
创建并使用集群
前面说了那么多的理论,其实不是我们关心的,下面正式开始说明怎么配置并使用Redis集群。要想创建一个集群首先要有多个运行在集群模式下的Redis实例才行,乍看好像是废话其实不是,集群模式的Redis实例是非传统模式的Redis,它需要配置为拥有特殊集群功能和可执行集群命令的实例。下面为一个最简单的集群配置:port 7000 #端口cluster-enabled yes #开启集群模式cluster-config-file nodes.conf #集群配置文件cluster-node-timeout 5000 #超时时间appendonly yes(code 1)其中需要注意的是cluster-config-file,改配置项指定一个名为nodes.conf的文件,不要试图手动修改该文件,它由集群在启动时自动生成并实时更新该文件。
Redis集群正常工作至少需要3个主节点,所以建议你在初次安装时配置3个主节点和三个从节点。下面正式开始配置步骤:
1、按照端口号建立6个与端口号同名的文件夹,命令如下:
mkdir cluster-testcd cluster-testmkdir 02 05(code 2)复制code 1中的内容,创建一个名为redis.conf文件,复制到6个文件夹中并修改port为相应的端口,。
2、下载源码(),编译
make PREFIX=/home/cluster-test/redis install,在redis目录下就会生成名为bin的目录,其中包含redis-benchmark、redis-check-aof、redis-check-dump、redis-cli、 redis-server5个可执行文件,复制这个5个文件分别至文件夹中。
3、在终端运行下面命令:
cd 7000./redis-server ./redis.conf
正如你看到的,在没有node.config文件的时候,系统会自动分配一个新的编号。
[22451] 31 Dec 14:09:07.507 * Max number of open files set to 10032[22451] 31 Dec 14:09:07.508 * No cluster configuration found, I'm e39dca9ae34ffcb93c3这个编号将用来标识这个redis实例,其他的节点也通过这个编号来识别这个Redis,而不是通过IP或者端口,IP和端口可能会变但是这个编号却永远不会变,所以我们称其为节点编号。
现在我们已经有了六个正在运行中的 Redis 实例, 接下来我们需要使用这些实例来创建集群, 并为每个节点编写配置文件。
通过使用 Redis 集群命令行工具 redis-trib , 编写节点配置文件的工作可以非常容易地完成: redis-trib 位于 Redis 源码的 src 文件夹中, 它是一个 Ruby 程序, 这个程序通过向实例发送特殊命令来完成创建新集群, 检查集群, 或者对集群进行重新分片(reshared)等工作。
我们需要执行以下命令来创建集群:
./redis-trib.rb create --replicas 1 127.0.0.1:.0.1:7001 \
127.0.0.1:.0.1:.0.1:.0.1:7005
命令的意义如下:
给定 redis-trib.rb 程序的命令是 create , 这表示我们希望创建一个新的集群。
选项 --replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
之后跟着的其他参数则是实例的地址列表, 我们希望程序使用这些地址所指示的实例来创建新集群。
简单来说, 以上命令的意思就是让 redis-trib 程序创建一个包含三个主节点和三个从节点的集群。
接着, redis-trib 会打印出一份预想中的配置给你看, 如果你觉得没问题的话, 就可以输入 yes , redis-trib 就会将这份配置应用到集群当中:&&& Creating cluster Connecting to node 127.0.0.1:7000: OK Connecting to node 127.0.0.1:7001: OK Connecting to node 127.0.0.1:7002: OK Connecting to node 127.0.0.1:7003: OK Connecting to node 127.0.0.1:7004: OK Connecting to node 127.0.0.1:7005: OK &&& Performing hash slots allocation on 6 nodes... Using 3 masters: 127.0.0.1:.0.1:.0.1:.0.1:7000 replica #1 is 127.0.0.1:.0.1:7001 replica #1 is 127.0.0.1:.0.1:7002 replica #1 is 127.0.0.1:7005 M: effa034c9 127.0.0.1:7000 slots:0- slots) master M: e68e52ceeb342f2fa083b 127.0.0.1:7001 slots: (5461 slots) master M: 393c6df5eb4b4cec323f0e4ca961c8b256e.0.1:7002 slots: (5462 slots) master S: 48b728dbcedff6bf990b7d1c35c3e0 127.0.0.1:7003 S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004 S: 3375be2ccc3234ffa87ee9fde973ff 127.0.0.1:7005 Can I set the above configuration? (type 'yes' to accept): yes 复制代码输入 yes 并按下回车确认之后, 集群就会将配置应用到各个节点, 并连接起(join)各个节点 —— 也即是, 让各个节点开始互相通讯:&&& Nodes configuration updated &&& Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join... &&& Performing Cluster Check (using node 127.0.0.1:7000) M: effa034c9 127.0.0.1:7000 slots:0- slots) master M: e68e52ceeb342f2fa083b 127.0.0.1:7001 slots: (5461 slots) master M: 393c6df5eb4b4cec323f0e4ca961c8b256e.0.1:7002 slots: (5462 slots) master M: 48b728dbcedff6bf990b7d1c35c3e0 127.0.0.1:7003 slots: (0 slots) master M: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004 slots: (0 slots) master M: 3375be2ccc3234ffa87ee9fde973ff 127.0.0.1:7005 slots: (0 slots) master [OK] All nodes agree about slots configuration.复制代码如果一切正常的话, redis-trib 将输出以下信息:&&& Check for open slots... &&& Check slots coverage... [OK] All 16384 slots covered. 复制代码这表示集群中的 16384 个槽都有至少一个主节点在处理, 集群运作正常。
集群的客户端
Redis 集群现阶段的一个问题是客户端实现很少。 以下是一些我知道的实现:
redis-rb-cluster 是我(@antirez)编写的 Ruby 实现, 用于作为其他实现的参考。 该实现是对 redis-rb 的一个简单包装, 高效地实现了与集群进行通讯所需的最少语义(semantic)。
redis-py-cluster 看上去是 redis-rb-cluster 的一个 Python 版本, 这个项目有一段时间没有更新了(最后一次提交是在六个月之前), 不过可以将这个项目用作学习集群的起点。
流行的 Predis 曾经对早期的 Redis 集群有过一定的支持, 但我不确定它对集群的支持是否完整, 也不清楚它是否和最新版本的 Redis 集群兼容 (因为新版的 Redis 集群将槽的数量从 4k 改为 16k 了)。
Redis unstable 分支中的 redis-cli 程序实现了非常基本的集群支持, 可以使用命令 redis-cli -c 来启动。
测试 Redis 集群比较简单的办法就是使用 redis-rb-cluster 或者 redis-cli , 接下来我们将使用 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
redis 127.0.0.1:7002& set hello world
-& Redirected to slot [866] located at 127.0.0.1:7000
redis 127.0.0.1:7000& get foo
-& Redirected to slot [12182] located at 127.0.0.1:7002
redis 127.0.0.1:7000& get hello
-& Redirected to slot [866] located at 127.0.0.1:7000
redis-cli 对集群的支持是非常基本的, 所以它总是依靠 Redis 集群节点来将它转向(redirect)至正确的节点。
一个真正的(serious)集群客户端应该做得比这更好: 它应该用缓存记录起哈希槽与节点地址之间的映射(map), 从而直接将命令发送到正确的节点上面。
这种映射只会在集群的配置出现某些修改时变化, 比如说, 在一次故障转移(failover)之后, 或者系统管理员通过添加节点或移除节点来修改了集群的布局(layout)之后, 诸如此类。
使用 redis-rb-cluster 编写一个示例应用
在展示如何使用集群进行故障转移、重新分片等操作之前, 我们需要创建一个示例应用, 了解一些与 Redis 集群客户端进行交互的基本方法。
在运行示例应用的过程中, 我们会尝试让节点进入失效状态, 又或者开始一次重新分片, 以此来观察 Redis 集群在真实世界运行时的表现, 并且为了让这个示例尽可能地有用, 我们会让这个应用向集群进行写操作。
本节将通过两个示例应用来展示 redis-rb-cluster 的基本用法, 以下是本节的第一个示例应用, 它是一个名为 example.rb 的文件, 包含在redis-rb-cluster 项目里面:
这个应用所做的工作非常简单: 它不断地以 foo&number& 为键, number 为值, 使用 SET 命令向数据库设置键值对。
如果我们执行这个应用的话, 应用将按顺序执行以下命令:
SET foo0 0
SET foo1 1
SET foo2 2
诸如此类。。。
代码中的每个集群操作都使用一个 begin 和 rescue 代码块(block)包裹着, 因为我们希望在代码出错时, 将错误打印到终端上面, 而不希望应用因为异常(exception)而退出。
代码的第七行是代码中第一个有趣的地方, 它创建了一个 Redis 集群对象, 其中创建对象所使用的参数及其意义如下:
第一个参数是记录了启动节点的 startup_nodes 列表, 列表中包含了两个集群节点的地址。
第二个参数指定了对于集群中的各个不同的节点, Redis 集群对象可以获得(take)的最大连接数 (maximum number of connections this object is allowed to take)。
第三个参数 timeout 指定了一个命令在执行多久之后, 才会被看作是执行失败。
记住, 启动列表中并不需要包含所有集群节点的地址, 但这些地址中至少要有一个是有效的(reachable): 一旦 redis-rb-cluster 成功连接上集群中的某个节点时, 集群节点列表就会被自动更新, 任何真正的(serious)的集群客户端都应该这样做。
现在, 程序创建的 Redis 集群对象实例被保存到 rc 变量里面, 我们可以将这个对象当作普通 Redis 对象实例来使用。
在十一至十九行, 我们先尝试阅读计数器中的值, 如果计数器不存在的话, 我们才将计数器初始化为 0 : 通过将计数值保存到 Redis 的计数器里面, 我们可以在示例重启之后, 仍然继续之前的执行过程, 而不必每次重启之后都从 foo0 开始重新设置键值对。
为了让程序在集群下线的情况下, 仍然不断地尝试读取计数器的值, 我们将读取操作包含在了一个 while 循环里面, 一般的应用程序并不需要如此小心。
二十一至三十行是程序的主循环, 这个循环负责设置键值对, 并在设置出错时打印错误信息。
程序在主循环的末尾添加了一个 sleep 调用, 让写操作的执行速度变慢, 帮助执行示例的人更容易看清程序的输出。
执行 example.rb 程序将产生以下输出:
ruby ./example.rb
------------------------------------------------------------------------------------------------------
这个程序并不是十分有趣, 稍后我们就会看到一个更有趣的集群应用示例, 不过在此之前, 让我们先使用这个示例来演示集群的重新分片操作。
对集群进行重新分片
现在, 让我们来试试对集群进行重新分片操作。
在执行重新分片的过程中, 请让你的 example.rb 程序处于运行状态, 这样你就会看到, 重新分片并不会对正在运行的集群程序产生任何影响, 你也可以考虑将 example.rb 中的 sleep 调用删掉, 从而让重新分片操作在近乎真实的写负载下执行。
重新分片操作基本上就是将某些节点上的哈希槽移动到另外一些节点上面, 和创建集群一样, 重新分片也可以使用 redis-trib 程序来执行。
执行以下命令可以开始一次重新分片操作:
$ ./redis-trib.rb reshard 127.0.0.1:7000
------------------------------------------------------------------------------------------------------
你只需要指定集群中其中一个节点的地址, redis-trib 就会自动找到集群中的其他节点。
目前 redis-trib 只能在管理员的协助下完成重新分片的工作, 要让 redis-trib 自动将哈希槽从一个节点移动到另一个节点, 目前来说还做不到 (不过实现这个功能并不难)。
执行 redis-trib 的第一步就是设定你打算移动的哈希槽的数量:
$ ./redis-trib.rb reshard 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7005: OK
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7003: OK
Connecting to node 127.0.0.1:7004: OK
&&& Performing Cluster Check (using node 127.0.0.1:7000)
M: effa034c9 127.0.0.1:7000
slots:0- slots) master
M: 393c6df5eb4b4cec323f0e4ca961c8b256e.0.1:7002
slots: (5462 slots) master
S: 3375be2ccc3234ffa87ee9fde973ff 127.0.0.1:7005
slots: (0 slots) slave
M: e68e52ceeb342f2fa083b 127.0.0.1:7001
slots: (5461 slots) master
S: 48b728dbcedff6bf990b7d1c35c3e0 127.0.0.1:7003
slots: (0 slots) slave
S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004
slots: (0 slots) slave
[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
------------------------------------------------------------------------------------------------------
我们将打算移动的槽数量设置为 1000 个, 如果 example.rb 程序一直运行着的话, 现在 1000 个槽里面应该有不少键了。
除了移动的哈希槽数量之外, redis-trib 还需要知道重新分片的目标(target node), 也即是, 负责接收这 1000 个哈希槽的节点。
指定目标需要使用节点的 ID , 而不是 IP 地址和端口。 比如说, 我们打算使用集群的第一个主节点来作为目标, 它的 IP 地址和端口是 127.0.0.1:7000 , 而节点 ID 则是 effa034c9 , 那么我们应该向 redis-trib 提供节点的 ID :
$ ./redis-trib.rb reshard 127.0.0.1:7000
What is the receiving node ID? effa034c9
redis-trib 会打印出集群中所有节点的 ID , 并且我们也可以通过执行以下命令来获得节点的运行 ID :
$ ./redis-cli -p 7000 cluster nodes | grep myself
effa034c9 :0 myself,master - 0 0 0 connected 0-5460
------------------------------------------------------------------------------------------------------
接着, redis-trib 会向你询问重新分片的源节点(source node), 也即是, 要从哪个节点中取出 1000 个哈希槽, 并将这些槽移动到目标节点上面。
如果我们不打算从特定的节点上取出指定数量的哈希槽, 那么可以向 redis-trib 输入 all , 这样的话, 集群中的所有主节点都会成为源节点, redis-trib 将从各个源节点中各取出一部分哈希槽, 凑够 1000 个, 然后移动到目标节点上面:
$ ./redis-trib.rb reshard 127.0.0.1:7000
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:all
---------------------------------------------------------------------------------------
输入 all 并按下回车之后, redis-trib 将打印出哈希槽的移动计划, 如果你觉得没问题的话, 就可以输入 yes 并再次按下回车:
$ ./redis-trib.rb reshard 127.0.0.1:7000
Moving slot 11421 from 393c6df5eb4b4cec323f0e4ca961c8b256e3460a
Moving slot 11422 from 393c6df5eb4b4cec323f0e4ca961c8b256e3460a
Moving slot 5461 from e68e52ceeb342f2fa083b
Moving slot 5469 from e68e52ceeb342f2fa083b
Moving slot 5959 from e68e52ceeb342f2fa083b
Do you want to proceed with the proposed reshard plan (yes/no)? yes
---------------------------------------------------------------------------------------
输入 yes 并使用按下回车之后, redis-trib 就会正式开始执行重新分片操作, 将指定的哈希槽从源节点一个个地移动到目标节点上面:
$ ./redis-trib.rb reshard 127.0.0.1:7000
Moving slot 5934 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5935 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5936 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5937 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5959 from 127.0.0.1:7001 to 127.0.0.1:7000:
------------------------------------------------------------------------------------------------------
在重新分片的过程中, example.rb 应该可以继续正常运行, 不会出现任何问题。
在重新分片操作执行完毕之后, 可以使用以下命令来检查集群是否正常:
$ ./redis-trib.rb check 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7005: OK
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7003: OK
Connecting to node 127.0.0.1:7004: OK
&&& Performing Cluster Check (using node 127.0.0.1:7000)
M: effa034c9 127.0.0.1:7000
slots:0--1 slots) master
M: 393c6df5eb4b4cec323f0e4ca961c8b256e.0.1:7002
slots: (4961 slots) master
S: 3375be2ccc3234ffa87ee9fde973ff 127.0.0.1:7005
slots: (0 slots) slave
M: e68e52ceeb342f2fa083b 127.0.0.1:7001
slots: (4962 slots) master
S: 48b728dbcedff6bf990b7d1c35c3e0 127.0.0.1:7003
slots: (0 slots) slave
S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004
slots: (0 slots) slave
[OK] All nodes agree about slots configuration.
&&& Check for open slots...
&&& Check slots coverage...
[OK] All 16384 slots covered.
------------------------------------------------------------------------------------------------------
根据检查结果显示, 集群运作正常。
需要注意的就是, 在三个主节点中, 节点 127.0.0.1:7000 包含了 6461 个哈希槽, 而节点 127.0.0.1:7001 和节点 127.0.0.1:7002 都只包含了 4961 个哈希槽, 因为后两者都将自己的 500 个哈希槽移动到了节点 127.0.0.1:7000 。
一个更有趣的示例应用
我们在前面使用的示例程序 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
------------------------------------------------------------------------------------------------------
那么 consistency-test.rb 将向我们报告不一致情况:
(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 命令。
因为这个示例程序具有一致性检查功能, 所以我们用它来测试 Redis 集群的故障转移操作。
故障转移测试
在执行本节操作的过程中, 请一直运行 consistency-test 程序。
要触发一次故障转移, 最简单的办法就是令集群中的某个主节点进入下线状态。
首先用以下命令列出集群中的所有主节点:
$ redis-cli -p 7000 cluster nodes | grep master
3e3a6cb0d9a9aa0b24026c0aae3f0 127.0.0.1:7001 master - 0 2 0 connected
def1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 2 0 connected
97a3ace4c8db myself,master - 0 0 0 connected 0--11422
------------------------------------------------------------------------------------------------------
通过命令输出, 我们知道端口号为 7000 、 7001 和 7002 的节点都是主节点, 然后我们可以通过向端口号为 7002 的主节点发送 DEBUG SEGFAULT 命令, 让这个主节点崩溃:
$ 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 几乎是同时执行将命令回复发送给客户端, 以及将命令复制给从节点这两个操作, 所以实际上造成命令丢失的时间窗口是非常小的。
不过, 尽管出现的几率不高, 但丢失命令的情况还是有可能会出现的, 所以我们对 Redis 集群不能提供强一致性的这一描述仍然是正确的。
现在, 让我们使用 cluster nodes 命令, 查看集群在执行故障转移操作之后, 主从节点的布局情况:
$ redis-cli -p 7000 cluster nodes
3fc07fdbefb.0.0.1:7004 slave 3e3a6cb0d9a9aa0b24026c0aae3f0 0 1 0 connected
a211e242fc6b22a9427fedfa04e08 127.0.0.1:7003 slave 97a3ace4c8db 3 0 connected
97a3ace4c8db myself,master - 0 0 0 connected 0--11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc.0.1:7005 master - 0 3 3 connected
3e3a6cb0d9a9aa0b24026c0aae3f0 127.0.0.1:7001 master - 0 5 0 connected
def1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc 3 connected
------------------------------------------------------------------------------------------------------
我重启了之前下线的 127.0.0.1:7002 节点, 该节点已经从原来的主节点变成了从节点, 而现在集群中的三个主节点分别是 127.0.0.1:7000 、 127.0.0.1:7001 和 127.0.0.1:7005 , 其中 127.0.0.1:7005 就是因为 127.0.0.1:7002 下线而变成主节点的。
cluster nodes 命令的输出有点儿复杂, 它的每一行都是由以下信息组成的:
节点 ID :例如 3fc07fdbefb36454d73 。
ip:port :节点的 IP 地址和端口号, 例如 127.0.0.1:7000 , 其中 :0 表示的是客户端当前连接的 IP 地址和端口号。
flags :节点的角色(例如 master 、 slave 、 myself )以及状态(例如 fail ,等等)。
如果节点是一个从节点的话, 那么跟在 flags 之后的将是主节点的节点 ID : 例如 127.0.0.1:7002 的主节点的节点 ID 就是 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 。
集群最近一次向节点发送 PING 命令之后, 过去了多长时间还没接到回复。
节点最近一次返回 PONG 回复的时间。
节点的配置纪元(configuration epoch):详细信息请参考 Redis 集群规范 。
本节点的网络连接情况:例如 connected 。
节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为 5960 至 10921 的哈希槽。
添加新节点到集群
根据新添加节点的种类, 我们需要用两种方法来将新节点添加到集群里面:
如果要添加的新节点是一个主节点, 那么我们需要创建一个空节点(empty node), 然后将某些哈希桶移动到这个空节点里面。
另一方面, 如果要添加的新节点是一个从节点, 那么我们需要将这个新节点设置为集群中某个节点的复制品(replica)。
本节将对以上两种情况进行介绍, 首先介绍主节点的添加方法, 然后再介绍从节点的添加方法。
无论添加的是那种节点, 第一步要做的总是添加一个空节点。
我们可以继续使用之前启动 127.0.0.1:7000 、 127.0.0.1:7001 等节点的方法, 创建一个端口号为 7006 的新节点, 使用的配置文件也和之前一样, 只是记得要将配置中的端口号改为 7000 。
以下是启动端口号为 7006 的新节点的详细步骤:
在终端里创建一个新的标签页。
进入 cluster-test 文件夹。
创建并进入 7006 文件夹。
将 redis.conf 文件复制到 7006 文件夹里面,然后将配置中的端口号选项改为 7006 。
使用命令 ../../redis-server redis.conf 启动节点。
如果一切正常, 那么节点应该会正确地启动。
接下来, 执行以下命令, 将这个新节点添加到集群里面:
./redis-trib.rb addnode 127.0.0.1:.0.1:7000
------------------------------------------------------------------------------------------------------
命令中的 addnode 表示我们要让 redis-trib 将一个节点添加到集群里面, addnode 之后跟着的是新节点的 IP 地址和端口号, 再之后跟着的是集群中任意一个已存在节点的 IP 地址和端口号, 这里我们使用的是 127.0.0.1:7000 。
通过 cluster nodes 命令, 我们可以确认新节点 127.0.0.1:7006 已经被添加到集群里面了:
redis 127.0.0.1:7006& cluster nodes
3e3a6cb0d9a9aa0b24026c0aae3f0 127.0.0.1:7001 master - 0 5 0 connected
3fc07fdbefb.0.0.1:7004 slave 3e3a6cb0d9a9aa0b24026c0aae3f0 0 3 0 connected
f093c80dde814da99c5cf72a7ddb :0 myself,master - 0 0 0 connected
def1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc 3 connected
a211e242fc6b22a9427fedfa04e08 127.0.0.1:7003 slave 97a3ace4c8db 5 0 connected
97a3ace4c8db.0.0.1:7000 master - 0 0 0 connected 0--11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc.0.1:7005 master - 0 8 3 connected
------------------------------------------------------------------------------------------------------
新节点现在已经连接上了集群, 成为集群的一份子, 并且可以对客户端的命令请求进行转向了, 但是和其他主节点相比, 新节点还有两点区别:
新节点没有包含任何数据, 因为它没有包含任何哈希桶。
尽管新节点没有包含任何哈希桶, 但它仍然是一个主节点, 所以在集群需要将某个从节点升级为新的主节点时, 这个新节点不会被选中。
接下来, 只要使用 redis-trib 程序, 将集群中的某些哈希桶移动到新节点里面, 新节点就会成为真正的主节点了。
因为使用 redis-trib 移动哈希桶的方法在前面已经介绍过, 所以这里就不再重复介绍了。
现在, 让我们来看看, 将一个新节点转变为某个主节点的复制品(也即是从节点)的方法。
举个例子, 如果我们打算让新节点成为 127.0.0.1:7005 的从节点, 那么我们只要用客户端连接上新节点, 然后执行以下命令就可以了:
redis 127.0.0.1:7006& cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
------------------------------------------------------------------------------------------------------
其中命令提供的 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 就是主节点 127.0.0.1:7005 的节点 ID 。
执行 cluster replicate 命令之后, 我们可以使用以下命令来确认 127.0.0.1:7006 已经成为了 ID 为 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 的节点的从节点:
$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
f093c80dde814da99c5cf72a7ddb 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc 3 connected
def1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc 3 connected
3c3a0c... 现在有两个从节点, 一个从节点的端口号为 7002 , 而另一个从节点的端口号为 7006 。
------------------------------------------------------------------------------------------------------
欢迎加入about云群 、 ,云计算爱好者群,关注
主题听众收听
注册会员, 积分 53, 距离下一级还需 147 积分
注册会员, 积分 53, 距离下一级还需 147 积分
非常感谢分享
主题听众收听
注册会员, 积分 118, 距离下一级还需 82 积分
注册会员, 积分 118, 距离下一级还需 82 积分
站长推荐 /4
会员注册不成功的原因
新手获取积分方法
hadoop3.0学习:零基础安装部署hadoop集群
about云课程:大数据日志实时分析
Powered by

我要回帖

更多关于 redis集群最少3个节点 的文章

 

随机推荐