2.持久化FileTxnLog

本文涉及的产品
RDS AI 助手,专业版
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 本文深入分析Zookeeper持久化机制的源码实现,重点解析`TxnLog`与`FileTxnLog`类。涵盖事务日志结构、文件格式、核心方法如append、commit、truncate及日志迭代器实现,揭示数据如何通过日志追加、校验、刷盘与截断保障持久化可靠性,为理解Zookeeper数据存储奠定基础。(238字)

一、前言
  前一篇已经分析了序列化,这篇接着分析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写入磁盘文件,返回true
    append间接调用了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函数  
    Java
    运行代码
    复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public synchronized void commit() throws IOException {
    if (logStream != null) {

     // 强制刷到磁盘
     logStream.flush();
    

    }
    for (FileOutputStream log : streamsToFlush) { // 遍历流

     // 强制刷到磁盘
     log.flush();
     if (forceSync) { // 是否强制同步
         long startSyncNS = System.nanoTime();
    
         log.getChannel().force(false);
         // 计算流式的时间
         long syncElapsedMS =
             TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startSyncNS);
         if (syncElapsedMS > fsyncWarningThresholdMS) { // 大于阈值时则会警告
             LOG.warn("fsync-ing the write ahead log in "
                      + Thread.currentThread().getName()
                      + " took " + syncElapsedMS
                      + "ms which will adversely effect operation latency. "
                      + "See the ZooKeeper troubleshooting guide");
         }
     }
    

    }
    while (streamsToFlush.size() > 1) { // 移除流并关闭

     streamsToFlush.removeFirst().close();
    

    }
    }
    说明:该函数主要用于提交事务日志至磁盘,其大致步骤如下
      ① 若日志流logStream不为空,则强制刷新至磁盘,进入②
      ② 遍历需要刷新至磁盘的所有流streamsToFlush并进行刷新,进入③
      ③ 判断是否需要强制性同步,如是,则计算每个流的流式时间并在控制台给出警告,进入④
      ④ 移除所有流并关闭。

  5. truncate函数 
    Java
    运行代码
    复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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清空指定事务日志
相关文章
|
3月前
|
人工智能 Linux API
OpenClaw Token降本90%实战:阿里云/本地部署+三大核心Skill+免费大模型配置+避坑指南
2026年,OpenClaw(昵称“龙虾”)作为开源AI代理工具的热门选择,在个人效率提升与企业自动化场景中广泛应用。但大量用户在使用过程中面临共同痛点——Token消耗过快,甚至出现“说一句‘你好’消耗数元”的情况,部分用户单日Token成本高达200多美金。这并非工具本身的设计缺陷,而是源于对其运行机制的认知不足与使用方式的不当。本文将深度解析OpenClaw Token高消耗的核心原因,拆解三大降本技巧,补充2026年阿里云及本地MacOS/Linux/Windows11的完整部署流程、阿里云百炼Coding Plan免费大模型API配置方法,并整理部署与使用中的常见问题解答,帮助用户实
1188 1
|
2月前
|
弹性计算 人工智能 搜索推荐
一键部署 Hermes Agent 自进化智能体——阿里云纯免费体验Hermes,基于ECS、百炼和计算巢服务
Hermes Agent 是 Nous Research 开源的自进化智能体框架,具备跨会话持久记忆、自主学习闭环与多平台统一接入能力。支持本地私有部署,数据不出域。阿里云提供 ECS + 百炼 + 计算巢一键免费部署方案,开箱即用。
537 1
|
API
【vue2项目总结】——相关功能
【vue2项目总结】——相关功能
201 0
|
29天前
|
存储 算法 数据库
手游需要什么样的服务器,该关注哪些方面
手游服务器选型需兼顾性能、延迟、架构与安全:高并发承载、BGP多线低延、分区分服或全球同服架构、MySQL+Redis数据库组合、DDoS防护与TLS1.3加密、ELK运维监控及混合云成本优化。(239字)
|
监控 安全 物联网
RFID工地车辆管理
在工地场景中,RFID技术为车辆管理提供智能化解决方案。通过自动识别与权限控制、实时定位与状态监控、任务分配跟踪等功能,提高管理效率和数据准确性,优化资源配置并降低成本。同时,驾驶员资质审查、安全区域限制等措施增强安全性,实现信息化管理,确保施工进度与现场安全。图文源于网络,侵删!
|
机器学习/深度学习 人工智能 自然语言处理
如何通过AIOps将智能注入IT运营
试图更好地监控IT资产的组织正在转向人工智能,以便在性能问题出现之前解决掉,并在感受到负面影响之前自动将其修复。云平台、托管服务提供商和进行数字化转型的组织正开始从一种新兴的IT趋势中获益:使用人工智能驱动的IT运营技术来自动监控和管理其IT产品组合。
446 0
|
14天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
5315 28
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
8天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1054 1