<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>MiniDFS - 系列 - 星河拾贝录</title><link>https://blog.liubang.cc/series/minidfs/</link><description>MiniDFS - 系列 - 星河拾贝录</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><managingEditor>it.liubang@gmail.com (liubang)</managingEditor><webMaster>it.liubang@gmail.com (liubang)</webMaster><copyright>Copyright © 2019-2026 LiuBang. All Rights Reserved.</copyright><lastBuildDate>Sun, 24 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.liubang.cc/series/minidfs/" rel="self" type="application/rss+xml"/><item><title>MiniDFS 01: 架构与协议设计</title><link>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-01-architecture/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><author><name>liubang</name></author><guid>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-01-architecture/</guid><description><![CDATA[<p>MiniDFS 是一个用 C++20 从零实现的简化版分布式文件系统。它不追求功能完整覆盖，而是聚焦分布式文件系统最核心的几个问题——元数据管理、数据分块与 Pipeline 复制、副本放置与容错——给出一个可以实际运行的实现，并在过程中深入理解每个设计决策背后的 tradeoff。</p>
<p>这篇文章是系列的入口。我会先讲为什么要造这个项目、它和 HDFS 的关系，然后给出整体架构，最后完整走一遍&quot;写入一个文件&quot;的端到端链路，让读者对后续每篇文章的位置有一个全局认知。</p>
<h2 id="为什么要自己实现一个分布式文件系统" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e8%87%aa%e5%b7%b1%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa%e5%88%86%e5%b8%83%e5%bc%8f%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f" class="header-mark"></a>为什么要自己实现一个分布式文件系统</h2><p>学习分布式系统最有效的方式是亲手实现一遍。阅读论文能理解设计意图，但只有真正写出能跑的代码，才会遇到论文中一笔带过的工程问题——事务边界怎么划、并发控制在哪一层做、心跳超时设多长才合理。</p>
<p>HDFS 的源码是 Java 实现，经过十余年演进，代码量庞大（核心模块超过 30 万行），HA、Federation、Erasure Coding 等高级特性与核心逻辑交织在一起，阅读门槛极高。MiniDFS 的目标是一个<strong>最小可运行闭环</strong>：保留 HDFS 的核心架构决策，砍掉所有非本质复杂度，把精力集中在真正重要的设计问题上。</p>
<h2 id="minidfs-vs-hdfs保留了什么砍掉了什么" class="headerLink">
    <a href="#minidfs-vs-hdfs%e4%bf%9d%e7%95%99%e4%ba%86%e4%bb%80%e4%b9%88%e7%a0%8d%e6%8e%89%e4%ba%86%e4%bb%80%e4%b9%88" class="header-mark"></a>MiniDFS vs HDFS：保留了什么，砍掉了什么</h2><p>MiniDFS 的设计哲学是「保留骨架，简化实现」。下面从两个维度做对比。</p>
<p><strong>保留的核心设计：</strong></p>
<p>单 NameNode + 多 DataNode 的 Master/Worker 架构；Block 分块存储 + Pipeline 链式复制；Rack-aware 副本放置策略；Lease 机制实现写互斥；Heartbeat + BlockReport 的注册与上报机制；Block 与 Replica 的双层状态机管理（<code>kAllocating → kCommitted → kDeleted</code> 和 <code>kWriting → kFinalized → kCorrupt → kDeleted</code>）。</p>
<p><strong>砍掉的特性：</strong></p>
<p>HA（Secondary NameNode / JournalNode / ZKFC）、Federation（多 Namespace）、Snapshot / Quota / ACL、Append / Truncate、Erasure Coding、Short-circuit Local Read、HDFS Balancer / Mover。这些特性各自重要，但它们本质上是在核心架构之上的增量演进，不影响对基本原理的理解。</p>]]></description></item><item><title>MiniDFS 02: 元数据持久化</title><link>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-02-metadata/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><author><name>liubang</name></author><guid>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-02-metadata/</guid><description><![CDATA[<p>NameNode 的核心职责是管理元数据。HDFS 用 EditLog + FsImage 实现持久化——这套方案在生产中经受了海量验证，但它的复杂度（checkpoint 合并、HA 下的 JournalNode 同步、启动时重放 EditLog）对一个教学项目来说是过度的。MiniDFS 选择了一条不同的路：直接用 MySQL 做元数据后端。</p>
<p>这篇文章深入讲解这个设计选择的 tradeoff，以及在 MySQL 之上构建的三层关键机制：连接池 RAII 封装、事务绑定、ID 原子分配。</p>
<p><figure><a class="lightgallery" href="/images/minidfs/metadata-layer.svg" title="MiniDFS 元数据层架构" data-thumbnail="/images/minidfs/metadata-layer.svg" data-sub-html="<h2>元数据层整体架构：从 NameNode Manager 到 MySQL 的分层设计</h2><p>MiniDFS 元数据层架构</p>"><img  loading="lazy" src='/images/minidfs/metadata-layer.svg'   alt="MiniDFS 元数据层架构"  ></a><figcaption class="image-caption">元数据层整体架构：从 NameNode Manager 到 MySQL 的分层设计</figcaption>
</figure></p>
<h2 id="hdfs-的-editlog--fsimage为什么复杂" class="headerLink">
    <a href="#hdfs-%e7%9a%84-editlog--fsimage%e4%b8%ba%e4%bb%80%e4%b9%88%e5%a4%8d%e6%9d%82" class="header-mark"></a>HDFS 的 EditLog + FsImage：为什么复杂</h2><p>HDFS 的元数据持久化遵循经典的 WAL（Write-Ahead Log）思路。每次元数据变更——创建文件、追加 block、修改权限——都以一条 EditLog record 追加写入磁盘。FsImage 则是某一时刻的全量 namespace 快照。NameNode 启动时加载最近的 FsImage，然后顺序重放此后的所有 EditLog 条目，恢复到最新状态。</p>
<p>这个方案的工程复杂度主要体现在三处。第一是 Checkpoint 过程：SecondaryNameNode（或 HA 架构下的 StandbyNameNode）需要定期将 EditLog 合并进 FsImage 以避免重放时间无限增长，大集群的 FsImage 动辄数十 GB，合并本身就是一个不可忽视的 I/O 密集操作。第二是 HA 方案引入的 JournalNode 集群：Active NameNode 将 EditLog 写入多数派 JournalNode，Standby 从 JournalNode 拉取并重放，保持 namespace 同步——这套机制引入了 Paxos 式的多数派确认、fencing、epoch 管理等分布式一致性的全套复杂度。第三是 EditLog 自身的格式管理：segment 滚动、序列化版本升级、损坏恢复工具。</p>]]></description></item><item><title>MiniDFS 03: Namespace 与 Lease</title><link>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-03-namespace-and-lease/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><author><name>liubang</name></author><guid>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-03-namespace-and-lease/</guid><description><![CDATA[<p>分布式文件系统对用户呈现的是一棵目录树——<code>/data/logs/2024/app.log</code> 这样的路径看起来和本地文件系统没什么区别。但在底层，这棵树的每个节点（inode）是存储在 MySQL 中的一行记录，路径解析是逐级查询，文件创建需要加写锁（Lease）来防止并发冲突。</p>
<p>这篇文章深入 NamespaceManager 和 LeaseManager 的实现，重点讲路径解析的逐级查找、<code>mkdir -p</code> 的事务化实现、递归删除的级联问题，以及 Lease 从分配到过期的完整生命周期。</p>
<p><figure><a class="lightgallery" href="/images/minidfs/namespace-lease.svg" title="Namespace 与 Lease 机制架构" data-thumbnail="/images/minidfs/namespace-lease.svg" data-sub-html="<h2>Namespace 目录树结构与 Lease 状态机：从路径解析到写互斥的全景视图</h2><p>Namespace 与 Lease 机制架构</p>"><img  loading="lazy" src='/images/minidfs/namespace-lease.svg'   alt="Namespace 与 Lease 机制架构"  ></a><figcaption class="image-caption">Namespace 目录树结构与 Lease 状态机：从路径解析到写互斥的全景视图</figcaption>
</figure></p>
<h2 id="inode-数据模型" class="headerLink">
    <a href="#inode-%e6%95%b0%e6%8d%ae%e6%a8%a1%e5%9e%8b" class="header-mark"></a>Inode 数据模型</h2><p>MiniDFS 的目录树由 inode 节点构成。每个 inode 代表一个文件或目录，定义在 <code>types.h</code> 中：</p>
<div class="code-block highlight is-open show-line-numbers  tw:group tw:my-2">
  <div class="
    
    tw:flex 
    tw:flex-row
    tw:flex-1 
    tw:justify-between 
    tw:w-full tw:bg-bgColor-secondary
    ">      
    <button 
      class="
        code-block-button
        tw:mx-2 
        tw:flex
        tw:flex-row
        tw:flex-1"
      aria-hidden="true">
          <span class="tw:group-[.is-open]:rotate-90 tw:transition-[transform] tw:duration-500 tw:ease-in-out tw:print:hidden! tw:w-min tw:h-min tw:my-1 tw:mx-1"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg></span>
          <span class="tw:select-none tw:my-1! tw:block">cpp</span>
      </button>

   <div class="tw:flex">
      <button 
        class="
          line-number-button
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.show-line-numbers]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle line numbers"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z"/></svg></button>

      <button 
        class="
          wrap-code-button
          tw:select-none 
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.is-wrap]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle code wrap"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"/></svg></button>
      
      <button 
        class="
          copy-code-button
          tw:select-none
          tw:mx-2 
          tw:hidden
          tw:group-[.is-open]:block
          tw:hover:text-fgColor-link 
          tw:print:hidden!"
        title="Copy code">
          <span class="copy-icon tw:block"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"/></svg></span>
          <span class="check-icon tw:hidden"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/></svg></span>
      </button>
        
      <button 
        class="
          tw:select-none 
          tw:mx-2 
          tw:block 
          tw:group-[.is-open]:hidden 
          tw:print:hidden!" 
        disabled
        aria-hidden="true"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z"/></svg></button>
    </div>
  </div>
  <pre style="counter-reset: codeblock;" class="tw:block tw:m-0 tw:p-0"><code 
    id="codeblock-id-1" 
    class="
      chroma 
      tw:block! 
      tw:p-0
      tw:m-0
      tw:transition-[max-height] 
      tw:duration-500 
      tw:ease-in-out 
      tw:group-[.is-closed]:max-h-0! 
      tw:group-[.is-wrap]:text-wrap
      tw:overflow-y-hidden
      tw:overflow-x-auto
      tw:scrollbar-thin
      "><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">InodeType</span> <span class="o">:</span> <span class="kt">uint8_t</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kDirectory</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">kFile</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">FileState</span> <span class="o">:</span> <span class="kt">uint8_t</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kNormal</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">kUnderConstruction</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">kDeleted</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Inode</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">inode_id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">InodeType</span> <span class="n">type</span> <span class="o">=</span> <span class="n">InodeType</span><span class="o">::</span><span class="n">kDirectory</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">parent_id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">owner</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">group</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">permission</span> <span class="o">=</span> <span class="n">kDefaultPermission</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">length</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">replication</span> <span class="o">=</span> <span class="n">kDefaultReplication</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">block_size</span> <span class="o">=</span> <span class="n">kDefaultBlockSize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">FileState</span> <span class="n">state</span> <span class="o">=</span> <span class="n">FileState</span><span class="o">::</span><span class="n">kNormal</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">ctime_ms</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">mtime_ms</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">version</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre>
</div>
<p>几个关键设计决策值得展开。首先是 <code>parent_id + name</code> 的组合定位方式。每个 inode 不存储完整路径（如 <code>/data/logs/app.log</code>），而是只存储自己的 <code>name</code>（<code>app.log</code>）加上父目录的 <code>inode_id</code>。这个设计使得 rename 操作只需修改一行记录的 <code>parent_id</code> 和 <code>name</code> 字段，而存储完整路径的方案需要更新这个节点及其所有后代的路径——在深层目录树中这是 O(n) 的代价。子目录查询也很自然：<code>WHERE parent_id = ?</code> 即可列出某目录下的所有直接子节点。</p>]]></description></item><item><title>MiniDFS 04: 写入 Pipeline</title><link>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-04-write-pipeline/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><author><name>liubang</name></author><guid>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-04-write-pipeline/</guid><description><![CDATA[<p>分布式文件系统的写入远比单机复杂——数据要同时落到多个副本上，任何一个环节的失败都需要被检测和处理。HDFS 的经典方案是 Pipeline Replication：Client 只需要把数据发给第一个 DataNode，由 DataNode 链式转发给后续节点，形成一条写入流水线。</p>
<p>这篇文章从一次 <code>put()</code> 调用开始，逐步拆解 Block 分配、目标节点选择、Pipeline 建立与数据传输、两层 CRC32C 校验，以及 chunk 级别的幂等重试设计。</p>
<h2 id="写入请求的完整链路" class="headerLink">
    <a href="#%e5%86%99%e5%85%a5%e8%af%b7%e6%b1%82%e7%9a%84%e5%ae%8c%e6%95%b4%e9%93%be%e8%b7%af" class="header-mark"></a>写入请求的完整链路</h2><p>一次 <code>DfsClient::put(dfs_path, local_path)</code> 调用涵盖五个阶段。首先通过 <code>CreateFile</code> RPC 在 NameNode 创建 inode 并获取 lease（保证写互斥）；接着按 <code>kDefaultBlockSize</code>（128 MB）将本地文件切分成若干 block，对每个 block 执行 AllocateBlock / 多次 WriteBlock / CommitBlock 的循环；最后 <code>CompleteFile</code> 释放 lease，使文件对外可见。</p>
<div class="code-block highlight is-open show-line-numbers  tw:group tw:my-2">
  <div class="
    
    tw:flex 
    tw:flex-row
    tw:flex-1 
    tw:justify-between 
    tw:w-full tw:bg-bgColor-secondary
    ">      
    <button 
      class="
        code-block-button
        tw:mx-2 
        tw:flex
        tw:flex-row
        tw:flex-1"
      aria-hidden="true">
          <span class="tw:group-[.is-open]:rotate-90 tw:transition-[transform] tw:duration-500 tw:ease-in-out tw:print:hidden! tw:w-min tw:h-min tw:my-1 tw:mx-1"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg></span>
          <span class="tw:select-none tw:my-1! tw:block">cpp</span>
      </button>

   <div class="tw:flex">
      <button 
        class="
          line-number-button
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.show-line-numbers]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle line numbers"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z"/></svg></button>

      <button 
        class="
          wrap-code-button
          tw:select-none 
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.is-wrap]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle code wrap"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"/></svg></button>
      
      <button 
        class="
          copy-code-button
          tw:select-none
          tw:mx-2 
          tw:hidden
          tw:group-[.is-open]:block
          tw:hover:text-fgColor-link 
          tw:print:hidden!"
        title="Copy code">
          <span class="copy-icon tw:block"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"/></svg></span>
          <span class="check-icon tw:hidden"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/></svg></span>
      </button>
        
      <button 
        class="
          tw:select-none 
          tw:mx-2 
          tw:block 
          tw:group-[.is-open]:hidden 
          tw:print:hidden!" 
        disabled
        aria-hidden="true"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z"/></svg></button>
    </div>
  </div>
  <pre style="counter-reset: codeblock;" class="tw:block tw:m-0 tw:p-0"><code 
    id="codeblock-id-17" 
    class="
      chroma 
      tw:block! 
      tw:p-0
      tw:m-0
      tw:transition-[max-height] 
      tw:duration-500 
      tw:ease-in-out 
      tw:group-[.is-closed]:max-h-0! 
      tw:group-[.is-wrap]:text-wrap
      tw:overflow-y-hidden
      tw:overflow-x-auto
      tw:scrollbar-thin
      "><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">DfsClient</span><span class="o">::</span><span class="n">put</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">dfs_path</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">local_path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">auto</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">nn_stub_</span><span class="o">-&gt;</span><span class="n">CreateFile</span><span class="p">(</span><span class="n">dfs_path</span><span class="p">,</span> <span class="n">block_size_</span><span class="p">,</span> <span class="n">replication_</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">auto</span> <span class="n">inode_id</span> <span class="o">=</span> <span class="n">resp</span><span class="p">.</span><span class="n">inode_id</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">ifstream</span> <span class="n">ifs</span><span class="p">(</span><span class="n">local_path</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ios</span><span class="o">::</span><span class="n">binary</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">char</span><span class="o">&gt;</span> <span class="n">buf</span><span class="p">(</span><span class="n">block_size_</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">ifs</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">buf</span><span class="p">.</span><span class="n">data</span><span class="p">(),</span> <span class="n">block_size_</span><span class="p">)</span> <span class="o">||</span> <span class="n">ifs</span><span class="p">.</span><span class="n">gcount</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">bytes_read</span> <span class="o">=</span> <span class="n">ifs</span><span class="p">.</span><span class="n">gcount</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">auto</span> <span class="n">alloc</span> <span class="o">=</span> <span class="n">nn_stub_</span><span class="o">-&gt;</span><span class="n">AllocateBlock</span><span class="p">(</span><span class="n">inode_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">write_block</span><span class="p">(</span><span class="n">alloc</span><span class="p">,</span> <span class="n">buf</span><span class="p">.</span><span class="n">data</span><span class="p">(),</span> <span class="n">bytes_read</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">nn_stub_</span><span class="o">-&gt;</span><span class="n">CommitBlock</span><span class="p">(</span><span class="n">alloc</span><span class="p">.</span><span class="n">block_id</span><span class="p">(),</span> <span class="n">bytes_read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="n">alloc</span><span class="p">.</span><span class="n">generation_stamp</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">nn_stub_</span><span class="o">-&gt;</span><span class="n">CompleteFile</span><span class="p">(</span><span class="n">inode_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
</div>
<p>整个过程中 NameNode 只参与元数据协调——分配 block_id、记录副本位置、推进状态机——从不接触实际数据。Client 将数据通过 <code>DataTransferService::WriteBlock</code> 直接发送给 Pipeline 的头节点（DN1），由 DN1 链式转发给后续节点。应答沿反方向回溯：DN3 完成写入后向 DN2 应答，DN2 再向 DN1 应答，最终 DN1 向 Client 返回结果。这种设计使 Client 只需维护一条连接，复制带宽由各 DataNode 分摊。</p>]]></description></item><item><title>MiniDFS 05: DataNode 存储与心跳</title><link>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-05-datanode-internals/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><author><name>liubang</name></author><guid>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-05-datanode-internals/</guid><description><![CDATA[<p>前四篇从全局视角走完了 MiniDFS 的命名空间、写入 Pipeline 和元数据管理。从这一篇开始，我们把视角切换到单个 DataNode 内部——它如何管理本地磁盘上的 block 文件，如何通过心跳向 NameNode 证明自己还活着，以及如何通过块报告让 NameNode 了解它持有哪些副本。</p>
<p><figure><a class="lightgallery" href="/images/minidfs/datanode-internals.svg" title="DataNode 内部架构" data-thumbnail="/images/minidfs/datanode-internals.svg" data-sub-html="<h2>DataNode 进程内部组件：LocalBlockStore、HeartbeatSender、BlockReporter 与 NameNode 的交互</h2><p>DataNode 内部架构</p>"><img  loading="lazy" src='/images/minidfs/datanode-internals.svg'   alt="DataNode 内部架构"  ></a><figcaption class="image-caption">DataNode 进程内部组件：LocalBlockStore、HeartbeatSender、BlockReporter 与 NameNode 的交互</figcaption>
</figure></p>
<h2 id="localblockstore本地存储引擎" class="headerLink">
    <a href="#localblockstore%e6%9c%ac%e5%9c%b0%e5%ad%98%e5%82%a8%e5%bc%95%e6%93%8e" class="header-mark"></a>LocalBlockStore：本地存储引擎</h2><h3 id="目录布局" class="headerLink">
    <a href="#%e7%9b%ae%e5%bd%95%e5%b8%83%e5%b1%80" class="header-mark"></a>目录布局</h3><p>每个 DataNode 的数据根目录下有三个子目录，对应 block 文件的三个生命阶段：</p>
<div class="code-block highlight is-open show-line-numbers  tw:group tw:my-2">
  <div class="
    
    tw:flex 
    tw:flex-row
    tw:flex-1 
    tw:justify-between 
    tw:w-full tw:bg-bgColor-secondary
    ">      
    <button 
      class="
        code-block-button
        tw:mx-2 
        tw:flex
        tw:flex-row
        tw:flex-1"
      aria-hidden="true">
          <span class="tw:group-[.is-open]:rotate-90 tw:transition-[transform] tw:duration-500 tw:ease-in-out tw:print:hidden! tw:w-min tw:h-min tw:my-1 tw:mx-1"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg></span>
          <span class="tw:select-none tw:my-1! tw:block">text</span>
      </button>

   <div class="tw:flex">
      <button 
        class="
          line-number-button
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.show-line-numbers]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle line numbers"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z"/></svg></button>

      <button 
        class="
          wrap-code-button
          tw:select-none 
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.is-wrap]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle code wrap"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"/></svg></button>
      
      <button 
        class="
          copy-code-button
          tw:select-none
          tw:mx-2 
          tw:hidden
          tw:group-[.is-open]:block
          tw:hover:text-fgColor-link 
          tw:print:hidden!"
        title="Copy code">
          <span class="copy-icon tw:block"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"/></svg></span>
          <span class="check-icon tw:hidden"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/></svg></span>
      </button>
        
      <button 
        class="
          tw:select-none 
          tw:mx-2 
          tw:block 
          tw:group-[.is-open]:hidden 
          tw:print:hidden!" 
        disabled
        aria-hidden="true"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z"/></svg></button>
    </div>
  </div>
  <pre style="counter-reset: codeblock;" class="tw:block tw:m-0 tw:p-0"><code 
    id="codeblock-id-19" 
    class="
      chroma 
      tw:block! 
      tw:p-0
      tw:m-0
      tw:transition-[max-height] 
      tw:duration-500 
      tw:ease-in-out 
      tw:group-[.is-closed]:max-h-0! 
      tw:group-[.is-wrap]:text-wrap
      tw:overflow-y-hidden
      tw:overflow-x-auto
      tw:scrollbar-thin
      "><span class="line"><span class="cl">&lt;storage_root&gt;/
</span></span><span class="line"><span class="cl">  tmp/            — 正在通过 Pipeline 写入的 block
</span></span><span class="line"><span class="cl">    blk_1001_42.blk
</span></span><span class="line"><span class="cl">  current/        — 已 finalize 的 block，对外可读
</span></span><span class="line"><span class="cl">    blk_1000_41.blk
</span></span><span class="line"><span class="cl">  trash/          — 软删除的 block，等待异步清理
</span></span><span class="line"><span class="cl">    blk_999_40.blk</span></span></code></pre>
</div>
<p>文件命名格式为 <code>blk_&lt;block_id&gt;_&lt;generation_stamp&gt;.blk</code>，将 block_id 和 generation_stamp 编码在文件名中，使得文件系统层面即可唯一标识一个 block 的特定版本。</p>]]></description></item><item><title>MiniDFS 06: 容错与自愈</title><link>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-06-fault-tolerance/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><author><name>liubang</name></author><guid>https://blog.liubang.cc/minidfs/2026-05-24-minidfs-06-fault-tolerance/</guid><description><![CDATA[<p>分布式存储系统的核心价值不在于&quot;一切正常时能工作&quot;，而在于&quot;局部故障时仍然可靠&quot;。前五篇我们搭建了 MiniDFS 的完整数据通路——从命名空间到元数据持久化，从读路径到写 Pipeline，再到 DataNode 内部机制。这一篇，我们把目光转向系统的免疫系统：Lease 管理如何防止写冲突，ReplicationManager 如何检测和修复副本缺失，以及整个容错闭环如何通过 Heartbeat 通道协调 NameNode 与 DataNode 完成自愈。</p>
<h2 id="lease-管理写互斥的语义保证" class="headerLink">
    <a href="#lease-%e7%ae%a1%e7%90%86%e5%86%99%e4%ba%92%e6%96%a5%e7%9a%84%e8%af%ad%e4%b9%89%e4%bf%9d%e8%af%81" class="header-mark"></a>Lease 管理：写互斥的语义保证</h2><p>在分布式文件系统中，同一个文件不能被两个 Client 同时写入——否则数据会混乱不可恢复。MiniDFS 通过 Lease 机制实现写互斥：Client 在 CreateFile 时获取 Lease，持有期间独占写权限，CompleteFile 时释放。</p>
<p>LeaseManager 的接口设计非常精炼：</p>
<div class="code-block highlight is-open show-line-numbers  tw:group tw:my-2">
  <div class="
    
    tw:flex 
    tw:flex-row
    tw:flex-1 
    tw:justify-between 
    tw:w-full tw:bg-bgColor-secondary
    ">      
    <button 
      class="
        code-block-button
        tw:mx-2 
        tw:flex
        tw:flex-row
        tw:flex-1"
      aria-hidden="true">
          <span class="tw:group-[.is-open]:rotate-90 tw:transition-[transform] tw:duration-500 tw:ease-in-out tw:print:hidden! tw:w-min tw:h-min tw:my-1 tw:mx-1"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg></span>
          <span class="tw:select-none tw:my-1! tw:block">cpp</span>
      </button>

   <div class="tw:flex">
      <button 
        class="
          line-number-button
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.show-line-numbers]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle line numbers"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z"/></svg></button>

      <button 
        class="
          wrap-code-button
          tw:select-none 
          tw:mx-2 
          tw:hidden 
          tw:group-[.is-open]:block 
          tw:group-[.is-wrap]:text-fgColor-link 
          tw:print:hidden!" 
        title="Toggle code wrap"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"/></svg></button>
      
      <button 
        class="
          copy-code-button
          tw:select-none
          tw:mx-2 
          tw:hidden
          tw:group-[.is-open]:block
          tw:hover:text-fgColor-link 
          tw:print:hidden!"
        title="Copy code">
          <span class="copy-icon tw:block"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"/></svg></span>
          <span class="check-icon tw:hidden"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/></svg></span>
      </button>
        
      <button 
        class="
          tw:select-none 
          tw:mx-2 
          tw:block 
          tw:group-[.is-open]:hidden 
          tw:print:hidden!" 
        disabled
        aria-hidden="true"><svg class="icon"
    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z"/></svg></button>
    </div>
  </div>
  <pre style="counter-reset: codeblock;" class="tw:block tw:m-0 tw:p-0"><code 
    id="codeblock-id-27" 
    class="
      chroma 
      tw:block! 
      tw:p-0
      tw:m-0
      tw:transition-[max-height] 
      tw:duration-500 
      tw:ease-in-out 
      tw:group-[.is-closed]:max-h-0! 
      tw:group-[.is-wrap]:text-wrap
      tw:overflow-y-hidden
      tw:overflow-x-auto
      tw:scrollbar-thin
      "><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">LeaseManager</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">explicit</span> <span class="n">LeaseManager</span><span class="p">(</span><span class="n">MetadataStore</span><span class="o">*</span> <span class="n">store</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Result</span><span class="o">&lt;</span><span class="kt">uint64_t</span><span class="o">&gt;</span> <span class="n">acquire_lease</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">inode_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">client_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Result</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="n">renew_lease</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">lease_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                             <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">client_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Result</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="n">release_lease</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">lease_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Result</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="n">expire_stale_leases</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">Result</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span> <span class="n">has_active_lease</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="n">inode_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">MetadataStore</span><span class="o">*</span> <span class="n">store_</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre>
</div>
<p>几个关键设计决策值得展开讨论。</p>]]></description></item></channel></rss>