哨兵:https://www.cnblogs.com/huangfuyuan/p/9880379.html
其他:https://blog.youkuaiyun.com/bird73/article/details/79792548
大概:https://buluo.qq.com/p/detail.html?bid=404153&pid=3922274-1540278633&from=grp_sub_obj
Redisson:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95
redis并发写并发竞争问题:https://www.cnblogs.com/shamo89/p/8385390.html
缓存的一些问题:https://www.cnblogs.com/dinglang/p/6133501.html
Redis服务器将所有数据库都保存在服务器状态redisServer结构的db数组中,数组每个项都是一个redisDb结构,每个redisDb结构代表一个数据库。
在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库。
struct redisServer{
//一个数组,保存着服务器中的所有数据
redisDb *db;
//服务器的数据库数量
int dbnum;
//记录了保存条件的数组
struct saveparam *saveparams;
//计数器
long long dirty;
//上一次执行保存的时间
time_t lastsave;
//AOF缓冲区
sds aof_buf;
//一个链表,保存了所有客户端状态
list *clients;
//主服务器的地址
char *masterhost;
//主服务器的端口
int masterport;
}
select命令来切换数据库。
typedef struct redisClient{
//记录客户端当前正在使用的数据库
redisDb *db;
//客户端正在使用的套接字描述符
int fd;
//客户端名字,null可以设定
rodj *name;
//标志记录客户端角色
int flags;
//输入缓冲区
sds querybuf;
//输出缓冲区
char buf[REDIS_REPLY_CHUNK_BYTES];
int bufpos;
//argv[0]保存要执行的命令,之后是命令参数
robj **argv;
//argv数组长度
int argc;
//身份验证
int authenticated;
//创建客户端时间
time_t ctime;
//与服务器最后一次互动时间
time_t lastinteraction;
//空转时间,最后一次互动到现在过去多少秒
time_t obuf_soft_limit_reached_time;
}
redisClient.db指针指向redisServer.db数组中的一个元素,被指向的元素就是客户端的目标数据库。
数据库的键空间
Redis是一个键值对数据库服务器,服务器中的每一个数据库都由一个redisDb结构表示,其中dict字典保存了数据库中的所有键值对,将这个字典称为键空间。
typedef struct redisDb{
//数据库键空间,保存数据中的所有键值对
dict *dict;
//过期字典,保存键过期时间
dict *expires;
}redisDb;
键空间的和用户所见的数据库是直接对应的;
- 键空间的键也就是数据库的键,每个键都是一个字符串对象。
- 键空间的值也就是数据库的值,每个值可以是字符串对象,列表对象,哈希表对象,集合对象和有序集合对象中的任意一种redis对象。
读写键空间时的维护操作
当使用redis命令对数据库进行读写时,服务器不仅会对键空间执行指定的读写操作,还会执行一些额外的维护操作,包括如下
- 在读取一个键之后(读写操作都需要对一个键进行读取),服务器会根据键是否存在来更新服务器的键空间命中hit次数或键空间不命中miss次数,这两个值可以在info stats命令的keyspace_hits属性和keyspace_misses属性中查看。
- 在读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,这个值可以用于计算键的闲置时间,使用OBJECT idletime《key》命令查看闲置时间。
- 如果服务器读取一个键时发现该键已经过期,那么服务器会先删除这个过期键,然后才执行余下操作。
- 如果客户端使用WATCH命令监视了某个键,那么服务器在对呗监视的键进行修改后,会将这个键标志为脏(dirty),从而让事务程序注意到这个键已经被修改了。
- 服务器每次修改一个键后,都会对脏(dirty)键计数器值增1,这个计数器会触发服务器的持久化以及复制操作。
- 如果服务器开启了数据库通知功能,那么在对键修改之后,服务器将按配置发送相应的数据库通知。
保存过期时间。
redisDb结构的expires字典保存了数据库中所有键的过期时间,我们称这个字典为过期字典。
- 过期字典的键时一个指针,这个指针指向键空间中的某个键对象(也即是某个数据库键)。
- 过期字典的值是一个long long类型的整数,这个整数保存了键所指向的数据库键的过期时间,一个毫秒精度的Unix时间戳。
过期键删除策略
- 定时删除:在设置一个键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。
- 惰性删除:放任键过期不管,但是每次从键空间获取键时,都检查取得的键是否过期,如果过期的话,就删除,没有就返回。
- 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键,至于要删多少,以及要检查多少则由算法决定。
在这三种策略中,1.3为主动删除,2为被动删除。
惰性删除实现。
惰性删除策略由db.c/expireIfNeeded函数实现,所有读写数据库的Redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查。如果已过期,那么对输入键从数据库删除。如果未过期,那么不做动作。
定期删除策略的实现
由redis.c/activeExpireCycle函数实现,每当Redis的服务器周期性操作redis.c/serverCron函数时,activeExpireCycle函数就会被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。其中全局变量current_db会记录当前检查进度,并在下一次调用时,接着上次继续处理,保证所有数据库都会检查一遍,然后重置为0,开始新的一轮。
AOF,RDB和复制功能对过期键的处理。
- 生成RDB文件,会对数据库键进行检查,已过期的键不会保存到新创建的RDB文件中。
- 载入RDB文件。如果以主服务器运行,那么在载入RDB文件时,程序会对文件保存的键进行检查,未过期的会载入。过期的忽略。
- 如果以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有键,不论是否过期,都会呗载入数据库中,不过,因为主从服务器在进行数据同步的时候,从服务器的数据会清空。
- 当服务器以AOF持久化模式运行时,如果数据库中的某个键已过期,但他还没有被惰性删除或定期删除,那么不会有任何影响。当过期键被惰性删除或者定期删除后,会向文件追加一条del命令,来显示删除。
- AOF重写,和生产RDB类似,对键检查,过期的不会被保存。
- 复制,当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器控制。
命令请求的执行过程
一个命令请求从发送到完成主要包括以下步骤:
- 客户端将命令请求发送给服务器。
- 服务器读取命令请求,并分析出命令参数。
- 命令执行器根据参数查找命令的实现函数,然后执行实现函数并得出命令回复。
- 服务器将命令回复返回给客户端。
serverCron函数默认每隔100毫秒执行一次,它的工作主要包括更新服务器状态信息,处理服务器接收的SIGTERM信号,管理客户端资源和数据库状态,检查并执行持久化操作等等。
服务器从启动到能够处理客户端的命令请求需要执行以下步骤
- 初始化服务器状态,
- 载入服务器配置,
- 初始化服务器数据结构,
- 还原数据库状态,
- 执行事件循环
RDB
通过save,和bgsave可以生产RDB文件。
save命令会直接阻塞Redis服务器进程,直到RDB文件创建完毕,在服务器阻塞期间不处理任何命令请求。
bgsave命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程继续处理命令请求。
创建RDB文件的工作由rdb.c/rdbSave函数完成。
RDB文件载入是在服务器启动时自动执行的。
AOF文件的更新频率通常比RDB文件的更新频率高,所以
- 如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态。
- 只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态。
- 服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。
自动间隔性保存
因为BGSAVE命令可以不阻塞服务器进程的情况下执行,所以Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。
如果配置save 900 1,即900秒有一次修改就会被执行
设置保存条件,当服务器启动时,用户可以通过指定配置文件或者传入启动参数的方式设置save选项,用户没有回主动设置默认条件,save900 1,save 300 10 ,save 60 10000,接着会根据save选项所设置的保存条件,设置服务器状态redisServer结构saveparams属性。该属性是一个数组,数组中的每个元素都是一个saveparam结构,每个结构都保存一个save选项设置的保存条件。
struct saveparam{
//秒数
time_t seconds;
//修改数
int changes;
};
除saveparams数组外,服务器还维持一个dirty计数器,以及一个lastsave属性。
dirty计数器记录距离上一次成功执行save命令或者BGSAVE命令之后,服务器对数据库状态进行了多少次修改。
lastsave属性是一个时间戳,记录上一次数据库成功执行save命令或BGSAVE命令的时间。
检查保存条件是否满足。
Redis服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,其中一项就是检查save选项所设置的保存条件是否满足,如果满足就执行BGSAVE命令。
RDB文件结构
- RDB文件最开头是REDIS部分,长度5字节,保存REDIS五个字符,用于快速检查是否是RDB文件。
- db_version长度为4字节,字符串表示的整数记录RDB文件的版本号。
- databases部分包含零至多个数据库,以及各个数据库中的键值对数据。
- EOF常量,标志着RDB文件正文内容结束。
- check_sum是一个8字节的无符号整数,保存着一个校验和。以此检查RDB文件是否出错和损坏。
databases部分
- selectdb常量的长度为一个字节,标识接下来读入的是数据库号码。
- db_number保存一个数据库号码进行切换到正确的数据库。
- key_value_pairs部分保存数据库中所有键值对数据,如果带有过期时间,那么过期时间也会和键值对保存在一起。
key_value_pairs部分
不带过期时间
- type:代表一种对象类型或者底层编码。
- key:字符串对象,保存键对象
- value:保存值对象。
带过期时间
- expiretime_ms:长度一字节标识下来将是一个毫秒的过期时间
- ms:8字节长的带符号整数,记录一个毫秒单位的Unix时间戳,代表过期时间。
- type:代表一种对象类型或者底层编码。
- key:字符串对象,保存键对象
- value:保存值对象。
AOF
通过保存Redis服务器锁执行的写命令来记录数据库状态的。
AOF持久化的实现
分为命令追加,文件写入,文件同步三个步骤。
命令追加:当AOF持久化功能处于开启状态时,服务器在执行完一个写命令后,会以协议的格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。
文件写入与同步:Redis进程就是一个事件循环,这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,在每次结束一个事件循环之前,会调用flushAppendOnlyFile函数,考虑是否将aof_buf缓冲区中的内容写入和保存到AOF文件里面。flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项的值来决定。
- always:将aof_buf缓冲区的所有内容写入同步AOF文件。
- everysec:将aof_buf缓冲区的内容写入文件,如果上次同步的时间距离现在超过1秒钟,那么在次同步,并且是由另一个线程专门负责。
- no:将aof_buf缓冲区中的所有内容写入文件,但不对AOF文件进行同步,何时同步由系统决定。
AOF重写。
为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写功能,通过该功能Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,保存数据库状态相同,但新文件不会有冗余命令,体积会小很多。
AOF重写实现。
AOF文件重写并不需要对现有的AOF文件做任何操作,是通过读取服务器当前的数据库状态来实现的。
AOF后台重写
AOF重写程序放到子程序里执行,子进程运行期间,服务器进程可以处理命令请求,并且子进程带有服务器进程的副本,使用子进程而不是线程,可以避免使用锁的情况下,保证数据安全性。
AOF在重写期间,服务器继续处理命令请求,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建了子进程之后开始使用,当服务器执行完一个写命令后,他会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。
事件
Redis服务器是一个事件驱动程序,服务器需要处理两类事件:
- 文件事件,Redis服务器通过套接字与客户端或其他Redis服务器进行连接,而文件事件是服务器对套接字操作的抽象,服务器与客户端的通信会产生相应的文件事件,而服务器通过监听并处理这些事件来完成一系列网络通信操作。
- 时间事件,Redis服务器中的一些操作,比如serverCron函数,需要给定时间点执行,而时间事件就是服务器对这类定时操作的抽象。
文件事件
文件事件处理器使用I/O多路复用程序来同事监控多个套接字,并且根据嵌套字目前执行的任务来为套接字关联不同的事件处理器。
当被监听的套接字准备好执行连接应答,读取,写入,关闭等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
文件事件处理器的构成。
分别是套接字,I/O多路复用程序,文件事件分派器和事件处理器。
多路复用程序监听多个套接字,然后放入队列里面,然后通过这个队列以有序,同步,每次一个套接字的方式向文件事件分派器传送套接字,当上一个完毕后才会继续向文件事件分派器传送下一个套接字。
时间事件
分为定时事件和周期事件,定时事件只在指定的时间到达一次,而周期性事件则每隔一段时间到达一次。
文件事件和时间时间之间是合作关系,服务器会轮流处理这两种事件,并且处理事件过程中也不会进行抢占。
时间时间的实际处理时间通常会比设定的到达时间晚一些。
客户端
套接字描述符
fd:可以是-1或者大于-1的整数。
- 伪客户端的fd属性为-1,伪客户端的请求来自于AOF文件或者Lua脚本,而不是网络,所以不需要套接字连接,
- 普通客户端的fd属性大于-1的整数,普通客户端使用套接字来与服务器进行通信,所以会用fd来记录套接字描述符
标志
flags记录了客户端的角色和所处状态,
输入缓冲区
输入缓冲区的大小会根据输入内容动态的 缩小和扩大,但大小不能超过1GB,否则服务器将关闭这个客户端。
命令与命令参数
服务器将客户端发送的命令请求保存到客户端状态的querybuf属性之后 ,服务器将对命令请求的内容进行分析,并将命令参数和命令个数保存到argv属性和argc属性;
命令实现函数
当服务器从协议内容中分析并得出argv属性和argc属性后,将根据项argv[0]的值,在命令表中查找命令所对应的命令实现函数。
之后服务器可以将cmd属性指向redisCommand结构,以及argv,argc属性中的信息调用命令实现函数。
输出缓冲区
执行命令回复会被保存在客户端状态的输出缓冲区里面,每个客户端都有两个输出缓冲区可用,一个缓冲区大小是固定的,另一个是大小可变的
固定的缓冲区用于回复长度比较小的回复,比如OK,简短字符串值,整数,错误回复等
可变大小的缓冲区用于保存长度比较大的回复,
buf是一个默认16*1024字节的数组,bof数组的默认大小为16KB,
身份验证
authenticated属性用于记录是否通过了身份验证
复制(主从)
用户可以通过执行slaveof命令或者设置slaveof选项,让一个服务器去复制另一个服务器。
旧版复制功能的实现。
Redis的复制功能分为同步和命令传播两个操作。
- 同步操作作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态。
- 命令传播操作则用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器重新回到一致状态。
同步。
- 从服务器向主服务器发送SYNC命令。
- 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
- 当主服务器的BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,重服务器载入这个RDB文件,将自己更新至主服务器执行BGSAVE命令时的数据库状态。
- 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
命令传播
在同步执行完毕之后,主从服务器两者的数据库将达到一致,然后主服务器会将自己执行的写命令发送给从服务器执行。
缺陷:
主从复制的两种情况。
初次复制,从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上次不同
断线后复制,处于命令传播阶段的主从因为网络中断又重新连接继续复制的情况。
SYNC是一个耗资源的操作,耗费大量CPU,内存,和磁盘I/O资源,发送RDB会耗网络资源,从服务器在载入时,没办法处理命令请求。
新版复制功能实现。
为了解决低效的问题使用PSYNC命令执行复制操作,他具有完整重同步和部分重同步两种模式。
- 其中完整重同步用于初次复制情况,完整重同步的执行步骤和SYNC命令的执行步骤一样,通过主服务器生成RDB文件,向缓冲区写命令来进行同步。
- 而部分重同步则用于处理断线后情况,当从服务器在断线之后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,
部分重同步的实现
- 主服务器的复制偏移量和从服务器的复制偏移量。
- 主服务器的复制积压缓冲区。
- 服务器运行ID
复制偏移量,执行复制的双方——主服务器和从服务器会分别维护一个复制偏移量。
- 主服务器每次向从服务器传播N个字节数据时,就将自己的复制偏移量的值加N。
- 从服务器每次收到主服务器传播来的N个字节数据时,就将自己的复制偏移量的值加上N。
如果主从服务器的处于一致状态那么偏移量是相同的,如果不同,那么主从服务器的状态未处于一致状态。
复制积压缓冲区,
复制积压缓冲区是用主服务器维护一个固定长度先进先出队列,默认1MB,当主服务器进行命令传播时,他不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面,因此,主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令并且缓冲区会为队列中的每个字节记录相应的复制偏移量。
当从服务器重新连接上主服务器时,从服务器会将自己的复制偏移量offset发送给主服务器,主服务器根据复制偏移量来决定对从服务器执行何种同步操作。
- 如果偏移量之后的数据仍然在复制积压缓冲区里,那么执行部分同步操作。
- 相反,那么执行完整的同步操作
服务器运行ID。
- 每个服务器都会有自己运行的ID。
- 运行ID在服务器启动时自动生成,由40个随机16进制字符组成。
当从服务器进行初次复制时,主服务器会将自己的ID传送给从服务器,从服务器会保存起来,当重新连上一个主服务器时,会向当前服务器发送之前保存的ID,如果相同执行部分同步,不同则执行完整同步。
复制的实现。
- 设置主服务器的地址和端口.
- 建立套接字连接。
- 发送PING命令。
- 身份验证。
- 发送端口信息。
- 同步。
- 命令传播。
- 心跳检测
哨兵
- 哨兵是Redis高可用性的解决方案,有一个或多个哨兵实例组成系统可以监视任意多个主服务器,以及这些主服务器下的所有从服务器,并在被监视的主服务器下线时,自动将某个从服务器升级为新的主服务器
- 哨兵只是一个运行在特殊模式下的Redis服务器,他使用了和普通模式不同的命令表,所以哨兵模式能够使用的命令和普通Redis服务器能够使用的命令不同。
- 哨兵会读入用户指定的配置文件,为每个要被监视的主服务器创建相应的实例结构,并创建连向主服务器的命令连接和订阅连接,其中命令连接用于向主服务器发送命令请求,而订阅连接则用于接收指定频道的信息。
- 哨兵通过向主服务器发送INFO命令来获得主服务器属下所有从服务器的地址信息,并为这些从服务器创建相应的实例结构,以及连向这些从服务器的命令连接和订阅连接。
- 在一般情况下,哨兵以没10秒一次的频率向被监视的主服务器和从服务器发送INFO命令,当主服务器处于下线状态,或者哨兵正在对主服务器进行故障转移操作时,哨兵向从服务器发送INFO命令会改为1秒一次。
- 对于监视同一个主服务器和从服务器的多个哨兵来说,他们会以每两秒一次的频率通过向被监视服务器的_sentinel_:hello频道发送消息来向其他哨兵宣告自己的存在。
- 每个哨兵也会从_sentinel_:hello频道中接收其他哨兵发来的信息,并根据这些信息为其他哨兵创建相应的实例结构,已经命令连接。
- 哨兵只会与主服务器和从服务器创建命令连接和订阅连接,哨兵与哨兵直接只会创建命令连接。
- 哨兵以每秒一次的频率向实例(包括主服务器,从服务器,其他哨兵)发送PING命令,并根据实例对PING命令的回复来判断实例是否在线,当一个实例在指定的时长中连接向哨兵发送无效回复时,哨兵会将这个实例判断为主观下线。
- 当哨兵将一个主服务器判断为主观下线时,他会向同样监视这个主服务器的其他哨兵进行询问,看他们是否同意这个主服务器已经进入主观下线状态。
- 当哨兵收集到足够多的主观下线投票后,他会将主服务器判断为客观下线,并发起一次针对主服务器的故障转移操作。
集群
Redis集群是Redis提供的分布式数据库方案,集群通过分片进行数据共享,并提供复制和故障转移功能。
分为节点,槽指派,命令执行,重新分片,转向,故障转移,消息。
节点
一个Redis集群通常由多个节点组成,刚开始的时候都是相互独立的,他们都处于只包含自己的集群中,要组建一个真正可操作的工作集群,必须将各个独立的节点连接起来。
槽指派
Redis集群通过分片的方式来保存数据库中的键值对,集群的整个数据库被分为16384个槽,数据库中的每个键都属于这16384个槽的其中一个,集群中的每一个节点可以处理0个或最多16384个槽,当数据库中的16384个槽都有节点在处理时,集群属于上线状态,相反处于下线状态。
通过发送CLUSTER ADDSLOTS命令,我们可以将一个或者多个槽指派给节点负责。
在集群中执行命令。
在对数据库16384个槽都进行指派后,集群进入上线状态,这时客户端就可以向集群发数据命令了。
当客户端向节点发与数据库有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己,如果所在槽在当前节点,那么这个节点执行命令,如果不在,那么节点返回一个MOVED错误,并引客户端转向至正确的节点并再次发送之前想要执行的命令。。
计算键属于哪个槽。
MOVED错误
节点数据库的实现
节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库。
重新分片
集群重新分片操作可以将任意数量已经指派给某个节点的槽修改为指派给另一个节点,并且相关槽所属的键值对也会从源节点被移动到目标节点。
重新分片操作可以在线进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
ASK 错误
在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现一种情况,属于迁移槽的一部分键值保存在源节点里面,而另一半在目标节点里面。然后MOVED错误
复制与故障转移
故障检测。
集群中的每个节点都会定期的向集群中的其他节点发送PING消息,以此来检测对方是否在线。
故障转移
- 复制下线主节点的所有从节点里面,会有一个从节点会被选中
- 呗选中的从节点会执行SLAVEOF no one 命令,成为新的主节点
- 新的主节点会撤销所有对已下线的主节点的槽指派,并将这些槽全部指派给自己。
- 新的主节点向集群广播一天PONG消息,这条PONG消息可以让集群中的其他节点立即致电这个节点变成主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
- 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。
选举新的主节点
- 集群的配置纪元是一个自增计数器,初始值为0
- 当集群里的某个节点开始一次故障转移操作时,集群配置纪元会增一
- 对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票机会,而第一个向主节点要求投票的从节点将获得主节点的投票。
- 当从节点发现自己正在负责的主节点进入已下线状态时,从节点会向集群广播一条消息,让具有投票权的主节点向这个从节点投票
- 如果一个主节点具有投票权并且这个主节点尚未投票给其他从节点,那么主节点返回一条信息,支持从节点成为新的主节点。
- 每个参与选举的从节点都会接收……ACK消息,并根据收到多少这种消息来统计自己获得了多少主节点的支持。
- 如果集群里有N个具有投票权的主节点,那么当一个从节点收集到大于等于N/2+1涨支持票时,这个节点就会当选为新的主节点。
- 如果在一个配置纪元里面没有从节点数能收集到足够多的支持票,那么会进入一个新纪元,在次选举。