持久化FileTxnLog

简介: 本文深入分析Zookeeper持久化机制,重点解读TxnLog与FileTxnLog源码。涵盖事务日志结构、append追加、truncate截断、commit提交等核心操作,解析日志格式、文件预分配及zxid管理,揭示数据存储与恢复原理。

一、前言  前一篇已经分析了序列化,这篇接着分析Zookeeper的持久化过程源码,持久化对于数据的存储至关重要,下面进行详细分析。二、持久化总体框架  持久化的类主要在包org.apache.zookeeper.server.persistence下,此次也主要是对其下的类进行分析,其包下总体的类结构如下图所示。  

  · TxnLog,接口类型,读取事务性日志的接口。  · FileTxnLog,实现TxnLog接口,添加了访问该事务性日志的API。  · Snapshot,接口类型,持久层快照接口。  · FileSnap,实现Snapshot接口,负责存储、序列化、反序列化、访问快照。  · FileTxnSnapLog,封装了TxnLog和SnapShot。  · Util,工具类,提供持久化所需的API。  下面先来分析TxnLog和FileTxnLog的源码。三、TxnLog源码分析  TxnLog是接口,规定了对日志的响应操作。其中,TxnLog除了提供读写事务日志的API外,还提供了一个用于读取日志的迭代器接口TxnIterator。四、FileTxnLog源码分析  对于LogFile而言,其格式可分为如下三部分  LogFile:    FileHeader TxnList ZeroPad  FileHeader格式如下    FileHeader: {    magic 4bytes (ZKLG)    version 4bytes    dbid 8bytes  }  TxnList格式如下  TxnList:    Txn || Txn TxnList  Txn格式如下  Txn:    checksum Txnlen TxnHeader Record 0x42  Txnlen格式如下  Txnlen:    len 4bytes  TxnHeader格式如下  TxnHeader: {    sessionid 8bytes    cxid 4bytes      zxid 8bytes    time 8bytes    type 4bytes  }  ZeroPad格式如下  ZeroPad:    0 padded to EOF (filled during preallocation stage)  了解LogFile的格式对于理解源码会有很大的帮助。4.1 属性 4.2. 核心函数 1. append函数说明:append函数主要用做向事务日志中添加一个条目,其大体步骤如下  ① 检查TxnHeader是否为空,若不为空,则进入②,否则,直接返回false  ② 检查logStream是否为空(初始化为空),若不为空,则进入③,否则,进入⑤  ③ 初始化写数据相关的流和FileHeader,并序列化FileHeader至指定文件,进入④  ④ 强制刷新(保证数据存到磁盘),并获取当前写入数据的大小。进入⑤  ⑤ 填充数据,填充0,进入⑥  ⑥ 将事务头和事务序列化成ByteBuffer(使用Util.marshallTxnEntry函数),进入⑦  ⑦ 使用Checksum算法更新步骤⑥的ByteBuffer。进入⑧  ⑧ 将更新的ByteBuffer写入磁盘文件,返回trueappend间接调用了padLog函数,其源码如下 说明:padLog其主要作用是当文件大小不满64MB时,向文件填充0以达到64MB大小。2. getLogFiles函数 说明:该函数的作用是找出刚刚小于或者等于snapshot的所有log文件。其步骤大致如下。  ① 对所有log文件按照zxid进行升序排序,进入②  ② 遍历所有log文件并记录刚刚小于或等于给定snapshotZxid的log文件的logZxid,进入③    ③ 再次遍历log文件,添加zxid大于等于步骤②中的logZxid的所有log文件,进入④  ④ 转化后返回getLogFiles函数调用了sortDataDir,其源码如下说明:getLogFiles其用于排序log文件,可以选择根据zxid进行升序或降序。getLogFiles函数间接调用了getZxidFromName,其源码如下: 说明:getZxidFromName主要用作从文件名中解析zxid,并且需要从指定的前缀开始。3. getLastLoggedZxid函数 说明:该函数主要用于获取记录在log中的最后一个zxid。其步骤大致如下  ① 获取已排好序的所有log文件,并从最后一个文件中取出zxid作为候选的最大zxid,进入②  ② 新生成FileTxnLog并读取步骤①中zxid之后的所有事务,进入③  ③ 遍历所有事务并提取出相应的zxid,最后返回。其中getLastLoggedZxid调用了read函数,其源码如下 说明:read函数会生成一个FileTxnIterator,其是TxnLog.TxnIterator的子类,之后在FileTxnIterator构造函数中会调用init函数,其源码如下说明:init函数用于进行初始化操作,会根据zxid的不同进行不同的初始化操作,在init函数中会调用goToNextLog函数,其源码如下  说明:goToNextLog表示选取下一个log文件,在init函数中还调用了next函数,其源码如下  说明:next表示将迭代器移动至下一个事务,方便读取,next函数的步骤如下。  ① 读取事务的crcValue值,用于后续的验证,进入②  ② 读取事务,使用CRC32进行更新并与①中的结果进行比对,若不相同,则抛出异常,否则,进入③  ③ 将事务进行反序列化并保存至相应的属性中(如事务头和事务体),会确定具体的事务操作类型。  ④ 在读取过程抛出异常时,会首先关闭流,然后再尝试调用next函数(即进入下一个事务进行读取)。4. commit函数  说明:该函数主要用于提交事务日志至磁盘,其大致步骤如下  ① 若日志流logStream不为空,则强制刷新至磁盘,进入②  ② 遍历需要刷新至磁盘的所有流streamsToFlush并进行刷新,进入③  ③ 判断是否需要强制性同步,如是,则计算每个流的流式时间并在控制台给出警告,进入④  ④ 移除所有流并关闭。5. truncate函数 

Java

运行代码复制代码

public class FileTxnLog implements TxnLog {

   private static final Logger LOG;

 

   // 预分配大小 64M

   static long preAllocSize =  65536 * 1024;

 

   // 魔术数字,默认为1514884167

   public final static int TXNLOG_MAGIC =

       ByteBuffer.wrap("ZKLG".getBytes()).getInt();


   // 版本号

   public final static int VERSION = 2;


   /** Maximum time we allow for elapsed fsync before WARNing */

   // 进行同步时,发出warn之前所能等待的最长时间

   private final static long fsyncWarningThresholdMS;


   // 静态属性,确定Logger、预分配空间大小和最长时间

   static {

       LOG = LoggerFactory.getLogger(FileTxnLog.class);


       String size = System.getProperty("zookeeper.preAllocSize");

       if (size != null) {

           try {

               preAllocSize = Long.parseLong(size) * 1024;

           } catch (NumberFormatException e) {

               LOG.warn(size + " is not a valid value for preAllocSize");

           }

       }

       fsyncWarningThresholdMS = Long.getLong("fsync.warningthresholdms", 1000);

   }

 

   // 最大(新)的zxid

   long lastZxidSeen;

   // 存储数据相关的流

   volatile BufferedOutputStream logStream = null;

   volatile OutputArchive oa;

   volatile FileOutputStream fos = null;


   // log目录文件

   File logDir;

 

   // 是否强制同步

   private final boolean forceSync = !System.getProperty("zookeeper.forceSync", "yes").equals("no");;

 

   // 数据库id

   long dbId;

 

   // 流列表

   private LinkedList<FileOutputStream> streamsToFlush = new LinkedList<FileOutputStream>();

 

   // 当前大小

   long currentSize;

   // 写日志文件

   File logFileWrite = null;

}

23

public boolean truncate(long zxid) throws IOException {

FileTxnIterator itr = null;

try {

// 获取迭代器

itr = new FileTxnIterator(this.logDir, zxid);

PositionInputStream input = itr.inputStream;

long pos = input.getPosition();

// now, truncate at the current position

// 从当前位置开始清空

RandomAccessFile raf = new RandomAccessFile(itr.logFile, "rw");

raf.setLength(pos);

raf.close();

while (itr.goToNextLog()) { // 存在下一个log文件

if (!itr.logFile.delete()) { // 删除

LOG.warn("Unable to truncate {}", itr.logFile);

}

}

} finally {

// 关闭迭代器

close(itr);

}

return true;

}

说明:该函数用于清空大于给定zxid的所有事务日志。五、总结  对于持久化中的TxnLog和FileTxnLog的源码分析就已经完成了,本章节需重点记住:append函数实现日志追加,记录通过事务的crcValue验证,决定是否更新通过getLogFiles获取全部日志文件并排序通过getLastLoggedZxid找到最大的zxid,保证后续函数决定下一个日志文件id通过commit提交,真正生成日志文件通过trancate清空指定事务日志


相关文章
|
SQL Java 数据库连接
JDBC DriverManager 详解
JDBC(Java Database Connectivity)是 Java 标准库中用于与数据库进行交互的 API。它允许 Java 应用程序连接到各种不同的数据库管理系统(DBMS),执行 SQL 查询和更新操作,以及处理数据库事务。在 JDBC 中,DriverManager 是一个关键的类,用于管理数据库驱动程序和建立数据库连接。本文将详细介绍 JDBC DriverManager 的用法,面向基础小白,帮助您快速入门 JDBC 数据库连接。
419 1
|
JavaScript 前端开发 小程序
Vue 3的高颜值UI组件库
Vue 3的高颜值UI组件库
1115 1
|
1月前
|
人工智能 自然语言处理 搜索推荐
2025年热销榜单:头部AI数字人产品推荐
2025年AI数字人迎来爆发,集之互动、Synthesia、Soul Machines三款产品引领行业变革。从智能问答到多语言翻译,再到情感交互,它们在服务、教育、营销等场景展现强大潜力。本文深度解析其技术优势与适用领域,助你精准选型,把握人机交互新机遇。
126 0
|
6月前
|
存储 算法 Windows
【硬盘容量缩水真相】新买的U盘插上那刻我崩溃了:标称128G的存储为何只剩112G?
新买的硬盘或U盘在Windows中显示的容量为何与标称不符?其实并非商家“坑”,而是存储单位换算标准不同所致。本文详解bit、Byte、KB、MB、GB等存储单位的区别,介绍国际单位制(SI)与国际电工委员会(IEC)两种标准,解释为何500GB硬盘在系统中仅显示约465GB。带你从底层原理理解计算机存储单位的换算逻辑,揭开“消失”的容量之谜。
1076 0
【硬盘容量缩水真相】新买的U盘插上那刻我崩溃了:标称128G的存储为何只剩112G?
|
存储 编解码
MATLAB | 科研绘图第二十一期散点密度双柱状图强化版
MATLAB | 科研绘图第二十一期散点密度双柱状图强化版
189 0
|
10月前
|
人工智能 数据挖掘 Linux
DeepSeek满血版大赏:官方得了“MVP”,第三方是“躺赢狗”?
DeepSeek开源了6710亿参数的R1和2360亿参数的V3两大满血版模型,助力第三方AI平台快速提升性能。此举不仅扩大了DeepSeek的技术影响力,还通过数据飞轮效应优化模型,同时为企业级用户提供灵活变现方式。对于大众,这意味着更多选择、更低使用成本和更快技术迭代。尽管第三方平台可能“阉割”或定制功能,但它们将顶级AI带入更多场景,如比亚迪车机、腾讯元宝等,让AI更普及。官方与第三方各有所长:官方提供最强性能,第三方确保稳定体验。最终,DeepSeek与第三方共同推动AI发展,实现技术普惠。
527 2
|
11月前
|
机器学习/深度学习 人工智能 算法
DeepSeek-R1论文细节时间线梳理
中国AI初创公司DeepSeek发布了大语言模型R1,该模型在推理任务上媲美OpenAI的ChatGPT,且训练成本仅600万美元。DeepSeek由杭州对冲基金High-Flyer支持,总部位于杭州和北京。R1基于V3-Base,使用监督微调和强化学习训练,针对硬件限制进行了优化。模型在多语言处理、推理风格等方面表现出色,但存在一些局限性,如法语表现欠佳、偶尔切换语言等。DeepSeek的创新技术包括FP8量化、多头潜在注意力和蒸馏方法,引发了广泛关注和讨论。开源社区正积极尝试复现其结果,但面临训练数据和代码未公开的挑战。DeepSeek的低成本高效训练策略为AI领域带来了新的思考方向。
889 2
|
人工智能 自然语言处理 搜索推荐
销售易、悟空、神州云动CRM:全方位功能解析与优势特色盘点
销售易CRM、悟空CRM和神州云动CRM各自具备独特的产品功能与优势,适用于不同类型的企业。销售易CRM提供移动化、社交化和AI驱动的全流程管理,适合大型企业及跨国公司;悟空CRM以智能数据分析和移动办公支持见长,适合中大型企业;神州云动CRM则凭借灵活定制和多行业适配能力,特别适合大中型企业。企业在选择时应根据自身需求和发展战略,挑选最适合的CRM系统,以实现客户关系管理的最大化效益。
|
搜索推荐 数据挖掘 API
电商发展的强大助推器:淘宝API
淘宝API是阿里巴巴为开发者提供的访问淘宝开放平台(TOP)的接口,连接淘宝电商生态系统与外部应用。它支持商品管理、订单处理、用户信息和营销推广等功能,帮助开发者轻松获取商品信息、处理订单、管理会员和进行数据分析。通过淘宝API,电商企业能提高效率、降低成本、增加收入并优化用户体验。其应用广泛,涵盖电商平台建设、价格比较、市场研究及移动应用开发等领域。未来,淘宝API将融合人工智能、拓展生态合作并加强数据安全,持续推动电商行业发展。
898 34
|
传感器 安全 算法
物联网发布者在数据传输过程中如何防止数据被篡改
在物联网数据传输中,为防止数据被篡改,可采用加密技术、数字签名、数据完整性校验等方法,确保数据的完整性和安全性。