redis支持RDB和AOF两种持久化机制,持久化可以避免因进程退出而造成数据丢失;
5.1RDB持久化把当前进程数据生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发
手动触发有save和bgsave两命令
save命令:阻塞当前Redis,直到RDB持久化过程完成为止,若内存实例比较大会造成长时间阻塞,线上环境不建议用它
bgsave命令:redis进程执行fork操作创建子线程,由子线程完成持久化,阻塞时间很短(微秒级),是save的优化,在执行redis-cli shutdown关闭redis服务时,如果没有开启AOF持久化,自动执行bgsave;
显然bgsave是对save的优化。
bgsave运行流程
5.2RDB文件的操作
命令:config set dir /usr/local //设置rdb文件保存路径
备份:bgsave //将dump.rdb保存到usr/local下
恢复:将dump.rdb放到redis安装目录与redis.conf同级目录,重启redis即可
优点:1,压缩后的二进制文,适用于备份、全量复制,用于灾难恢复
2,加载RDB恢复数据远快于AOF方式
缺点:1,无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
2,保存后的二进制文件,存在老版本不兼容新版本rdb文件的问题
5.3AOF持久化
针对RDB不适合实时持久化,redis提供了AOF持久化方式来解决
开启:redis.conf设置:appendonly yes (默认不开启,为no)
默认文件名:appendfilename "appendonly.aof"
流程说明:1,所有的写入命令(set hset)会append追加到aof_buf缓冲区中
2,AOF缓冲区向硬盘做sync同步
3,随着AOF文件越来越大,需定期对AOF文件rewrite重写,达到压缩
4,当redis服务重启,可load加载AOF文件进行恢复
AOF持久化流程:命令写入(append),文件同步(sync),文件重写(rewrite),重启加载(load)
redis的AOF配置详解:
appendonly yes //启用aof持久化方式
# appendfsync always //每收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒强制写入磁盘一次,性能和持久化方面做了折中,推荐
# appendfsync no //完全依赖os,性能最好,持久化没保证(操作系统自身的同步)
no-appendfsync-on-rewrite yes //正在导出rdb快照的过程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 //aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-min-size 64mb //aof文件,至少超过64M时,重写
如何从AOF恢复?
1. 设置appendonly yes;
2. 将appendonly.aof放到dir参数指定的目录;
3. 启动Redis,Redis会自动加载appendonly.aof文件。
redis重启时恢复加载AOF与RDB顺序及流程:
1,当AOF和RDB文件同时存在时,优先加载
2,若关闭了AOF,加载RDB文件
3,加载AOF/RDB成功,redis重启成功
4,AOF/RDB存在错误,redis启动失败并打印错误信息
a,方式一、新增redis6380.conf, 加入 slaveof 192.168.42.111 6379, 在6379启动完后再启6380,完成配置;
b,方式二、redis-server --slaveof 192.168.42.111 6379
c,查看状态:info replication
d,断开主从复制:在slave节点,执行6380:>slaveof no one
e,断开后再变成主从复制:6380:> slaveof 192.168.42.111 6379
f,数据较重要的节点,主从复制时使用密码验证: requirepass
e,从节点建议用只读模式slave-read-only=yes, 若从节点修改数据,主从数据不一致
h,传输延迟:主从一般部署在不同机器上,复制时存在网络延时问题,redis提供repl-disable-tcp-nodelay参数决定是否关闭TCP_NODELAY,默认为关闭
参数关闭时:无论大小都会及时发布到从节点,占带宽,适用于主从网络好的场景,
参数启用时:主节点合并所有数据成TCP包节省带宽,默认为40毫秒发一次,取决于内核,主从的同步延迟40毫秒,适用于网络环境复杂或带宽紧张,如跨机房
2,主从拓扑:支持单层或多层
A,一主一从:用于主节点故障转移从节点,当主节点的“写”命令并发高且需要持久化,可以只在从节点开启AOF(主节点不需要),这样即保证了数据的安全性,也避免持久化对主节点的影响
B, 一主多从:针对“读”较多的场景,“读”由多个从节点来分担,但节点越多,主节点同步到多节点的次数也越多,影响带宽,也加重主节点的稳定
C,树状主从:一主多从的缺点(主节点推送次数多压力大)可用些方案解决,
主节点只推送一次数据到从节点1,再由从节点2推送到11,减轻主节点推送的压力。
执行slave master port后,
与主节点连接,同步主节点的数据,6380:>info replication:查看主从及同步信息
master:6379 |
(配置完slave of 127.0.0.1 6379) slave 6380启动 |
1,保存主节点信息 2,主从建立socket连接 3,发送ping命令 4,权限验证 5,同步数据集 6,命令持续复制 |
redis 2.8版本以上使用psync命令完成同步,过程分“全量”与“部分”复制
全量复制:一般用于初次复制场景(第一次建立SLAVE后全量)
部分复制:网络出现问题,从节占再次连主时,主节点补发缺少的数据,每次 数据增加同步
心跳:主从有长连接心跳,主节点默认每10S向从节点发ping命令,repl-ping-slave-period控制发送频率
1,为什么要讲哨兵机制?
A,我们学习了redis的主从复制,但如果说主节点出现问题不能提供服务,需要人工重新把从节点设为主节点,还要通知我们的应用程序更新了主节点的地址,这种处理方式不是科学的,耗时费事
B,同时主节点的写能力是单机的,能力能限
C,而且主节点是单机的,存储能力也有限
其中2,3的问题在后面redis集群课会讲,第1个问题我们用哨兵机制来解决
2,主从故障如何故障转移(不满足高可用):
A,主节点(master)故障,从节点slave-1端执行 slaveof no one后变成新主节点
B,其它的节点成为新主节点的从节点,并从新节点复制数据
3,哨兵机制(sentinel)的高可用:
A,原理:当主节点出现故障时,由redis sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。
主从复制与redis sentinel拓扑结构图
其实整个过程只需要一个哨兵节点来完成,首先使用Raft算法(感兴趣的同学可以查一下,其实就是个选举算法)实现选举机制,选出一个哨兵节点来完成转移和通知
哨兵有三个定时监控任务完成对各节点的发现和监控:
任务1,每个哨兵节点每10秒会向主节点和从节点发送info命令获取最拓扑结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时可以马上感知到
任务2,每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscribe来完成的;
任务3,每隔1秒每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据
主观下线和客观下线:
主观下线:刚我知道知道哨兵节点每隔1秒对主节点和从节点、其它哨兵节点发送ping做心跳检测,当这些心跳检测时间超过down-after-milliseconds时,哨兵节点则认为该节点错误或下线,这叫主观下线;这可能会存在错误的判断。
客观下线:当主观下线的节点是主节点时,此时该哨兵3节点会通过指令sentinel is-masterdown-by-addr寻求其它哨兵节点对主节点的判断,当超过quorum(法定人数)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线
领导者哨兵选举流程:
a,每个在线的哨兵节点都可以成为领导者,当它确认(比如哨兵3)主节点下线时,会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
b,当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;
c,如果哨兵3发现自己在选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举…………
4,故障转移机制
A,由Sentinel节点定期监控发现主节点是否出现了故障
sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了
流程: 1,将slave-1脱离原从节点,升级主节点,
2,将从节点slave-2指向新的主节点
3,通知客户端主节点已更换
4,将原主节点(oldMaster)变成从节点,指向新的主节点
D,故障转移后的redis sentinel的拓扑结构图
5,哨兵机制-故障转移详细流程
A,过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点
B,选择salve-priority从节点优先级最高(redis.conf)
C,选择复制偏移量最大,指复制最完整的从节点
5,如何安装和部署Reids Sentinel?
我们以3个Sentinel节点、2个从节点、1个主节点为例进行安装部署
1,前提:先搭好一主两从redis的主从复制,和之前复制搭建一样,搭建方式如下:
A主节点6379节点(/usr/local/bin/conf/redis6379.conf):
修改 requirepass 12345678,注释掉#bind 127.0.0.1
B从节点redis6380.conf和redis6381.conf:
修改 requirepass 12345678 ,注释掉#bind 127.0.0.1,
加上masterauth 12345678 ,加上slaveof 127.0.0.1 6379
注意:当主从起来后,主节点可读写,从节点只可读不可写
2,redis sentinel哨兵机制核心配置(也是3个节点):
/usr/local/bin/conf/sentinel_26379.conf
/usr/local/bin/conf/sentinel_26380.conf
/usr/local/bin/conf/sentinel_26381.conf
将三个文件的端口改成: 26379 26380 26381
然后:sentinel monitor mymaster 190.168.1.111 6379 2 //监听主节点6379
sentinel auth-pass mymaster 12345678 //连接主节点时的密码
三个配置除端口外,其它一样。
3,哨兵其它的配置:只要修改每个sentinel.conf的这段配置即可:
sentinel monitor mymaster 192.168.1.10 6379 2
//监控主节点的IP地址端口,sentinel监控的master的名字叫做mymaster
2代表,当集群中有2个sentinel认为master死了时,才能真正认为该master已经不可用了
sentinel auth-pass mymaster 12345678 //sentinel连主节点的密码
sentinel config-epoch mymaster 2 //故障转移时最多可以有2从节点同时对新主节点进行数据同步
sentinel leader-epoch mymaster 2
sentinel failover-timeout mymasterA 180000 //故障转移超时时间180s,
a,如果转移超时失败,下次转移时时间为之前的2倍;
b,从节点变主节点时,从节点执行slaveof no one命令一直失败的话,当时间超过180S时,则故障转移失败
c,从节点复制新主节点时间超过180S转移失败
sentinel down-after-milliseconds mymasterA 300000//sentinel节点定期向主节点ping命令,当超过了300S时间后没有回复,可能就认定为此主节点出现故障了……
sentinel parallel-syncs mymasterA 1 //故障转移后,1代表每个从节点按顺序排队一个一个复制主节点数据,如果为3,指3个从节点同时并发复制主节点数据,不会影响阻塞,但存在网络和IO开销
4,启动sentinel服务:
./redis-sentinel conf/sentinel_26379.conf &
./redis-sentinel conf/sentinel_26380.conf &
./redis-sentinel conf/sentinel_26381.conf &
关闭:./redis-cli -h 192.168.42.111 -p 26379 shutdown
5,测试:kill -9 6379 杀掉6379的redis服务
看日志是分配6380 还是6381做为主节点,当6379服务再启动时,已变成从节点
假设6380升级为主节点:进入6380>info replication 可以看到role:master
打开sentinel_26379.conf等三个配置,sentinel monitor mymaster 127.0.0.1 6380 2
打开redis6379.conf等三个配置, slaveof 192.168.42.111 6380,也变成了6380
注意:生产环境建议让redis Sentinel部署到不同的物理机上。
重要:sentinel monitor mymaster 192.168.42.111 6379 2 //切记将IP不要写成127.0.0.1
不然使用JedisSentinelPool取jedis连接的时候会变成取127.0.0.1 6379的错误地址
注:我们稍后要启动四个redis实例,其中端口为6379 的redis设为master,其他两个设为slave 。所以mymaster 后跟的是master的ip和端口,最后一个’2’代表只要有2个sentinel认为master下线,就认为该master客观下线,选举产生新的master。通常最后一个参数不能多于启动的sentinel实例数。
哨兵sentinel个数为奇数,选举嘛,奇数哨兵个才能选举成功,一般建议3个
6,RedisSentinel如何监控2个redis主节点呢?
sentinel monitor mymasterB 192.168.1.20 6379 2
……与上面一样…………。
7,部署建议:
a,sentinel节点应部署在多台物理机(线上环境)
b,至少三个且奇数个sentinel节点
c,通过以上我们知道,3个sentinel可同时监控一个主节点或多个主节点
监听N个主节点较多时,如果sentinel出现异常,会对多个主节点有影响,同时还会造成sentinel节点产生过多的网络连接,
一般线上建议还是, 3个sentinel监听一个主节点
8,sentinel哨兵的API
命令:redis-cli -p 26379 //进入哨兵的命令模式,使用redis-cli进入
26379>sentinel masters或sentinel master mymaster //查看redis主节点相关信息
26379>sentinel slaves mymaster //查看从节点状态与相关信息
26379>sentinel sentinels mymaster //查sentinel节点集合信息(不包括当前26379)
26379>sentinel failover mymaster //对主节点强制故障转移,没和其它节点协商
9,客户端连接(redis-sentinel例子工程)
远程客户端连接时,要打开protected-mode no
./redis-cli -p 26380 shutdown //关闭
在使用工程redis-sentinel,调用jedis查询的流程如下:
1,将三个sentinel的IP和地址加入JedisSentinelPool
2,根据IP和地址创建JedisSentinelPool池对象
3,在这个对象创建完后,此时该对象已把redis的主节点
(此时sentinel monitor mymaster 必须写成192.168.42.111 6379 2,不能为127.0.0.1,不然查询出来的主节点的IP在客户端就变成了127.0.0.1,拿不到连接了)查询出来了,当客户准备发起查询请求时,调用pool.getResource()借用一个jedis对象,内容包括主节点的IP和端口;
4,将得到jedis对象后,可执行jedis.get(“age”)指令了……。
RedisCluster是redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求,当遇到单机内存、并发等瓶颈时,可使用此方案来解决这些问题
8.1分布式数据库概念:
1,分布式数据库把整个数据按分区规则映射到多个节点,即把数据划分到多个节点上,每个节点负责整体数据的一个子集
比如我们库有900条用户数据,有3个redis节点,将900条分成3份,分别存入到3个redis节点
900条数据 |
分区规则 |
redis-1 |
redis-2 |
redis-3 |
2,分区规则:
常见的分区规则哈希分区和顺序分区,redis集群使用了哈希分区,顺序分区暂用不到,不做具体说明;
rediscluster采用了哈希分区的“虚拟槽分区”方式(哈希分区分节点取余、一致性哈希分区和虚拟槽分区),其它两种也不做介绍,有兴趣可以百度了解一下。
3,虚拟槽分区(槽:slot)
RedisCluster采用此分区,所有的键根据哈希函数(CRC16[key]&16383)映射到0-16383槽内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据
哈希函数: Hash()=CRC16[key]&16383 按位与
槽与节点的关系如下
0~5460 |
5461~10921 |
10922~16383 |
data |
data |
data |
RedisCluster |
keys |
hash(key) |
用hash函数将键映射到槽,再由槽指向数据 |
0~5461 |
5462~10923 |
10923~16383 |
reids-1节点 |
redis-2节点 |
redis-3节点 |
槽范围 |
槽与节点的关系 |
1092310923
redis用虚拟槽分区原因:1,解耦数据与节点关系,节点自身维护槽映射关系,分布式存储
4,redisCluster的缺陷:
a,键的批量操作支持有限,比如mset, mget,如果多个键映射在不同的槽,就不支持了
b,键事务支持有限,当多个key分布在不同节点时无法使用事务,同一节点是支持事务
c,键是数据分区的最小粒度,不能将一个很大的键值对映射到不同的节点
d,不支持多数据库,只有0,select 0
e,复制结构只支持单层结构,不支持树型结构。
8.2集群环境搭建:
1,在/usr/local/bin/clusterconf目录,
6389为6379的从节点,6390为6380的从节点,6391为6381的从节点
2,分别修改6379、 6380、 7381、 6389、 6390、 6391配置文件
port 6379 //节点端口
cluster-enabled yes //开启集群模式
cluster-node-timeout 15000 //节点超时时间(接收pong消息回复的时间)
cluster-config-file /usrlocalbin/cluster/data/nodes-6379.conf 集群内部配置文件
其它节点的配置和这个一致,改端口即可
3,配置完后,启动6个redis服务
4,自动安装模式:
在/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
a, cd ruby-2.3.1
b, ./configure -prefix=/usr/local/ruby
c, make && make install //过程会有点慢,大概5-10分钟
d, 然后gem install -l redis-3.3.0.gem //没有gem需要安装yum install gem
e,准备好6个节点,(注意不要设置requirepass),将/usr/local/bin/clusterconf/data的config-file删除;依次启动6个节点:./redis-server clusterconf/redis6379.conf
如果之前redis有数据存在,flushall清空;(坑:不需要cluster meet ..)
f, 进入cd /usr/local/bin, 执行以下:1代表从节点的个数
./redis-trib.rb create --replicas 1 192.168.0.111:6379 192.168.0.111:6380 192.168.0.111:6381 192.168.0.111:6389 192.168.0.111:6390 192.168.0.111:6391
主从分配,6379是6389的从节点
貌似只有主节点可读写,从节点不可以
主节点死后,从节点变成主节点
e,集群健康检测:
redis-trib.rb check 192.168.42.111:6379 (注:redis先去注释掉requirepass,不然连不上)
如此出现了这个问题,6379的5798槽位号被打开了
解决如下:
6379,6380,6381的有部分槽位被打开了,分别进入这几个节点,执行
6380:>cluster setslot 1180 stable
cluster setslot 2998 stable
cluster setslot 11212 stable
其它也一样,分别执行修复完后:
此时修复后的健康正常;
当停掉6379后,过会6389变成主节点
注意:使用客户端工具查询时要加-c
./redis-cli -h 192.168.42.111 -p 6379 -c
mset aa bb cc dd,批设置对应在不同的solt上,缺点
14,集群正常启动后,在每个redis.conf里加上
masterauth “12345678”
requiredpass “12345678”
当主节点下线时,从节点会变成主节点,用户和密码是很有必要的,设置成一致
15,这上面是一主一从,那能不能一主多从呢?
./redis-trib.rb create --replicas 2
192.168.42.111:6379 192.168.42.111:6380 192.168.42.111:6381
192.168.42.111:6479 192.168.42.111:6480 192.168.42.111:6481
192.168.42.111:6579 192.168.42.111:6580 192.168.42.111:6581
8.3节点之间的通信
1,节点之间采用Gossip协议进行通信,Gossip协议就是指节点彼此之间不断通信交换信息
当主从角色变化或新增节点,彼此通过ping/pong进行通信知道全部节点的最新状态并达到集群同步
2,Gossip协议
Gossip协议的主要职责就是信息交换,信息交换的载体就是节点之间彼此发送的Gossip消息,常用的Gossip消息有ping消息、pong消息、meet消息、fail消息
meet消息:用于通知新节点加入,消息发送者通知接收者加入到当前集群,meet消息通信完后,接收节点会加入到集群中,并进行周期性ping pong交换
ping消息:集群内交换最频繁的消息,集群内每个节点每秒向其它节点发ping消息,用于检测节点是在在线和状态信息,ping消息发送封装自身节点和其他节点的状态数据;
pong消息,当接收到ping meet消息时,作为响应消息返回给发送方,用来确认正常通信,pong消息也封闭了自身状态数据;
fail消息:当节点判定集群内的另一节点下线时,会向集群内广播一个fail消息,后面会讲到。……
3,消息解析流程
所有消息格式为:消息头、消息体,消息头包含发送节点自身状态数据(比如节点ID、槽映射、节点角色、是否下线等),接收节点根据消息头可以获取到发送节点的相关数据。
消息解析流程:
4,选择节点并发送ping消息:
Gossip协议信息的交换机制具有天然的分布式特性,但ping pong发送的频率很高,可以实时得到其它节点的状态数据,但频率高会加重带宽和计算能力,因此每次都会有目的性地选择一些节点; 但是节点选择过少又会影响故障判断的速度,redis集群的Gossip协议兼顾了这两者的优缺点,看下图:
不难看出:节点选择的流程可以看出消息交换成本主要体现在发送消息的节点数量和每个消息携带的数据量
流程说明:
5,集群扩容
这也是分布式存储最常见的需求,当我们存储不够用时,要考虑扩容
扩容步骤如下:
1),同目录下新增redis6382.conf、redis6392.conf两
启动两个新redis节点
./redis-server clusterconf/redis6382.conf & (新主节点)
./redis-server clusterconf/redis6392.conf & (新从节点)
2),新增主节点
./redis-trib.rb add-node 192.168.42.111:6382 192.168.42.111:6379
6379是原存在的主节点,6382是新的主节点
3),添加从节点
redis-trib.rb add-node --slave --master-id 03ccad2ba5dd1e062464bc7590400441fafb63f2 192.168.42.111:6392 192.168.42.111:6379
--slave,表示添加的是从节点
--master-id 03ccad2ba5dd1e062464bc7590400441fafb63f2表示主节点6382的master_id
192.168.42.111:6392,新从节点
192.168.42.111:6379集群原存在的旧节点
4),redis-trib.rb reshard 192.168.42.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 //表示全部节点重新洗牌
新增完毕!
3,集群减缩节点:
集群同时也支持节点下线掉
下线的流程如下:
流程说明:
删除节点也分两种:
一种是主节点6382,一种是从节点6392。
在从节点6392中,没有分配哈希槽,执行
./redis-trib.rb del-node 192.168.42.111:6392 7668541151b4c37d2d9 有两个参数ip:port 和节点的id。 从节点6392从集群中删除了。
主节点6382删除步骤:
1,./redis-trib.rb reshard 192.168.42.111:6382
问我们有多少个哈希槽要移走,因为我们这个节点上刚分配了1000 个所以我们这里输入1000
2,最后
./redis-trib.rb del-node 192.168.42.111:6382 3e50c6398c75e0088a41f908071c2c2eda1dc900
此时节点下线完成……
请求路由重定向
我们知道,在redis集群模式下,redis接收的任何键相关命令首先是计算这个键CRC值,通过CRC找到对应的槽位,再根据槽找到所对应的redis节点,如果该节点是本身,则直接处理键命令;如果不是,则回复键重定向到其它节点,这个过程叫做MOVED重定向
故障转移:
redis集群实现了高可用,当集群内少量节点出现故障时,通过故障转移可以保证集群正常对外提供服务。
当集群里某个节点出现了问题,redis集群内的节点通过ping pong消息发现节点是否健康,是否有故障,其实主要环节也包括了 主观下线和客观下线;
主观下线:指某个节点认为另一个节点不可用,即下线状态,当然这个状态不是最终的故障判定,只能代表这个节点自身的意见,也有可能存在误判;
下线流程:
客观下线:指真正的下线,集群内多个节点都认为该节点不可用,达成共识,将它下线,如果下线的节点为主节点,还要对它进行故障转移
假如节点a标记节点b为主观下线,一段时间后节点a通过消息把节点b的状态发到其它节点,当节点c接受到消息并解析出消息体时,会发现节点b的pfail状态时,会触发客观下线流程;
当下线为主节点时,此时redis集群为统计持有槽的主节点投票数是否达到一半,当下线报告统计数大于一半时,被标记为客观下线状态。
故障恢复:
故障主节点下线后,如果下线节点的是主节点,则需要在它的从节点中选一个替换它,保证集群的高可用;转移过程如下:
当从节点收集到足够的选票(大于N/2+1)后,触发替换主节点操作,撤销原故障主节点的槽,委派给自己,并广播自己的委派消息,通知集群内所有节点。