这本书是一本不多的好书,把Redis设计和实现的细节讲得很清楚,虽然里面有一些细节算法如raft选举算法和variable swar
循环机制
redis服务器是事件驱动程序,服务器会处理两类事件
- 文件事件。套接字操作
- 时间时间。链表保存,非顺序链表,遍历查找
- 定时时间
- 定期时间
处理总逻辑
def main():
#初始化服务器
init_server()
#一直处理事件,知道服务器关闭
while server_is_not_shutdown():
asProcessEvents()
#服务器关闭
clean_server()
事件的调度和执行
def aeProcessEvents():
#获取到达时间离当前时间事件
time_event = aeSearchNearestTimer()
#计算最近的时间事件距离到达还有多少毫秒
remaind_ms = time_event.when - unix_ts_now()
#根据remaind_ms的值,创建timeval
timeval = create_timeval_with_ms(remaind_ms)
if timeval < 0:
remaind_ms = 0
#阻塞并期待文件事件产生,最大阻塞时间由传入的timeval结构决定
#如果remaind_ms的值为0,那么aeApiPoll调用后立刻返回,不阻塞
aeApiPoll(timeval)
#处理所有已经产生的文件事件
processFileEvent()
#处理所有已经到达的时间时间
processTimeEvent()
4种网络模型
- 阻塞 I/O(blocking IO)
- 非阻塞 I/O(nonblocking IO)
- I/O 多路复用( IO multiplexing)
- 异步 I/O(asynchronous IO)
Redis采用的是I/O多路复用
集群
Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移方案
节点
#连接各个节点
cluster meet <ip> <port>
#显示当前集群节点
cluster nodes
//3537963976396397 :0 myself, master - 0 0 0 connected
数据结构
- clusterNode每个节点都会为集群内所有的一个clusterNode结构
- ctime 创建的时间
- name 节点的名字
- flags 表示主从,上线下线
- configEpoch 配置纪元,用于故障转移
- ip 节点ip
- port
- clusterLink link 保存链接节点所需的有关信息
- unsigned char数组 slots长度为16384/8
- clusterLink
- ctime 连接的创建时间
- fd 描述符int类型
- sndbuf 输出缓冲区
- rcvbuf 输入缓冲区
- ClusterNode nodes 与这个链接相关的节点
- clusterState每个节点保存一个
- myself,指向一个clusterNode
- currentEpoch
- state 上线还是下线
- size
- nodes 集群节点名单
- slots 16384数组
16384个槽,及槽指派
槽指派
cluster addslots 0 1 2 3 4
槽指派信息,保存在clusterState和clusterNode中,不同处理场景
MOVED命令
重新分片及ASK错误
故障检测流程 - 通过ping消息,设置节点疑似下线
- 集群中各个节点会通过互相发送消息交换信息,如在线,PFAIL,FAIL
- 当收到疑似下线(PFAIL>一般),发送FAIL
- 其他节点立刻表示FAIL
故障转移及选举算法 - 发现FAIL
- 通过Raft在从节点中选择一个
- 被选中的执行slaveof no one命令
- 新节点指派FAIL节点所有的槽给自己
- 发送pong消息
发布和订阅
服务器状态在pubsub channels字典里保存所有频道的订阅关系
服务器会在pubsub patterns链表保存所有模式的的订阅关系
publish命令执行两个动作
- 将消息发送给channel频道的所有订阅者
- 遍历patterns链表,对匹配的订阅者发送
事务
事务入队
watch命令通过watched——keys字典实现,将key对应到客户端,如果一个key被修改了,在客户端的表示里加REDIS-DIRTY-CAS
二进制维数组
getbit
setbit
bitcount
bittop
汉明重量的计算(Variabel-precision)
统计一个位数组非0二进制位的数量
#以32位为例
uint32_t swar(uint32_t t) {
#将该二进制以两个一组进行分组,各组的十进制表示该组的汉明重量
i = (i & 0x55555555) + ((i >> 1) & 0x55555555)
#将得到的结果以四个一组进行分组,各组的十进制表示该组的汉明重量
i = (i & 0x33333333) + ((i >> 1) & 0x33333333)
#将得到的结果以八个一组进行分组,各组的十进制表示该组的汉明重量
i = (i & 0x0F0F0F0F) + ((i >> 1) & 0x0F0F0F0F)
#计算出汉明质量并保存在最高为八位,然后转移到最低8
}