Log实现
接口
append
追加entries到log,返回时新追加的entries可能还没落盘。上层接口使用时,会调用log的sync对象将数据刷入盘。
getLastLogIndex
获取log中最新entry的索引。
getLogStartIndex
获取log中第一条entry的索引。
truncatePrefix
删除给定索引前面的entries。
truncateSuffix
删除给定索引后面的entries。
MemoryLog
特点
数据保存在内存,没持久化,进程退出数据丢失; 速度快。
思路
使用std::deque
队列保存entries,实现各接口。
SimpleFileLog
特点
1个Log文件保存1条Entry,1条Entry只保存在1个Log文件。
思路
每条entry都写入到一个单独的文件。 如果把所有entries写到一个文件,要处理比较复杂的分辨entry,异常退出时entry写入不完整等问题。 一个entry写到一个单独的文件,可以避免上述问题。
另外,用单独的文件保存metadata,metadata包含3部分:LogStartIndex、LastLogIndex和版本号。 每次append操作都会将版本号增1,1个版本对应一次append的多条entries。 有两个metadata文件,奇数版本号保存在一个文件,偶数版本号保存在另外一个文件。
SegmentedLog
特点
1个Log文件保存多条Entry,同时有多个Log文件,每个Log文件保存一部分Entries。
思路
一个Log文件保存多条Entry,同时有多个Log文件,每个Log文件保存一部分Entries。 一个Log文件对应一个Segment,一条Entry在Segment中对应为一条Record。
Log文件分两种类型:openSegmentFile、closeSegmentFile。这两种Log文件的不同之处是openSegmentFile可以继续增加Entry,而closeSegmentFile不能继续增加Entry了。新Log文件一开始是openSegmentFile,当添加了足够多的entry后就将其转换为closeSegmentFile,足够多的标准是Log文件的大小,默认是8MB字节。此外,两种Log文件名的命名规则不同,closeSegmentFile的规则为startIndex-endIndex
,openSegmentFile的规则为open-counter
。
如何从Log文件中读取出Entries。定义记录Entry的格式:checksum、datalen、data。checksum和datalen都是固定大小的,data的大小由datalen指定。根据格式就能够从文件中依次读取出所有的Entry。
对给定的Log文件,如何确定Entry是否完整,例如在异常退出时数据是否完整落盘。每条Entry的数据都会有检验和,读取Entry数据时会进行校验,如果是closeSegmentFile文件,检查不通过则进程直接退出;如果检验不通过的是openSegmentFile文件,丢弃对应的Entry,进程不退出,仍然可以正常工作。