Redis设计与实现
2021-12-21 11:23:01 0 举报
AI智能生成
Redis设计与实现
作者其他创作
大纲/内容
数据结构与对象
数据结构
简单动态字符串
SDS的定义
len
free
buf[]
SDS与C字符串的区别
常数复杂度获取字符串长度
杜绝缓存区溢出
减少修改字符串时带来的内存重分配次数
空间预分配
惰性空间释放
二进制安全
兼容部分C字符串函数
链表
链表和链表结点的实现
字典
字典的实现
哈希表
table
dictEntry
size记录哈希表大小,也即table数组的大小
sizemask属性和哈希值一起决定一个键应该放到table数组的哪个索引上
字典
type
privatedata
ht
rehashidx
哈希算法
解决键冲突
rehash
过程
为ht[1]哈希表分配空间
将保存在ht[0]的所有键值对rehash好ht[1]上
当ht[0]包含的所有键值对都迁移到ht[1]之后,释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表
哈希表的扩展与缩容
load_factor
自动扩展
服务器目前没有执行BGSAVE命令或者BGREWRITEAOF命令,且负载因子大于等于1
服务器正在执行BGSAVE命令或者BFREWRITEAOF命令,并且哈希表的负载因子大于等于5
自动收缩
渐进式rehash
为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表
字典中维持一个索引计数器变量rehashidx,设置为0,表示rehash工作正式开始
在rehash进项期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作之外,还会顺带将ht[0]哈希表再rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成后,程序将rehashidx属性值+1
随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash到ht[1],这时程序将rehashidx属性的值设为-1,表示rehash操作已完成
跳跃表
跳跃表的实现
header
tail
level
length
zskiplistNode
level
前进指针
跨度
后退指针
score
成员对象
跳跃表
整数集合
实现
contents
length
encoding
升级
根据新元素类型,扩展整数集合底层数组的空间大小,并为新元素分配空间
将原数据按照新类型进行转换并保持有序性不变
将新元素添加到底层数组中
好处
提升灵活性
节约内存
降级
压缩列表
压缩列表的构成
压缩列表节点的构成
previous_entry_length
encoding
content
连锁更新
对象
对象的类型与编码
类型
编码和底层实现
字符串对象
int
raw
embstr
内存分配为一次
释放embstr编码的字符串对象只需调用一次内存释放函数
embstr编码的对象数据都保存在一块连续的内存中,能更好利用缓存带来的优势
编码的转换
int
embstr
列表对象
ziplist
linkedlist
编码转换
哈希对象
ziplist
hashtable
编码转换
集合对象
intset
hashtable
编码的转换
有序集合对象
ziplist
skiplist
zskiplist
dict
编码的转换
类型检查与命令多态
类型检查的实现
执行命令前,先检查键的值对象是否为执行命令所需类型
否则,拒绝执行,并返回错误
多态命令的实现
内存回收
对象共享
对象空转时长
单机数据库的实现
数据库
服务器中的数据库
切换数据库
数据库键空间
添加新键
删除键
更新键
对键取值
其他键空间操作
读写键空间时的维护操作
如果读取一个键时发现键已经过期,会先删除过期键
客户端使用WATCH命令监视某个键,该键修改后,会被标记为脏dirty,从而让事务程序注意到该键已经被修改
每修改一次键,脏dirty键计数器的值增1,该计数器会出发服务器的持久化以及复制操作
...
设置键的生存时间或过期时间
设置过期时间
保存过期时间
移除过期时间
计算并返回剩余生存时间
过期键的判定
检查给定键是否存在与过期字典,如果存在,那么取键的过期时间
检查当前UNIX时间戳是否大于键的过期时间,如果是的话,那么键过期
过期键删除策略
定时删除
惰性删除
定期删除
Redis的过期删除策略
惰性删除策略的实现
定期删除策略的实现
AOF、RDB和复制功能对过期键的处理
生成RDB文件
载入RDB文件
AOF文件写入
AOF重写
复制
数据库通知
发送通知
发送通知函数的实现
RDB持久化
RDB文件的创建与载入
SAVE命令执行时的服务器状态
BGSAVE命令执行时服务器状态
RDB文件载入时服务器处于阻塞状态
自动间隔性保存
设置保存条件
dirty计数器和lastsave属性
检查保存条件是否满足
RDB文件结构
REDIS
db_version
databases
database结构
SELECTDB
db_number
key_value_pairs
不带过期时间结构
TYPE
key
value
带有过期时间的结构
EXPIRETIME_MS
ms
value的编码
字符串对象
REDIS_ENCODING_INT
REDIS_ENCODING_RAW
无压缩
压缩
列表对象
list_length
item
集合对象
set_size
elem
哈希表对象
hash_size
key_value_pair
有序集合对象
sorted_set_size
element
member
score
INTSET编码的集合
ZIPLIST
分析RDB文件
不包含任何键值对的RDB文件
包含字符串键的RDB文件
包含带有过期时间的字符串键的RDB文件
包含一个集合键的RDB文件
关于分析RDB文件的说明
EOF
check_sum
AOF持久化
AOF持久化的实现
命令追加
文件写入与同步
AOF文件的载入与数据还原
创建一个不带网络连接的伪客户端
从AOF文件中分析并读取出一条写命令
使用伪客户端执行被读入的写命令
直到AOF文件中的所有命令都被执行完毕
AOF重写
重写的实现
AOF后台重写
重写缓冲区
重写完成后
将AOF重写缓冲区的内容写入到新AOF文件中
对新AOF文件改名,原子覆盖现有AOF文件,完整新旧交替
事件
文件事件
文件事件处理器的构成
多个文件事件
I/O多路复用程序的实现
事件的类型
API
ae.c/aeCreateFileEvent
ae.c/aeDeleteFileEvent
ae.c/aeGetFileEvents
ae.c/aeWait
ae.c/aeApiPol
ae.c/aeProcessEvents
ae.c/aeGetApiName
文件事件的处理器
连接应答处理器
命令请求处理器
命令回复处理器
一次完整的客户端与服务器连接事件示例
时间事件
实现
API
ae.c/aeCreateTimeEvent
ae.c/aeDeleteFileEvent
ae.c/aeSearchNearestTimer
ae.c/processTimeEvents
时间事件应用实例:serverCron函数
更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况等
清理数据库中的过期键值对
关闭和清理连接失效的客户端
尝试进行AOF或RDB持久化操作
如果是主服务器,对从服务器进行定期同步
如果处于集群模式,对集群进行定期同步和连接测试
事件的调度与执行
服务器运行流程
事件的调度和执行规则
aeApiPoll函数的最大阻塞时间由到达时间最接近当前时间的时间事件决定
处理文件事件
对文件事件和时间事件的处理都是同步、有序、原子地执行的
因为时间事件是在文件事件之后执行,并且事件之间不会出现抢占,所以时间事件的实际处理事件,通常会比时间事件设定的到达时间稍晚点
客户端
概要
客户端的套接字描述符
客户端的名字
客户端的标志值
指向客户端正在使用的数据库的指针,以及该数据库的号码
客户端当前要执行的命令、命令的参数、命令参数的个数,以及指向命令实现函数的指针
客户端的输入和输出缓冲区
客户端的复制状态信息,以及进行复制所需的数据结构
客户端执行BRPOP,BLPOP等列表阻塞命令时使用的数据结构
客户端的事务状态,以及执行WATCH命令时用到的数据结构
客户端执行发布与订阅功能时用到的数据结构
客户端的身份验证标志
客户端的创建时间,客户端和服务器最后一次通信时间,客户端的输出缓冲区大小超出软性限制的时间
客户端属性
通用属性
套接字描述符
名字
标志
客户端角色
REDIS_MASTER/REDIS_SLAVE
REDIS_PRE_PSYNC
REDIS_LUA_CLIENT
客户端状态
REDIS_MONITOR
REDIS_UNIX_SOCKET
REDIS_BLOCKED
REDIS_UNBLOCKED
REDIS_MULTI
REDIS_DIRTY_CAS
REDIS_CLOSE_ASAP
REDIS_CLOSE_AFTER_REPLY
REDIS_ASKING
REDIS_FORCE_AOF
输入缓冲区
命令与命令参数
argv
argc
命令的实现函数
输出缓冲区
固定缓冲区
可变大小缓冲区
身份验证
时间
客户端的创建与关闭
创建普通客户端
关闭普通客户端
客户端进程退出或者被杀死,那么客户端与服务器之间的网络连接被关闭,造成客户端被关闭
客户端向服务器发送了带有不符合协议格式的命令请求,客户端也会被服务器关闭
客户端成为了CLIENT KILL命令的目标,那么它也会被关闭
服务器设置了timeout配置选项,当客户端的空转时间超过设置时间时,客户端被关闭
客户端发送的命令请求超过了输入缓冲区的大小限制,默认1GB
发送给客户端的命令回复大小超过了输出缓冲区的限制大小,默认1GB
硬性限制
软性限制
lua脚本的伪客户端
AOF文件的伪客户端
服务器
命令请求的执行过程
发送命令请求
读取命令请求
读取套接字中协议格式的命令请求,并将其保存在客户端状态的输入缓冲区里面
对输入缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数,以及命令参数的个数,然后分别将参数和参数个数保存到客户端状态的argv属性和argc属性里面
调用命令执行器,执行客户端指定的命令
命令执行器
查找命令实现
redisCommand结构
执行预备操作
检查客户端状态的cmd指针是否指向NULL,如果是,则表示用户输入的命令找不到实现,向服务器返回错误
根据redisCommand结构的arity属性,检查命令请求所给的参数个数是否正确,不正确返回错误
检查客户端是否通过身份验证,未通过的只能执行AUTH命令
如果服务器打开了maxmemory功能,那么在执行命令前,检查服务器内存占用情况,并在需要时进行内存回收,如果内存回收失败,向客户端返回一个错误
如果服务器上次执行BGSAVE命令时出错,并且服务器打开了stop-writes-on-bgsave-error功能,而且将要执行的是写命令,服务器拒绝执行命令并返回错误
如果客户端当前正在用SUBSCRIBE命令订阅频道,或者正在用PSUBSCRIBE命令订阅模式,那么服务器只会执行客户端发来的SUBSCRIBE,PSUBSCRIBE,UNSUBSCRIBE,PUNSUBSCRIBE四个命令
如果服务器正在执行lua脚本而超时并进入阻塞,那么服务器只会执行客户端发来的SHUTDOWN nosave命令和SCRIPT KILL命令
如果客户端正在执行事务,那么服务器只会执行客户端发来的EXEC,DISCARD,MULTI,WATCH命令
如果服务器打开了监视器功能,那么服务器会将要执行的命令和参数等信息发送给监视器
完成以上预备操作后,服务器开始真正执行命令
调用命令的实现函数
保存命令回复
执行后续工作
如果开启了慢查询,会检查是否需要为刚刚执行完的命令请求添加一条慢查询日志
根据刚刚执行命令所耗费的时长,更新被执行命令的redisCommand结构的milliseconds属性,并将命令的redisCommand结构的calls计数器的值加一
如果开启了AOF持久化功能,AOF持久化模块会将刚刚执行的命令请求写入到AOF缓冲区里面
如果有其他服务器正在复制当前这个服务器,那么服务器会将刚刚执行的命令传播给所有从服务器
将命令回复发送给客户端
客户端接收并打印命令回复
serverCron函数
更新服务器时间缓存
更新LRU时钟
更新服务器每秒执行命令次数
更新服务器内存峰值记录
处理SIGTERM信号
管理客户端资源
管理数据库资源
执行被延迟的BGREWRITEAOF
检查持久化操作的运行状态
将AOF缓冲区中的内容写入AOF文件
关闭异步客户端
增加cronloops计数器的值
初始化服务器
初始化服务器状态结构
载入配置选项
初始化服务器数据结构
initServer
为服务器设置进程信号处理器
创建共享对象
打开服务器的监听端口,并为监听套接字关联连接应答事件处理器,等待服务器正式运行时接受客户端的连接
为serverCron函数创建时间事件,等待服务器正式运行时执行serverCron函数
如果AOF持久化功能已经打开,那么打开现有的AOF文件,如果AOF文件不存在,创建并打开新的AOF文件
初始化服务器的后台I/O模块,为将来IO操作做好准备
还原数据库状态
执行事件循环
多机数据库的实现
复制
旧版复制功能的实现
同步
SYNC命令
从服务器向主服务器发送SYNC命令
收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令
当主服务器的BGSAVE命令执行完毕时,主服务器会将生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态
命令传播
旧版复制功能的缺陷
初次复制
断线后重复制
新版复制功能的实现
完整重同步
部分重同步
同步过程
部分重同步的实现
复制偏移量
复制积压缓冲区
构造
从服务器重连
如果offset偏移量之后的数据(也即是offset+1开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作
如果offset偏移量之后的数据已经不存在于复制积压缓冲区里面,那么主服务器将对从服务器执行完整的重同步操作
服务器运行ID
如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作
如果从服务器保存的运行ID和当前连接的主服务器的运行ID不相同,那么说明断线前后的主服务器不一致,主服务器将对从服务器执行完整的重同步
PSYNC命令的实现
调用方法
如果重服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步
如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时向主服务器发送PSYNC <runid> <offset>命令:其中runidid是上次复制的主服务器运行id,offset是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数判断应该U对从服务器执行哪个同步操作
主服务器回复
+FULLRESYNC <runid> <offset>
+CONTINUE
-ERR
复制的实现
设置主服务器的地址和端口
建立套接字连接
从服务器
主服务器
发送PING命令
作用
PING命令检查套接字的读写是否正常
PING命令检查主服务器能否正常处理命令请求
回复
主服务器处理完后回复命令,但从服务器不能再规定时间内读到,表示主从之间网络连接不好,不能执行后续的复制工作。从服务器会断开并重新创建连向主服务器的套接字
主服务器返回错误,表示主服务器暂时没办法处理从服务器的命令请求,不能继续执行复制工作的后续步骤,从服务器断开并重新创建连向主服务器的套接字
从服务器读取到PONG回复,表示一切正常,可以复制
身份验证
是否验证
从服务器设置了masterauth选项,进行身份验证
没设置,不验证
身份验证阶段遇到的情况
主服务器没设置requirepass,从服务器没设置masterauth,那么主服务器继续执行从服务器的命令,复制工作继续
从服务器通过AUTH发送的密码和主服务器requirepass选项设置的密码相同,复制继续,不同不可复制
主服务器设置requirepass,从服务器没设置masterauth,主服务器返回NOAUTH错误。
主服务器没设置requirepass,从服务器设置了masterauth,主服务器返回no password is set错误
发送端口信息
属性
同步
完整重同步时,主服务器需要成为从服务器的客户端才能将保存在缓冲区内的写命令发送给从服务器执行
部分重同步时,主服务器需要成为从服务器的客户端,才能将保存在复制积压缓冲区里面的命令发送给从客户端
命令传播
心跳检测
检测主从服务器的网络连接状态
辅助实现min-slaves选项
检测命令丢失
Sentinel
启动并初始化Sentinel
初始化服务器
使用Sentinel专用代码
初始化Sentinel状态
初始化Sentinel状态的masters属性
sentinelRedisInstance
结构
实例
创建连向主服务器的网络连接
被监控的服务器
命令连接,专门用于向主服务器发送命令,并接收命令回复
订阅连接,专门用于订阅主服务器的__sentinel_:hello频道
获取主服务器信息
INFO命令回复
关于主服务器本身的信息,包括run_id域记录的服务器运行id,以及role域记录的服务器角色
关于主服务器属下所有从服务器的信息,每个从服务器由slave字符串开头,记录了ip,port。根据这个,Sentinel可自动发现从服务器
Sentinel信息更新
主服务器
从服务器
字典的键是由Sentinel自动设置的从服务器名字,格式为ip:port
字典的值是从服务器对应的实例结构
获取从服务器信息
INFO命令
实例结构
向主服务器和从服务器发送信息
s_
m_
接收来自主服务器和从服务器的频道信息
向服务器发送信息
更新sentinels字典
sentinels字典的键是其中一个Sentinel的名字,格式为ip:port
sentinels字典的值是键所对应Sentinel的实例结构
接收其他Sentinel发来的信息时
接收其他Sentinel发来的信息时,从信息中提取源Sentinel的IP地址、端口号,运行ID和配置纪元
提取源Sentinel正在监视的主服务器的名字、IP地址、端口号和配置纪元
创建连向其他Sentinel的命令连接
检测主观下线状态
主观下线
检查客观下线
发送SENTINEL is-master-down-by-addr命令
接收SENTINEL is-master-down-by-addr命令
接收SENTINEL is-master-down-by-addr命令的回复
选举领头Sentinel
所有在线Sentinel都有被选为领头Sentinel的资格
每次选举Sentinel之后,不论是否成功,所有Sentinel的配置纪元都会自增。配置纪元就是个计数器
在同一个配置纪元里,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改
每个发现主服务器进入客观下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel
源Sentinel向目标Sentinel发送SENTINEL is-master-down-by-addr命令,runid是源运行ID,表示要求目标Sentinel将前者设置为后者的局部领头Sentinel
Sentinel设置局部领头Sentinel规则是先到先得,最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,之后接收到的所有设置要求都被目标Sentinel拒绝
目标Sentinel接收到SENTINEL is-master-down-by-addr命令之后,向源Sentinel返回命令,回复中的leader_runid和leader_epoch参数分别记录了目标Sentinel的局部领头Sentinel的运行ID和配置纪元
源Sentinel接收到目标Sentinel返回的命令回复后,会检查回复中的leader_epoch参数的值和自己的配置纪元是否相同,如果相同继续取出leader_runid参数,如果该参数和源Sentinel的运行ID一致,那么表示目标Sentinel将源Sentinel设置成了局部领头Sentinel
如果某个Sentinel被半数以上的Sentinel设置成了局部Sentinel,那么这个Sentinel称为领头Sentinel
在一个配置纪元里,只可能出现一个领头Sentinel
如果给定时限内,没有一个Sentinel被选举称为领头Sentinel,那么各个Sentinel将在一段时间之后再次进行选举,直到选出领头Sentinel为止
故障转移
选出新的主服务器
挑选规则
删除列表中处于下线或者断线状态的从服务器
删除列表中所有最近五秒内没有回复过领头Sentinel的INFO命令的从服务器,保证最近成功进行过通信
删除所有与已下线主服务器连接断开超过down-after-milliseconds*10毫秒的从服务器
根据从服务器的优先级,对列表剩余的从服务器进行排序
优先级相同情况下,按照复制偏移量排序,偏移量相同,按照ID排序
升级成功
修改从服务器的复制目标
修改成功
将旧的主服务器变为从服务器
集群
节点
启动节点
集群数据结构
每个节点都保存一个clusterState结构
clusterNode结构保存了一个节点的当前状态
clusterLink
CLUSTER MEET命令的实现
节点A会为节点B创建一个clusterNode结构,并添加到自己的clusterState.nodes字典里面
节点A根据CLUSTER MEET命令给定的IP:PORT,向节点B发送一条MEET消息
节点B收到A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面
节点B向节点A返回一条PONG消息
节点A将接收到节点B返回的PONG消息,通过这条PONG消息节点A知道B成功接收到了自己发送的MEET消息
节点A向节点B返回一条PING消息
节点B收到节点A发送的PING消息,通过PING消息节点B知道节点A已经收到PONG消息,握手完成
节点A将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与B握手,最终,经过一段时间后,节点B会被集群中所有节点认识
槽指派
记录节点的槽指派信息
slots
numslots
传播节点的槽指派信息
记录集群所有槽的指派信息
CLUSTER ADDSLOTS命令的实现
伪代码
初始状态
在集群中执行命令
计算属于哪个槽
判断槽是否由当前节点负责处理
MOVED错误
节点数据库的实现
数据库
slots_to_keys
重新分片
重新分片的实现原理
reids-trib对目标节点发送CLUSTER SETSLOT<slot> IMPORTING <sourct_id>命令,让目标节点准备好从源节点导入属于槽slot的键值对
redis-trib对源节点发送CLUSTER SETSLOT <slot> MIGRATING <target_id>命令,让源节点准备好将属于槽slot的键值对迁移到目标节点
redis-trib向源节点发送CLUSTER GETKEYSINSLOT <slot> <count>命令,获得最多count个属于槽slot的键值对的键名
对于获得的键名,redis-trib向源节点发送一个MIGRANTE <target_ip> <target_port> <key_name> 0 <timeout>命令,将被选中的键原子地从源节点迁移至目标节点
重复3和4,直到源节点保存的所有属于槽slot的键值对都被迁移到目标节点为止
redis-trib向集群中的任意一个节点发送CLUSTER SETSLOT<slot> NODE <target_id>命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点
ASK错误
CLUSTER SETSLOT IMPORTING命令的实现
CLUSTER SETSLOT MIGRANTING命令的实现
ASK错误
ASKING命令
ASK错误和MOVED错误的区别
MOVED错误代表槽的负责权已经从一个节点转移到另一个节点
ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施
复制与故障转移
设置从节点
接收到该命令的节点首先会在自己的clusterState.nodes字典中找到node_id所对应节点的clusterNode结构,并将自己的clusterState.myself.slaveof指针指向这个结构,以此来记录这个节点正在复制的主节点
节点会修改自己在clusterState.myself.flags中的属性,关闭原本的REDIS_NODE_MASTER标识,打开REDIS_NODE_SLAVEb标识,表示这个节点已经由原来的主节点变成了从节点
根据clusterState.myself.slaveof指向的clusterNode结构所保存的IP:PORT,对主节点进行复制。因为节点的复制功能和单机Redis服务器的复制功能使用了相同的代码,所以让主节点复制主节点相当于向从节点发送命令SLAVEOF <master_ip> <master_port>
一个节点称为从节点,并开始复制某个主节点这一信息会通过消息发送给集群中的其他节点,最终集群中的所有节点都会知道某个从节点正在复制某个主节点
集群中的所有节点都会在代表主节点的clusterNode结构的slaves属性和numslaves属性中记录正在复制这个主节点的从节点名单
故障检测
疑似下线
集群中的各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息
下线报告
已下线
故障转移
复制下线主节点的所有从节点里面,会有一个从节点被选中
被选中的从节点会执行SLAVEOF no one命令,称为新的主节点
新的主节点会将下线主节点的槽指派给自己
新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽
新的主节点可是接收和自己复杂的处理的槽有关的命令请求,故障转移完成
选举新的主节点
集群的配置纪元是一个自增计数器,初始值为0
当集群中的某个节点开始一次故障转移时,集群的配置纪元的值加一
对于每个配置纪元,集群中负责处理槽的每个主节点都有一次投票的机会,第一个向主节点要求投票的从节点将会获得主节点的投票
当从节点发现自己正在复制的主节点进入已下线状态时,从节点会向集群广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息,并且具有投票权的主节点向这个从节点投票
如果一个主节点具有投票权(正在负责处理槽),并且这个主节点尚未投票个其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点称为新的主节点
每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持
如果集群中有N个具有投票权的主节点,当一个从节点收集到大于等于N/2+1张支持票时,这个从节点会当选为新的主节点
每个配置纪元中,只有一个从节点获得票数>=N/2+1
如果在一个配置纪元中没有从节点收到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点
消息
节点发送消息的种类
MEET消息
PING消息
PONG消息
FAIL消息
PUBLISH消息
消息头
clusterMsg
clusterMsgData
MEET,PING,PONG消息的实现
clusterMsgDataGossip
接收者接到
被选中节点不存在于接收者的已知节点列表,接收者根据记录中的IP:PORT,与被选中节点进行握手
否则,更新被选中节点的clusterNode结构
FAIL消息的实现
clusterMsgDataFail
PUBLISH消息的实现
clusterMsgDataPublish
bulk_data
独立功能的实现
发布与订阅
频道的订阅与退订
订阅频道
过程
退订频道
模式的订阅与退订
订阅模式
新建一个pubsubPattern结构,将结构的pattern属性设置为被订阅的模式,client属性设置为订阅模式的客户端
将pubsubPattern结构添加到pubsub_patterns链表的尾表
退订模式
发送消息
将消息发送给频道订阅者
将消息发送给模式订阅者
查看订阅信息
PUBSUB CHANNELS
PUBSUB NUMSUB
PUBSUB NUMPAT
事务
0 条评论
下一页