etcd raft 处理流程图系列2-wal的读写
本文仅介绍wal的基本处理,如create、open、close、read等操作。鉴于篇幅原因,下面介绍replayWAL
(启动raft节点时执行)函数涉及的读文件操作:从wal目录中加载snapshot,wal文件的创建,以及读取wal目录中的所有数据(主要是entryType
、stateType
、metadataType
这几类)。
WAL的处理还是比较复杂的可以借鉴的地方也很多。WAL在编码以及flush时使用缓存来提升效率。flush的单位为分页,每页又分为8个section,section的作用是用来检测写入的数据是否被破坏,检测逻辑为:如果某个section中的所有字节都为0,则说明数据遭到破坏,反之则认为数据正常。在isTornEntry
中,主要通过section机制来检测WAL文件中最后一个record是否因为数据破坏而导致json解析或crc校验失败。
创建
下面是wal的create流程,在创建文件事先预分配文件大小(64MB),用于提升性能。wal通过encode()
函数将编码后的数据写入文件,因此需要在对文件执行写操作时加锁,写入的数据以record为单位(record首先被写入缓存,当数据以页为单位对齐时通过flush写入文件)。先计算数据的crc校验码,然后计算record的帧数据。写数据时,先写入帧数据,再写入record。在写入数据(无论是帧数据还是record)时,会以页为单位将数据写入文件,不足一页的数据会暂存在缓存中。帧数据保存了实际的数据大小和pad的数据大小,在读取wal文件时会用到该信息。
wal的文件名由两部分构成:seq和index,前者应该顺序递增的,以保证日志文件的连续性(isValidSeq
会根据seq校验日志文件的连续性)。
加载snapshot
下面是在wal目录中加载snapshot的操作,该操作中用到了上面的帧数据。wal使用decode()
函数进行解码,首先取出在帧数据中解析出record的大小和padBytes的小,然后根据record的大小解码数据,最后根据record的类型采集并返回所有snapshot。
从上面可以看到,wal的encoder用于写文件,因此encoder会关联到当前正在编辑的文件,记录了文件句柄、当前字节偏移以及缓存等信息,一般会选择WAL.locks
中的最后一个元素。而decoder用于读取所有文件,因此关联到多个wal文件,记录了这些文件句柄。
读取所有数据
下图是从wal目录中尝试读取所有信息(如metadata、entries、state)的过程。涉及读取wal目录中的文件信息,以此构建WAL
结构,然后通过生成的decoder来将文件解码为不同类型的数据进行处理。最终返回解码后的数据。需要注意decoder的文件是有序的,可以从源码fileutil.ReadDir
看出来,其对文件名进行了sort.Strings(names)
操作。
此外,在读取文件时,根据文件的读写模式分别进行了处理。读模式下只需读完所有文件,关闭文件并返回结果即可。写模式下文件是加锁的,在decodeRecord
中会读取lastValidOff
(frameSizeBytes + recBytes + padBytes)长度的数据,并将该长度之后的数据归0,防止文件中出现被破坏的数据,由于对文件的修改会改变文件的crc校验,但好在新的record不会立即刷新到文件中(源码中的描述如下),更新文件的encoder,后续通过encoder将数据最终写入文件即可。
// decodeRecord() will return io.EOF if it detects a zero record,
// but this zero record may be followed by non-zero records from
// a torn write. Overwriting some of these non-zero records, but
// not all, will cause CRC errors on WAL open. Since the records
// were never fully synced to disk in the first place, it's safe
// to zero them out to avoid any CRC errors from new writes.
WAL的保存
raftexample的serveChannels
中当接收到node.Ready()
传来的数据时,会对这些数据进行持久化。如下图,首先会保存状态和entry信息,如果locks中最后一个文件(该文件)的内容大于或等于SegmentSizeBytes
时需要切割文件。
在切分文件时,将已有的数据同步到文件中,后面的操作就是新建一个文件。新文件来自于WAL.fp
是在重命名文件或创建文件时创建的,可以看到一个WAL只有一个fp
,用于在切割文件时提高效率。
首先在新文件中记录当前的crc,然后写入metadata
和state
信息,并重新计算crc,在读取时可以校验到此为止的crc。新文件作为WAL.locks中的最后一个文件。
etcd raft 处理流程图系列2-wal的读写的更多相关文章
- etcd raft 处理流程图系列1-raftexample
最近在看raft相关的代码和实现,发现etcd的raft模块在实现上还是比较灵活的,但缺点就是需要用户实现比较多的功能,如存储和网络等,同时带来的优点就是不会对用户的存储和传输作限制.网上对该模块的描 ...
- etcd raft 处理流程图系列3-wal的存储和运行
存储和节点的创建 raftexample中的存储其实有两种,一个是通过raft.NewMemoryStorage()进行创建的raft.raftStorage,关联到单个raft节点,另一个是通过ne ...
- etcd raft 处理流程图系列2-transport
本章给出了raftexample中使用的传输层代码,补全了上一节中传输层与raft节点(raft server和channel server)的交互细节.下图中流程的核心在于传输层中的streamRt ...
- etcd raft library设计原理和使用
早在2013年11月份,在raft论文还只能在网上下载到草稿版时,我曾经写过一篇blog对其进行简要分析.4年过去了,各种raft协议的讲解铺天盖地,raft也确实得到了广泛的应用.其中最知名的应用莫 ...
- 彻底搞懂etcd raft选举、数据同步
etcd raft选举机制 etcd 是一个分布式的k/V存储系统.核心使用了RAFT分布式一致性协议.一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服 ...
- etcd raft如何实现成员变更
成员变更在一致性协议里稍复杂一些,由于不同的成员不可能在同一时刻从旧成员组切换至新成员组,所以可能出现两个不相交的majority,从而导致同一个term出现两个leader,进而导致同一个index ...
- etcd raft library
https://github.com/coreos/etcd/tree/master/raft import "github.com/coreos/etcd/raft" ----- ...
- etcd raft如何实现leadership transfer
leadership transfer可以把raft group中的leader身份转给其中一个follower.这个功能可以用来做负载均衡,比如可以把leader放在性能更好的机器或者离客户端更近的 ...
- etcd raft如何实现Linearizable Read
Linearizable Read通俗来讲,就是读请求需要读到最新的已经commit的数据,不会读到老数据. 对于使用raft协议来保证多副本强一致的系统中,读写请求都可以通过走一次raft协议来满足 ...
随机推荐
- excel用函数去掉单元格内容中的括号,并只保留单元格里面的内容
1.substitute(需要执行替换操作的单元格,需要替换的字符,替换后的字符,有多个需要替换的字符可以指定替换的第几个) 例如:aab--substitute("aab",&q ...
- kafka高性能吞吐原因
1. 简单回顾 Kafka作为时下最流行的开源消息系统,被广泛地应用在数据缓冲.异步通信.汇集日志.系统解耦等方面.相比较于RocketMQ等其他常见消息系统,Kafka在保障了大部分功能特性的同时, ...
- Vue3全家桶升级指南一composition API
1.setup() vue3中的composition API中最重要的就是setup方法了,相当于组件的入口,所有的composition API都必须放到setup()中的使用. setup是在组 ...
- Linux云计算-02_CentOS Linux 7.X系统管理
Linux系统安装完毕,需要对Linux系统进行管理和维护,让Linux服务器能真正应用于企业中. 本章介绍Linux系统32位与64位区别.内核命名规则.引导原理.启动流程.TCP/IP协议概述.I ...
- flex mx:TabNavigator进行选项卡切换,需要进行交互时。发生Error #1009错误
当需要进行 mx:TabNavigator选项卡进行切换时,需要进行交互,然后却报了"TypeError: Error #1009: 无法访问空对象引用的属性或方法."错误,产生这 ...
- 基于Redis的分布式锁设计
前言 基于Redis的分布式锁实现,原理很简单嘛:检测一下Key是否存在,不存在则Set Key,加锁成功,存在则加锁失败.对吗?这么简单吗? 如果你真这么想,那么你真的需要好好听我讲一下了.接下来, ...
- QQ邮箱获取授权码方法
1.登录QQ邮箱,点击"设置" 2.点击"账户" 3.开启POP3/SMTP服务 4.点击"生成授权码" 5.完成验证后,即可生成授权码 P ...
- CRM客户管理系统哪个好用
当企业管理者在进行CRM系统的选型时,面对搜索引擎上五花八门的结果和各式各样的广告,一定会有这样的疑惑:CRM客户管理系统到底哪个好用?抛开网上那些为了广告效果而"夸张"出的优点, ...
- 1shell变量的作用域
Shell 局部变量 Shell 全局变量 shell全局变量的易错点 linux shell中./a.sh , sh a.sh , source a.sh, . ./a.sh的区别 Shell 环境 ...
- 关于mysql binlog二进制
binlog 在mysql中,当发生数据变更时,都会将变更数据的语句,通过二进制形式,存储到binlog日志文件中. 通过binlog文件,你可以查看mysql一段时间内,对数据库的所有改动. 也可以 ...