一、分片集群配置
分片集群的原理,只不过是将多个复制集联合起来,每个复制集具有一个主实例和多个从实例。并且每个复制集朱保存一部分数据库中的键值对,解决了主从复制集中总数据存储量收最小实例的限制而形成木桶效应。redis的分片集群可以在数据量不断增大的情况下进行水平扩容,将键值放在指定的实例中。分片集群的结构图如下。
所有的节点都具有监控功能,在出现某个节点宕掉,其他节点会通过(PING-PONG命令)感知,并选举出新的主节点。
1、手动配置集群
1、创建多个实例这里主要使用一下IP和端口:
192.168.121.12:6379
192.168.121.12:6380
192.168.121.12:6381
192.168.121.14:6379
192.168.121.14:6380
192.168.121.14:6381
其中192.168.121.12:6379、192.168.121.12:6380、192.168.121.14:6379为集群的主节点,192.168.121.12:6381、192.168.121.14:6380、192.168.121.14:6381为从节点。
2、cluster配置文件
cluster-enabled yes //开启redis的集群模式
cluster-config-file nodes-6379.conf //该文件会持久化的将集群中的状态持久化的存储到该配置文件中
cluster-node-timeout 5000 //节点间超时互联的阈值。
3、通过启动指令将六个redis实例全部启动。
4、节点点握手
在任一节点上对其他五个实例实现握手。本实践在192.168.121.12:6379上进行握手。
192.168.121.12:6379> cluster meet 192.168.121.12 6380
192.168.121.12:6379> cluster meet 192.168.121.12 6381
192.168.121.12:6379> cluster meet 192.168.121.14 6379
192.168.121.12:6379> cluster meet 192.168.121.14 6380
192.168.121.12:6379> cluster meet 192.168.121.14 6381
握手的目的是让各个实例之间能够互相了解,知道其他实例的基本信息,进行信息互通。
4、插槽的分配
所有的键值只能分布在插槽上进行存储,一个集群只能有16384个插槽,为了能够实现分片集群,让所有的键值分布在整个集群中,需要将16384个插槽均匀的分布到主数据库上。实现读写均衡,减轻服务器压力。每个实例都有自己的唯一标识ID,插槽需要通过数据库ID进行分配。在这里需要两步走,设置主节点和从节点。
设置主节点:
现在有三个主节点,需要将插槽分配给主节点,分布情况为{0----5461}、{5462-----10922}、{10923----16383}。插槽的设置主节点的插槽命令:cluster addslots {slotn..slotm}。在各个主节点上的执行步骤如下:
192.168.121.12:6379> cluster addslots {0..5461}
192.168.121.12:6380> cluster addslots {5462..10922}
192.168.121.14:6379> cluster addslots {10923..16383}
查看实例信息的指令为cluster nodes。
上图中的前半部分为每个实例的ID,中间部分为实例的ip与port。现在需要设置主节点与从节点。
设置从节点:
通过上图可以查到各个节点的ID,现在需要通过主节点的ID来设置从节点,命令为cluster replicate NODEID。这一步需要在从数据库上操作。步骤如下:
192.168.121.12:6381> cluster replicate ad708e25056e7792e95cb11e67bdfdbad1f7d539
192.168.121.14:6380> cluster replicate df8c6d499e3eb6d985d200029124b49f3237e623
192.168.121.14:6381> cluster replicate 087510c60f9647b8c24e1ce9c9d9f70ad16ce1e8
查看实例信息的指令为cluster nodes
可以看到在第三部分有slave和master,这两个标识为从节点和主节点。从节点的后面的编码为主节点的ID,主节点的最后的数字区间为插槽的分配情况。
5、读写分离
开启与 Redis Cluster 从节点连接的读请求通常,从节点将重定向客户端到认证过的主节点,以获取在指定命令中所涉及的哈希槽,然而客户端能通过READONLY
命令将从节点设置为只读模式。
READONLY
告诉 Redis Cluster 从节点客户端愿意读取可能过时的数据并且对写请求不感兴趣。
2、增加新节点
在添加新节点时,如果添加的是主节点则需要两步走,即节点握手和分配插槽。如果是添加从节点,也需要两步走,即节点握手和复制主节点的ID。
添加主节点:
按照节点配置步骤,将新节点的配置文件设置为集群模式。然后在集群的任一节点使用cluster meet ip port命令与新节点进行握手。
需要将插槽分配给新节点,此时也会分为多种情况,1、插槽未分配过。2、插槽已经分配过。
添加的节点是从节点:
首先也是进行握手操作。
其次是复制主节点的ID cluster replicate NODEID
3、分配插槽
1、插槽未分配过,如同在搭建分片集群时,为主节点分配插槽的命令是一样的。cluster addslots slot1[slot2....slotn]
2、插槽已经分配过,需要将键值都要迁移过去,主要分两步走:迁移插槽和迁移键值。
迁移插槽 cluster setslot slot node 新运行节点的nodeid
查询出该插槽的键数 cluster countkeysinslot <slot>
返回该插槽上的键 cluster getkeysinslot <slot> <count>
迁移插槽上的全部键值 migrate 目标ip 目标port 键名 数据库号码 超时时间 [COPY] [REPLACE]
附加:在键的迁移过程中会遇到两种情况:1、在键迁移前客户端会请求新节点,但是此时键值没有迁移,无法读取到键值。2、在键值迁移后,客户端可能请求旧节点,此时也会造成数据丢失。遇到这两种情况可以使用如下步骤进行。
(1)cluster setslot <slot> importing <node_id> :从 node_id 指定的节点中导入槽 slot 到本节点。
(2)cluster setslot <slot> migrating <node_id> :将本节点的槽 slot 迁移到 node_id 指定的节点中。
(3)cluster setslot <slot> node <node_id>:将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给
(4)cmigrate <ip> <port> <key> <dbnumber> timeout [COPY] [REPLACE]
(5)cluster setslot <slot> node <node_id>
运行机制:在这里将节点A认为被迁移插槽的新节点,节点B为插槽的旧节点。在客户端在请求节点A时,如果键值在节点A,则直接获取;如果键值不在节点A,节点A会发送一个ASK跳转请求,告诉客户端在节点B上。此时客户端会发送一个ASKING命令,接着发送获取键值的命令,如果节点B收到过ASKING命令,就会返回该键值;如果没有收到过ASKING命令,就会返回MOVE命令,重定位到节点A。
3、常用命令
集群
cluster info :打印集群的信息
cluster nodes :列出集群当前已知的所有节点( node),以及这些节点的相关信息。
节点
cluster meet <ip> <port> :将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
cluster forget <node_id> :从集群中移除 node_id 指定的节点。
cluster replicate <node_id> :将当前节点设置为 node_id 指定的节点的从节点。
cluster saveconfig :将节点的配置文件保存到硬盘里面。
槽(slot)
cluster addslots <slot> [slot ...] :将一个或多个槽( slot)指派( assign)给当前节点。
cluster delslots <slot> [slot ...] :移除一个或多个槽对当前节点的指派。
cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
cluster setslot <slot> node <node_id> :将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。
cluster setslot <slot> migrating <node_id> :将本节点的槽 slot 迁移到 node_id 指定的节点中。
cluster setslot <slot> importing <node_id> :从 node_id 指定的节点中导入槽 slot 到本节点。
cluster setslot <slot> stable :取消对槽 slot 的导入( import)或者迁移( migrate)。
键
cluster keyslot <key> :计算键 key 应该被放置在哪个槽上。
cluster countkeysinslot <slot> :返回槽 slot 目前包含的键值对数量。
cluster getkeysinslot <slot> <count> :返回 count 个 slot 槽中的键
4、对节点和插槽操作的节本步骤总结
在添加节点时,先握手,后移动键值,最后移动插槽
添加从节点时,先握手,后复制主节点ID
删除主节点时,先移动键值,再移动插槽,后删除节点
删除从节点,直接删除即可。
5、故障恢复
1、实时监控
所有的节点可以被认为是哨兵,每个节点会每1秒随机选取5个节点作为监控对象。并从这5个对象中选择一个最久没有响应的节点发送PING命令,如果节点A没有收到节点B的响应,就会认为节点B主观下线,同时广播给其他节点,其他节点会记录下节点下线信息。并且出节点A之外的其他节点也会对节点B进行监控操作,如果节点C收到半数以上节点认为节点B下线,就会标记为客观下线。
2、选举主节点
如果某一个从节点发现主节点已经客观下线,就会向其他节点发送请求,要求对方选举自己为主节点。
如果对方没有选举过其他节点,则统一该从节点为主节点。
只要超过半数的节点统一该从节点为主节点,那么就会成功的选举为主节点。
如果选举不成功,会等待一个随机时间,重现进行选举。
二、redis的设计原理
1、集群基本运行机制
1、写安全
通常存在一个时间窗口,可能在分片中丢失写入数据。 但是一个连接到绝大部分master节点的客户端的时间窗口,与一个连接到极小部分master节点的客户端的时间窗口 有很大的区别。
(1)主从同步时
在客户端将数据写入master节点时,虽然写入master节点成功,但是在同步slave节点时,master节点可能会发生宕机事故,一旦宕机,就会丢失部分无法同步到slave节点的数据。
(2)主从切换
在master发生宕机之后,slave节点会升级为master节点,一段时间之后,master节点恢复工作。但是在恢复工作之前,每个客户端会保存一份过期的路由表,在这段时间内客户向
宕机的 master节点发送数据,会将写入的数据丢。
(3)检测到master节点宕机的time_out时间内
在当master节点在宕机之后,如果在在time_out时间内没有发送求求进行master节点监控,而是在time_out的时间点进行监控,此时所有客户端对该master节点的数据操作都会导致
数据丢失,只有在time_out时间段后才不会出现数据丢失。
2、可用性
在有多个master节点的集群中如果少数master节点出现数据故障,如果该部分节点有至少一个slave节点,那么在(time_out+N)秒后,该部分master节点的其中一个slave会升级为master。从
而保证集群的可用。例如:有一个集群有N个master节点,每个master节点有M个slave节点,此时如果其中一个master节点宕掉后,可用的概率为(1-1/(N*M-1))。如果某个master节点只剩一个
slave节点时,出现集群不可用的概率为(1/((N-1)*M+1))。由于在出现故障时,导致部分节点会出现孤立的master节点,redis集群会将其他master节点的slave节点副本迁移,变为孤立master节
点的从节点,从而达到集群的均衡。
3、性能
在集群中客户端在发送请求到服务端,服务端如果负责该部分键,将会把键的值传递到客户端,如果不在该服务端,会发送重定向MOVE命令,提示客户端向指定的服务端操作数据。客户端
也会更新自己的路由表为最新数据。所以客户端对服务集群的操作是透明的。
4、键分布模型
键的空间会分配到16384个插槽中,所以在分配集群时,能够将插槽分配的最大master节点数是16384个,但是建议在建立最大节点数时,以1000个为标准。键的分配是通过CRC-16算法计算
出所在的插槽,并将键值合理的存储到插槽。分配插槽的公式为:slot = CRC16(key)%16384。
5、键哈希标签
为了在一个插槽上实现多键操作,可以通过哈希标签实现,如果某一个键名出现{}这样的符号,那么计算哈希槽的字符串为{}内的内容。字符串以第一个出现{}为准。
例如user1000}.following 和 {user1000}.followers这样的键会分配到同一个插槽中。
6、集群节点属性
在集群中中每个节点都有自己唯一的ID作为标识,并且该表示在全局是唯一。该ID会保存到node.conf文件中,只有将该文件被删除或者通过cluster reset命令进行重新设置才会将ID重新生成。以
下内容为节点信息:
$ redis-cli cluster nodes d1861060fe6a534d42d8a19aeb36600e18785e04 127.0.0.1:6379 myself - 0 1318428930 1 connected 0-1364 3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729 d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095
以上列表信息中每行保存的内容为:节点ID、节点IP、节点端口、节点角色、节点最后一次发送ping的时间、最后一次发送pong命令的时间、配额、连接状态和分配的插槽。
7、集群总线与网络拓扑
集群之间的通信时通过集群总线和集群协议进行,他们的使用端口为每个实例端口号加上10000。每个节点都会拥有一个这样的端口作为接受来自其他节点信息的端口,而每个节点都有N-1个 对内
节点和N-1个对内节点进行信息通信。
8、节点握手
在集群中所有的节点是通过集群端口和总线进行握手的,握手是使用的meet命令进行介绍自己,这和ping消息的原理是一样的。通过meet命令可以让对方接受自己并成为该集群的一部分,由于在
集群内部几点可以通过广播的方式将该节点所了解的集群信息发送给其他节点,也就是说只要集群中的某一个节点知道有一个新节点加入集群,其他节点也会知道。所以在执行握手操作时,只需在一个
实例上执行即可。
9、MOVED重定向
在客户端发送数据请求到节点,如果节点有该键所分配的插槽,则会简单的执行该操作。如果该节点没有该插槽,节点会通过保存在本地的插槽---节点映射表,查找该键所在的插槽和节点,并在回
复客户端时附加该信息,客户端收到该信息需要进行两步操作:1、向指定的节点发送上次的数据请求,如果存在,则返回数据,如果不存在,通过返回的节点信息向该节点进行请求,知道获取到数据
为止。2、为了能够高效的执行数据操作,客户端会记录插槽与节点的映射关系,由于每次的MOVED返回信息中都有插槽的变更,所以客户端会对插槽的信息进行变更。以此获取最新的映射路由。
10、集群在线重新配置
在线重置主要是进行一下操作:1、节点的添加,2、节点的删除,3、重新均衡。在进行以上操作的时候,都会通过goosip协议向整个集群广播该消息,让整个集群对自己的路由列表进行更新,保证
集群信息的一致性。以下子命令实现重新配置:
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
11、ASK 重定向
ASK与MOVED相似,都会进行请求的重定向。MOVED的重定向是将请求永久的指向新的节点,并且在获取MOVED信息之后会更新客户端的路由。而ASK只是临时性的指向新的节点,该键可能会
在一段时间之后,回到返回ASK的节点,所以在收到ASK信息之后,无需更新路由列表。
ASK实现重定向的步骤:
首先在收到ASK信息之后,客户端会对原有的节点发送ASKING请求。
在发送ASKING命令之后,接着发送数据操作请求。如果原有节点没有收到ASKING请求,则不会处理数据操作,如果收到ASKING请求,则会处理该数据操作。
如果数据没有在原有节点,则原有节点会发送MOVED响应。
12、客户端首次连接和处理重定向
在节点启动时,会初始化集群的路由信息
在收到MOVED信息后节点也会更新为最近的路由信息
14、通过slave节点水平扩展读
客户端在发送请求到slave节点时,一般情况下,slave将请求会moved到指定的master,但是我们也可以通过readonly指令指定slave为只读模式,实现读能力的扩展。
2、容错性
1、节点心跳
在每秒内节点都会发送ping指令到其他某个节点,同时也会收到其他节点的pong信息。所有的节点信息都会保存到ping\pong命令中。在ping\pong命令中有两部分:请求头和gossip字段。
请求头:
节点ID
configEpoch和currentEpoch
节点标识
节点负责的哈希槽
主节点的ID
集群状态
发送端的集群端口号
gossip字段,该字段会保存集群中的部分节点的信息:
节点ID
节点IP和端口号
节点的运行状态 //PFAIL和FAIL
2、失效检测
在心跳检测时发送的ping和pong命令会在gossip字段中包含该标识,该标识有两个标识组成,PFAIL和FAIL,PFAIL表示可能失效,FAIL表示确实失效。
PFAIL:
在节点A发送ping指令到节点B后,节点A在一半的time_out时间内没有收到来自节点B的pong回复,节点A会立刻重新发送请求到节点B,如果在从第一次发送ping命令的time_out时间
内没有收到回复,就会将该节点在本地的路由表中将该节点标识为PFAIL。
FAIL:
由于节点心跳的原理,每个节点会收到包含其他节点的列表信息,当收到其他节点的gossip片段的节点B的信息为PFAIL的节点数超过一半,就会认为节点B为FAIL,将更新本地节点的路
由信息。将节点B标识为FAIL的节点会向集群中的其他节点发送节点B已经为FAIL,强制其他节点将节点B标识为FAIL。
3、配置处理,传播,和失效转移
一旦出现节点失效就要通过故障转移实现集群的有效运行。
currentEpoch:
在节点刚刚加入的时候,currentEpoch的值为0,只有在心跳检测的时候会接收到其他节点的epoch,如果currentepoch的值小于其他节点的epoch,则会将currentepoch的值改为最大
的epoch,最大的currentepoch值代表该节点的路由信息为最新信息。
configEpoch
该参数主要用于保存master的configepoch,slave会通过心跳检测获取master的configepoch,slave会实时更新configepoch为最新获取的master的configepoch。同时其他节点收到slave
的configeoch进行判断是否需要更新路由信息。
slave排名
slave发起投票请求的顺序是通过一个排名进行的,该排名是按照slave在同步master的数据操作指令的偏移量来排序。排名越靠前的,执行发起投票请求的延迟越短,延迟时间的计算公式是:
DELAY = 500 milliseconds + 0-500随机数 milliseconds + SLAVE_RANK * 1000 milliseconds.
发送选举请求和slave升级
在slave发送投票之前会增加configepoch的值,并向其他的master节点发送投票请求,master节点在没有对其他slave节点投票,就会对该节点进行投票,该slave节点收到回复的最大延迟时
间是time_out。并且其他master节点在发送投票之后的time_out*2的时间内不能对其他节点进行投票,这样也避免了对此投票选出多个master的问题。slave节点每收到一次投票就会将自己
的configepoch进行增大,slave节点对小于自己epoch的ACK就会丢弃,防止投票重复计数。一旦一个slave升级为master,该节点的configepoch为最大值,同时向集群的全部节点发送信息,进行
路由更新。
4、master节点回复slave节点的投票请求
master在进行投票前需要验证是否需要对slave进行投票,进行验证的三个条件:
1、该slave的master已经标记为FAIL.
2、master的lastVoteEpoch要小于请求中的currentepoch,这样就会保证了重复投票。
3、master的currentepoch要小于请求里的currentEpoch,这样就会保证该请求的slave的基本信息是最新。
5、哈希槽信息的传播
插槽信息的传播主要有连部分组成:
心跳包:
在心跳包中会保存一份部分节点的基本信息,接收请求包的节点会与本地信息进行比对,判断是否更新。
update信息:
如果接收到的心跳包中的configepoch小于本节点的configepoch,则会发送一个update的返回数据,对发送心跳包的节点信息进行更新。
6、节点加入并分配插槽
在有新节点加入集群,并将节点A的插槽全部分配给新节点,那么新节点将会成为节点A的master节点,节点A的slave节点也会成为新master节点的slave。但是并不是将节点A的所有插槽分配给
新节点,可能分配给其他的多个master,只有接收节点A的最后一个插槽的节点会成为节点A的master节点。
7、备份迁移和备份迁移算法
为了能够保证集群的正常工作,并且能够有效的利用服务器,故障转移具有以下策略:如果节点A有一个slave节点A1,节点B有一个slave节点B1,节点C有两个slave节点C1和C2.那么节点A或节点
B入股出现故障,slave节点A1或节点B1会升级为master节点,同时节点C的slave节点C1或C2中的一个转换为A1或者B1的从节点,从而保障集群的可用于故障转移。
执行此操作的配置参数是:cluster-migration-barrier,一个master节点在拥有多少个健康slave节点的时候就要割让一个slave节点出来。例如这个参数设为 2,那么只有当一个master节点拥有 2 个可
工作的slave节点时,它的一个slave节点会尝试迁移。
算法:
当检测出一个master节点为孤立节点,就会触发此算法,并找出某些master节点下应有最多slave节点并且这些slave节点均不为FAIL状态,如果这样的master节点有多个,可以从这些master节点
下找出各自ID最小的slave节点,如果这些slave节点均认为是ID最小的节点,则就会全部迁移到孤立master节点下成为slave节点。结果会导致其他原有的master节点下无slave节点,同时也会再次触发
此算法据需执行,直到集群均衡为止。
8、节点重置
在将集群重新配置或者将节点加入其他集群时,需要对节点进行重置,从而成为新的未加入集群的节点,重置分为软重置和硬重置。
命令如下:
cluster reset soft
slave节点会成为master节点,所有的数据都会丢失,插槽全部丢弃,节点列表中的其他节点全部移除
master节点如果没有数据则和slave节点的重置相同,如果有数据就不会执行重置操作,知道数据全部删除。
cluster reset hard
slave节点会成为master节点,所有的数据都会丢失,插槽全部丢弃,节点列表中的其他节点全部移除,重新分配节点ID,currentepoch、configepoch归为0。
master节点如果没有数据则和slave节点的重置相同,如果有数据就不会执行重置操作,知道数据全部删除。
10、集群删除节点
在将节点从集群删除时,需要先将数据迁移到其他节点,然后执行CLUSTER FORGET <node-id> 命令来实现,该命令的会执行两件事:
1、从指定节点的节点列表中删除节点。
2、在60s内组织该节点同步其他节点,防止节点间的心跳检测更新该节点的节点列表,将应删除的节点重新添加上。