本文仅介绍wal的基本处理,如create、open、close、read等操作。鉴于篇幅原因,下面介绍replayWAL(启动raft节点时执行)函数涉及的读文件操作:从wal目录中加载snapshot,wal文件的创建,以及读取wal目录中的所有数据(主要是entryTypestateTypemetadataType这几类)。

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,然后写入metadatastate信息,并重新计算crc,在读取时可以校验到此为止的crc。新文件作为WAL.locks中的最后一个文件。

原图连接

etcd raft 处理流程图系列2-wal的读写的更多相关文章

  1. etcd raft 处理流程图系列1-raftexample

    最近在看raft相关的代码和实现,发现etcd的raft模块在实现上还是比较灵活的,但缺点就是需要用户实现比较多的功能,如存储和网络等,同时带来的优点就是不会对用户的存储和传输作限制.网上对该模块的描 ...

  2. etcd raft 处理流程图系列3-wal的存储和运行

    存储和节点的创建 raftexample中的存储其实有两种,一个是通过raft.NewMemoryStorage()进行创建的raft.raftStorage,关联到单个raft节点,另一个是通过ne ...

  3. etcd raft 处理流程图系列2-transport

    本章给出了raftexample中使用的传输层代码,补全了上一节中传输层与raft节点(raft server和channel server)的交互细节.下图中流程的核心在于传输层中的streamRt ...

  4. etcd raft library设计原理和使用

    早在2013年11月份,在raft论文还只能在网上下载到草稿版时,我曾经写过一篇blog对其进行简要分析.4年过去了,各种raft协议的讲解铺天盖地,raft也确实得到了广泛的应用.其中最知名的应用莫 ...

  5. 彻底搞懂etcd raft选举、数据同步

    etcd raft选举机制 etcd 是一个分布式的k/V存储系统.核心使用了RAFT分布式一致性协议.一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服 ...

  6. etcd raft如何实现成员变更

    成员变更在一致性协议里稍复杂一些,由于不同的成员不可能在同一时刻从旧成员组切换至新成员组,所以可能出现两个不相交的majority,从而导致同一个term出现两个leader,进而导致同一个index ...

  7. etcd raft library

    https://github.com/coreos/etcd/tree/master/raft import "github.com/coreos/etcd/raft" ----- ...

  8. etcd raft如何实现leadership transfer

    leadership transfer可以把raft group中的leader身份转给其中一个follower.这个功能可以用来做负载均衡,比如可以把leader放在性能更好的机器或者离客户端更近的 ...

  9. etcd raft如何实现Linearizable Read

    Linearizable Read通俗来讲,就是读请求需要读到最新的已经commit的数据,不会读到老数据. 对于使用raft协议来保证多副本强一致的系统中,读写请求都可以通过走一次raft协议来满足 ...

随机推荐

  1. Doris开发手记2:用SIMD指令优化存储层的热点代码

    最近一直在进行Doris的向量化计算引擎的开发工作,在进行CPU热点排查时,发现了存储层上出现的CPU热点问题.于是尝试通过SIMD的指令优化了这部分的CPU热点代码,取得了较好的性能优化效果.借用本 ...

  2. MySql:Windows10安装mysql-8.0.18-winx64步骤

    步骤: 1. 首先在安装的mysql目录下创建my.ini文件 (深坑)注意:my.ini必须保存为ANSI格式!!! 可以先创建一个my.txt的文件,然后另存为ANSI格式的文件! my.ini内 ...

  3. Java基础00-多态19

    1. 多态 多态 1.1 多态概述 代码示例: 动物类: public class Animal { public void eat(){ System.out.println("动物吃东西 ...

  4. clickhouse分布式集群

    一.环境准备: 主机 系统 应用 ip ckh-01 centos 8 jdk,zookeeper,clickhouse 192.168.205.190 ckh-02 centos 8 jdk,zoo ...

  5. IDEA 生成类注释和方法注释

    目录 一.生成类注释-01 1.1.生成类注解模板 1.2.把模板设置到IDEA中 1.3.效果图 二.生成类注释-02 2.1.生成类注释模板 2.2.把模板设置到IDEA中 2.3.效果图 2.4 ...

  6. 学习总结 NCRE二级和三级

    NCRE二级C语言 证书 考试感想 2016年考的认证,5年过去了,"光阴荏苒真容易".趁着心有余力有余的时候,把一些个人的体会分享给大家,希望后来人能平稳前行. Windows ...

  7. Java项目调试技巧及版本控制

    开发项目中,调试是必不可少的. 本篇博客从以下4个方面介绍项目调试技巧: 响应状态码的含义 服务端断点调试技巧 客户端断点调试技巧 设置日志级别,并将日志输出到不同的终端 以及,最后简单的介绍了一下g ...

  8. 【LeetCode】59.螺旋矩阵II

    59.螺旋矩阵II 知识点:数组: 题目描述 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix . 示例 输入:n = 3 ...

  9. 【LeetCode】203.移除链表元素

    203.移除链表元素 知识点:链表:双指针 题目描述 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 . 示例 ...

  10. Requests方法 --- post 请求body的四种类型

    常见的 post 提交数据类型有四种: 1.第一种:application/json:这是最常见的 json 格式,也是非常友好的深受小伙伴喜欢的一种,如下{"input1":&q ...