<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Raft on codedump notes</title>
    <link>https://www.codedump.info/en/tags/raft/</link>
    <description>Recent content in Raft on codedump notes</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Sun, 12 Apr 2026 09:59:41 +0800</lastBuildDate>
    <atom:link href="https://www.codedump.info/en/tags/raft/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>第七章 事务</title>
      <link>https://www.codedump.info/en/dist-system-cn/transaction/</link>
      <pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/transaction/</guid>
      <description>&lt;p&gt;到目前为止，我们已经介绍了复制和分区技术，复制技术（包介后面绍的共识算法）提升了系统的容错性，而分区技术提升了系统的扩展性，这两项技术解决的是数据的*&amp;ldquo;物理问题&amp;rdquo;&lt;em&gt;。除此以外，分布式系统中的数据访问还经常面临着&lt;/em&gt;&amp;ldquo;逻辑问题&amp;rdquo;&lt;em&gt;，此时就需要本章将要介绍的&lt;/em&gt;事务*技术来解决：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;复制 (Replication)&lt;/strong&gt;&lt;/em&gt;：主要目标是&lt;em&gt;高可用性和数据冗余&lt;/em&gt;。通过在不同的节点上存储相同的数据副本，当某个节点发生故障时，系统可以从其他副本继续提供服务。它回答的是：&amp;ldquo;我的数据会不会因为一台机器挂掉而丢失或无法访问？&amp;quot;。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;分区 (Partitioning / Sharding)&lt;/strong&gt;&lt;/em&gt;：主要目标是&lt;em&gt;可扩展性&lt;/em&gt;。当单台机器的存储或计算能力无法承载全部数据和请求时，我们将数据水平切分到多个节点上。它回答的是：&amp;ldquo;我的系统如何处理不断增长的数据量和访问压力？&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;事务 (Transaction)&lt;/strong&gt;&lt;/em&gt;：主要目标是&lt;em&gt;数据操作的正确性&lt;/em&gt;。它将一系列操作打包成一个不可分割的逻辑单元，保证这些操作要么全部成功，要么全部失败，并且在并发执行时互不干扰。它回答的是：&amp;ldquo;我如何确保一系列相关的操作在任何情况下（并发、故障）都能保持数据的正确状态？&amp;rdquo;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;为什么只有复制和分区是不够的？我们来看以下几个典型场景：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;场景一：转账操作（原子性缺失的灾难）&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;用户A要向用户B转账100元。这个操作至少包含两个步骤：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;从用户A的账户中扣除100元。&lt;/li&gt;&#xA;&lt;li&gt;给用户B的账户增加100元。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;在一个缺少事务技术的分布式系统中，可能会出现以下的问题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;发生故障&lt;/strong&gt;：系统在执行完第1步后突然崩溃（比如数据库节点宕机）。此时，A的钱被扣了，但B没收到钱。钱&amp;quot;凭空消失&amp;quot;了。即使数据有多个副本（复制技术），但所有副本记录的都是这个&amp;quot;中间状态&amp;quot;的错误数据。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;跨分区操作&lt;/strong&gt;：假设用户A的数据在分区1，用户B的数据在分区2。这个转账操作需要一个协调者来分别通知两个分区执行操作。如果分区1成功扣款，但分区2因为网络问题或自身故障未能成功收款，同样会导致数据不一致。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;但是如果使用事务技术，事务的&lt;em&gt;原子性 (Atomicity)&lt;/em&gt; 保证了这一系列操作是一个&amp;quot;全有或全无&amp;quot;的原子单元。系统会确保&amp;quot;扣款&amp;quot;和&amp;quot;收款&amp;quot;这两个步骤要么都完成，要么如果中间有任何差错，所有已经完成的步骤都会被回滚（Rollback），系统状态恢复到操作开始之前。这样就绝不会出现钱平白消失或多出来的情况。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;场景二：并发抢购（隔离性缺失的混乱）&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;一个电商网站上，某件商品只剩下最后1件库存。此时，两个用户（用户C和用户D）同时点击了&amp;quot;购买&amp;quot;按钮。&lt;/p&gt;&#xA;&lt;p&gt;在一个缺少事务技术的分布式系统中，可能会出现以下的问题：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;用户C的请求到达，系统读取库存为1。&lt;/li&gt;&#xA;&lt;li&gt;在用户C的请求完成&amp;quot;减库存&amp;quot;操作之前，用户D的请求也到达了，系统读取库存仍然为1。&lt;/li&gt;&#xA;&lt;li&gt;用户C的请求执行&amp;quot;减库存&amp;rdquo;，库存变为0。&lt;/li&gt;&#xA;&lt;li&gt;用户D的请求也执行&amp;quot;减库存&amp;quot;，库存变为-1（超卖）。&lt;/li&gt;&#xA;&lt;li&gt;最终结果是：两个用户都以为自己买到了商品，而系统库存出现了负数。这造成了严重的业务逻辑错误。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;但是如果使用事务技术，事务的&lt;em&gt;隔离性 (Isolation)&lt;/em&gt; 保证了并发执行的多个事务之间互不干扰，就像它们是串行执行的一样。当用户C的事务开始处理库存时，它会锁定该数据。用户D的事务在用户C的事务完成（提交或回滚）之前，无法修改库存数据，它要么等待，要么读取到一个旧的值然后操作失败。这样就保证了最终只有一个人能成功买到商品。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;场景三：系统崩溃后的数据完整性（持久性缺失的风险）&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;一个订单系统刚刚完成了一笔重要订单的创建，所有数据已经写入。在数据从内存刷到磁盘的瞬间，服务器断电了。&lt;/p&gt;&#xA;&lt;p&gt;在一个缺少事务技术的分布式系统中，如果系统依赖于操作系统的缓存写入，那么这笔订单数据可能就永久丢失了。尽管你可能收到了&amp;quot;操作成功&amp;quot;的响应，但数据并未真正持久化。&lt;/p&gt;&#xA;&lt;p&gt;事务的&lt;em&gt;持久性 (Durability)&lt;/em&gt; 保证了一旦事务被提交，其结果就是永久性的。即使系统崩溃，数据也能够被恢复。这通常通过预写日志（Write-Ahead Logging, WAL）等机制来实现：在修改数据本身之前，先将操作记录到持久化的日志中。&lt;/p&gt;&#xA;&lt;p&gt;我们可以把分布式系统想象成一个大型的、跨部门的公司项目：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;复制&lt;/em&gt;技术保证了每个部门都有核心成员的备份，或者有完整的项目文档副本。如果一个核心成员请假或离职，备份人员可以顶上，保证部门工作不中断（高可用性）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;分区&lt;/em&gt;技术把项目拆分给不同的部门（前端部、后端部、数据库部），这提高了整个公司的处理能力（可扩展性）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;事务&lt;/em&gt;技术是项目管理中的一个&amp;quot;工作流&amp;quot;或&amp;quot;流程规定&amp;quot;。比如，&amp;ldquo;产品上线&amp;quot;这个工作流必须包含：1. 代码部署成功, 2. 数据库迁移成功, 3. CDN缓存刷新成功。这个流程规定了：这三件事必须全部搞定，这个&amp;quot;产品上线&amp;quot;才算真正成功。 如果任何一步失败，整个上线流程就要回滚到初始状态（比如代码回滚，数据库恢复），绝不能停在一个&amp;quot;部署了一半&amp;quot;的尴尬状态。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;除此以外，事务还为应用开发者提供简化的编程模型。试想一下，如果没有事务的各种保证，应用开发者需要：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;手动处理各种失败场景下的回滚逻辑。&lt;/li&gt;&#xA;&lt;li&gt;手动加锁来避免并发冲突。&lt;/li&gt;&#xA;&lt;li&gt;编写复杂的补偿代码来修复部分失败导致的数据不一致。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这会使业务代码变得异常复杂、容易出错且难以维护。事务将所有这些复杂性封装起来，向开发者提供了一个简洁的抽象：&amp;ldquo;你可以把一系列操作当作一个不可分割的单元来执行，系统保证其ACID属性。&amp;rdquo; 这极大地提升了开发效率和应用可靠性。&lt;/p&gt;&#xA;&lt;p&gt;在本章中，我们先从单机数据库事务开始展开对事务ACID特性的讨论，进而延展到涉及多个服务的分布式事务。在分布式系统中，由于可能跨多个服务（例如一次电商的购买行为中中涉及订单服务、库存服务、支付服务），面临着更大的挑战：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;网络不可靠，可能出现延迟、分区、丢包等情况。&lt;/li&gt;&#xA;&lt;li&gt;节点会失败，服务器可能会宕机、进程可能会崩溃。&lt;/li&gt;&#xA;&lt;li&gt;数据不一致的风险，一部分操作成功，一部分失败（例如，订单创建成功，但扣库存失败）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;71-深入理解acid&#34;&gt;&#xA;  7.1 深入理解ACID&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#71-%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3acid&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;在单机数据库中，可能出现各种故障：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;数据库正在写入数据时系统崩溃，在重启数据库之后，如何从故障数据中恢复。&lt;/li&gt;&#xA;&lt;li&gt;多个客户端同时写入多个数据，例如本章开头的电商购买例子中，&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;为了保障应用开发者不被这些故障困扰，事务技术一直据库系统的首选机制。事务技术向应用开发者提供了ACID的安全保证，我们首先来了解这些特性。ACID最早在论文中被提出，做为精确描述数据库的容错机制而定义，是以下四个单词的缩写：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;A（Atomicity）&lt;/strong&gt;&lt;/em&gt;：原子性，事务保证了对多个数据的修改，要么同时成功，要么同时失败。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;C（Consistency）&lt;/strong&gt;&lt;/em&gt;：一致性：一个事务在执行前后，数据库都必须处于正确的状态，满足完整性约束。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;I（Isolation）&lt;/strong&gt;&lt;/em&gt;：隔离性，多个事务并发执行时，一个事务的执行不应影响其他事务的执行。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;D（Durability）&lt;/strong&gt;&lt;/em&gt;：持久性，事务处理完成后，对数据的修改就是永久的，即便系统故障也不会丢失。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;总体而言，ACID属性提供了一种机制，使每个事务都作为一个单元，完成一组操作，产生一致结果，事务之间彼此隔离，更新永久生效&amp;rdquo;，从而来确保数据库的正确性和一致性。&lt;/p&gt;</description>
    </item>
    <item>
      <title>第六章：分区</title>
      <link>https://www.codedump.info/en/dist-system-cn/partitioning/</link>
      <pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/partitioning/</guid>
      <description>&lt;p&gt;到目前为止，我们讨论的系统中，假设了所有的机器都会存储&lt;em&gt;所有&lt;/em&gt;数据，在前面讨论复制的章节中，主节点在收到客户端的写入请求时，将全部数据分别保存在本地以及其它的从节点上，这样的存储方式存在以下的问题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;可扩展性&lt;/strong&gt;：如果使用主备方式进行数据复制，所有的工作都落在主节点上，在负载很大的情况下，主节点很快会成为系统的瓶颈；&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;单机瓶颈&lt;/strong&gt;：单台机器由于其物理硬件的能力（硬盘、内存、CPU等），总会遇到处理的上限。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;故障隔离&lt;/strong&gt;：如果一部分数据出现故障，会影响所有数据的访问，降低了系统的整体可用性。例如，如果能够将不同城市的数据保存在不同的节点上，当某个地区的服务出现异常时，就不会影响其它地区的数据。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在系统需要进行扩展时，通常有两种不同的扩展方案。如图6.1所示：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;垂直扩展（Vertical Scaling）&lt;/strong&gt;&lt;/em&gt;：也称为&amp;quot;Scale Up&amp;quot;，通过在单一服务器上增加更多资源（如更快的 CPU、更多的 RAM、更大的硬盘）来提升处理能力。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;水平扩展（Horizontal Scaling）&lt;/strong&gt;&lt;/em&gt;：也称为&amp;quot;Scale Out&amp;quot;，通过增加更多的服务器来分担负载，将它们组成一个集群共同工作。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;图6.1 垂直扩展和水平扩展&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/partition/scale-up-out.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;垂直扩展方案只需要扩展单台机器的处理能力，有以下的优点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;简单&lt;/strong&gt;：管理和维护一台机器远比管理一个集群简单。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;数据一致性强&lt;/strong&gt;：所有数据都在一台机器上，不需要处理分布式系统中的数据同步和一致性问题，事务处理简单。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;低延迟&lt;/strong&gt;：进程间通信（IPC）在单机内部非常快，没有网络开销。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;对应用透明&lt;/strong&gt;：应用代码通常不需要做大的改动来适应更强的硬件。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;然而，任何机器的CPU、内存扩展都有其物理上限，无法无限制一直提升下去。同时由于存在单点问题，服务器一旦宕机，整个服务将完全中断，可用性低，也给维护单台机器带来很大挑战，通常需要停机维护。&lt;/p&gt;&#xA;&lt;p&gt;对比而言，水平扩展通过增加机器扩展系统的处理能力，有以下的优点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;理论上无限扩展&lt;/strong&gt;：当负载增加时，只需向集群中添加更多标准服务器即可，扩展性极佳。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;高可用性与容错性&lt;/strong&gt;：集群中的一台或多台服务器宕机，系统可以继续服务（可能会有性能下降），没有单点故障。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;成本效益高&lt;/strong&gt;：可以使用大量廉价的硬件来组成集群，总体拥有成本更低。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;弹性伸缩&lt;/strong&gt;：可以根据负载动态地增加或减少服务器数量，特别适合云环境。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;在某些场景下，必须进行数据分区&lt;/strong&gt;。比如服务可以在不同大洲、国家、区域被访问，需要在这些地区分别部署服务。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;水平扩展方案中，由于数据分摊到集群上的不同节点上，面临以下的挑战：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;架构复杂性&lt;/strong&gt;：需要引入额外的组件和技术，如负载均衡器、服务发现、分布式数据存储、配置管理等。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;数据一致性挑战&lt;/strong&gt;：跨多台机器管理数据状态和保证一致性非常困难，这将是后续分布式事务章节讨论的重点。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;网络延迟&lt;/strong&gt;：节点间的通信依赖网络，相比单机内的 IPC，延迟更高且不可靠。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;以上简述了垂直和水平两种扩展方案的优缺点，如果业务处于早期阶段，用户量和数据量增长可预测且在单机范围内，推荐优先选择垂直扩展方案；而如果预期用户量会爆炸式增长，需要应对不可预测的流量洪峰，则优先选择水平扩展。本章中主要讨论水平分区策略。&lt;/p&gt;&#xA;&lt;p&gt;分布式系统中的水平扩展，引入了***&amp;ldquo;分区（Partition）&amp;rdquo;***的概念，将数据分摊到不同的机器，如图6.2所示，数据被分摊到两个不同的节点上，不再是所有节点保存所有数据的情况。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;图6.2 从一台机器存储所有数据，到数据分摊到不同机器存储&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/partition/partition-2.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;这里的分区指的是不同节点之间划分数据的方式，而不是网络分区导致的通信中断。&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;分区&amp;quot;在不同的系统中有不同的叫法，在有的系统中使用&amp;quot;shared&amp;quot;表示分区，有的系统使用&amp;quot;region&amp;rdquo;。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;通常而言，分区和复制技术会结合在一起。对于一份数据，会保存在同一分区的多个节点上，这意味着一条记录会保存在特定的分区，而分区有多个节点保存副本数据，以提高系统的容错性，如图6.3所示。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;图6.3 结合使用复制和分区技术&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/partition/partition-3.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;在数据进行分区之后，有些操作会变得复杂：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;如何访问数据&lt;/strong&gt;：原先只需要访问主节点即可，现在需要考虑如何路由数据请求到对应的分区。这是一个请求路由问题，我们将在6.3.2节中深入讨论这个问题。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;分区再平衡&lt;/strong&gt;：划分数据分区的目的之一是将数据尽量均衡得分布在多个分区中，但是当不同分区数据分布不均时，要考虑数据分区的再平衡问题。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;全排序问题&lt;/strong&gt;：我们将看到，不同的分区策略，对全排序的支持不一样。如果全排序操作是系统需要考虑的常见操作，要尽量选择全排序友好的分区策略。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在本章中，我们将讨论以下和分区相关的话题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;有哪些常见的数据分区方式，它们的优缺点是什么；&lt;/li&gt;&#xA;&lt;li&gt;如何路由数据请求到对应的分区，实现请求路由的常见策略。&lt;/li&gt;&#xA;&lt;li&gt;在某些分区的访问量较大时，出现了分区热点问题，应该如何解决。&lt;/li&gt;&#xA;&lt;li&gt;最后，做为复制和分区这两章的阶段性总结，我们将考察几个项目的实现方案。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;61-分区策略&#34;&gt;&#xA;  6.1 分区策略&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#61-%e5%88%86%e5%8c%ba%e7%ad%96%e7%95%a5&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 class=&#34;heading&#34; id=&#34;611-范围分区&#34;&gt;&#xA;  6.1.1 范围分区&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#611-%e8%8c%83%e5%9b%b4%e5%88%86%e5%8c%ba&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;范围分区是分布式数据存储中最直观、最符合直觉的分区策略。它的核心思想非常朴素：将数据主键看作一个连续的有序序列，将每一段切分出来的连续范围分配给不同的节点管理。&lt;/p&gt;&#xA;&lt;p&gt;想象一本字典，按照词条的首字母划分了不同的分卷：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;卷1：包含 A - B 开头的词条。&lt;/li&gt;&#xA;&lt;li&gt;卷2：包含 C - E 开头的词条。&lt;/li&gt;&#xA;&lt;li&gt;&amp;hellip;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这就是典型的范围分区，在这个类比中：词条单词就是分区键（Partition Key），每一卷书就是一个分区（Partition/Tablet/Region）。当你需要查找单词 &amp;ldquo;Apple&amp;rdquo; 时，你知道它一定在卷1；查找 &amp;ldquo;Cake&amp;rdquo; 时，它一定在卷2。&lt;/p&gt;</description>
    </item>
    <item>
      <title>第五章：共识算法</title>
      <link>https://www.codedump.info/en/dist-system-cn/consensus/</link>
      <pubDate>Wed, 18 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/consensus/</guid>
      <description>&lt;h1 class=&#34;heading&#34; id=&#34;第5章-分布式共识算法&#34;&gt;&#xA;  第5章 分布式共识算法&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e7%ac%ac5%e7%ab%a0-%e5%88%86%e5%b8%83%e5%bc%8f%e5%85%b1%e8%af%86%e7%ae%97%e6%b3%95&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;在前面的章节中，我们讨论了如何通过复制来防止单点故障导致的某些数据丢失，如何通过分区来承载海量数据，如何通过分布式事务来确保跨节点操作的原子性。&lt;/p&gt;&#xA;&lt;p&gt;然而，当我们试图将这些机制组合成一个真正能够全天候运行、自动容错的生产级系统时，会发现一个明显的缺口：如何在部分节点故障的情况下，依然让存活的节点对系统的状态达成一致？&lt;/p&gt;&#xA;&lt;p&gt;传统的两阶段提交协议（2PC）虽然解决了&amp;quot;所有节点要么全部提交，要么全部回滚&amp;quot;的一致性问题，但它是阻塞式的——一旦协调者发生故障，整个集群可能陷入无限等待。这揭示了分布式系统中一个更深层次的挑战：我们需要一种机制，它不仅能达成一致，还必须具备容错性。换言之，即使集群中的少数节点（如半数以下）发生了崩溃或网络隔离，系统依然能够继续推进，对外提供服务。&lt;/p&gt;&#xA;&lt;p&gt;这便是**共识算法（Consensus Algorithms）**的舞台。&lt;/p&gt;&#xA;&lt;p&gt;从本质上讲，共识算法解决的是分布式计算中著名的&amp;quot;协商&amp;quot;问题：在一个不可靠的网络环境中，由一组并发的进程提议某个值，并最终就该值达成唯一的、不可逆的协定。&lt;/p&gt;&#xA;&lt;p&gt;这一看似简单的目标，实则是分布式系统中最坚硬的内核。共识算法不仅是实现状态机复制（State Machine Replication）的理论基础，更是现代分布式数据库实现自动 Leader 选举（Leader Election）、原子广播（Atomic Broadcast）以及配置变更的核心动力。无论是 Paxos 的精妙理论，还是 Raft 的工程实践，它们都旨在将不确定的物理世界（丢包、延迟、宕机）抽象为一个逻辑上严格有序的整体。&lt;/p&gt;&#xA;&lt;p&gt;在本章中，我们将深入探讨共识算法的原理与演进，理解它们是如何在&amp;quot;一致性&amp;quot;与&amp;quot;可用性&amp;quot;之间找到那条完美的平衡线。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;51-共识算法简介&#34;&gt;&#xA;  5.1 共识算法简介&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#51-%e5%85%b1%e8%af%86%e7%ae%97%e6%b3%95%e7%ae%80%e4%bb%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 class=&#34;heading&#34; id=&#34;511-概述&#34;&gt;&#xA;  5.1.1 概述&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#511-%e6%a6%82%e8%bf%b0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;在分布式系统中，**共识（Consensus）**问题占据着核心地位。在此之前，我们讨论了通过复制来保证数据的持久性，通过分区来扩展系统的容量。然而，当这些机制在面临部分节点故障或网络分区时，如何让整个系统依然能够像一个单一的、连贯的整体一样行动，便是共识算法所要解决的根本问题。&lt;/p&gt;&#xA;&lt;p&gt;直观地说，共识就是指多个参与者（进程或节点）对某个特定的值（Value）达成一致看法的过程。这个&amp;quot;值&amp;quot;在不同的场景下可以代表不同的含义：它可能是一个布尔值（决定事务提交还是回滚），选出的主节点 ID（Leader Election），或者是一条具体的日志条目（Log Entry）。这个定义看似抽象，但其实涵盖了大量实际应用场景：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在主从复制中，客户端需要通过主节点才能写入数据，因此节点之间需要选举出一个领导者来协调任务分发、日志复制等工作。如果没有一个可靠的共识机制，可能会出现多个节点同时认为自己是领导者，导致**脑裂（split-brain）**现象，导致数据冲突。&lt;/li&gt;&#xA;&lt;li&gt;在分布式数据库、分布式存储等系统中，数据通常会被复制到多个节点上以提高可用性和容错能力。然而，如何保证所有副本在写入时保持一致？这就需要共识算法来协调写入顺序和结果。&lt;/li&gt;&#xA;&lt;li&gt;考虑银行转账问题。用户A要转账给用户B，这里涉及到两个操作：从用户A的账号上扣除款项，向用户B的账号增加款项。这两个操作必须合在一起成为一个&amp;quot;原子操作&amp;quot;：要么都成功，要么都失败。表现在具体的操作上，系统的所有节点需要对这个原子操作形成一致：所有的节点都提交成功，或者都终止操作回滚数据，不能出现在某些节点上提交成功，而在另外的节点上中断的情况。&lt;/li&gt;&#xA;&lt;li&gt;集群的配置发生变更，例如集群增加或者删除了节点时，这些配置信息同样需要在集群的节点中达成共识。&lt;/li&gt;&#xA;&lt;li&gt;在 2.5 节中我们提到，系统的状态由事件按照顺序组成，所以如果一组事件如果能对一个分布式系统中的所有节点保持一样的顺序，那么这些事件在所有节点中就会得到同样的状态，而这里的&amp;quot;事件顺序&amp;quot;也是一种共识。&lt;/li&gt;&#xA;&lt;li&gt;系统中的多个节点需要访问临界区资源时，需要决定访问的顺序。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;以上的问题虽然各不相同，在不同的问题场景中需要达成的一致内容各不相同：可能是消息传递的顺序、一组进程是存活还是宕机、哪个节点是主节点、谁能访问共享资源、事务应该终止还是结束等等，不管这些细节如何。但是它们都有一个共同的特性：系统中的节点，需要就某些共享信息达成一致。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading&#34; id=&#34;512-共识算法的难点&#34;&gt;&#xA;  5.1.2 共识算法的难点&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#512-%e5%85%b1%e8%af%86%e7%ae%97%e6%b3%95%e7%9a%84%e9%9a%be%e7%82%b9&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;如果网络是完美可靠的，或者节点之间不存在延迟，达成共识将变得轻而易举。共识算法之所以复杂且困难，根源在于我们所处的分布式环境通常是异步（Asynchronous）且不可靠的。&lt;/p&gt;&#xA;&lt;p&gt;在此环境下，共识算法面临以下核心挑战：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;节点故障&lt;/strong&gt;：节点可能因为硬件故障、软件错误或断电而停止工作。例如，一个数据库节点宕机，可能导致数据写入失败或不一致。共识算法需要确保即使部分节点宕机，系统仍能达成一致。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;网络问题&lt;/strong&gt;：分布式系统中的节点通过网络通信，可能面临延迟、分区（网络断开）或消息丢失。例如，两个数据中心之间的网络中断可能导致节点无法同步状态。共识算法需要通过超时、重试或投票机制来处理这些问题。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;拜占庭故障&lt;/strong&gt;：某些节点可能表现出恶意行为，比如发送错误的消息、伪造数据或故意不响应，这种情况在区块链等开放系统中尤其常见。共识算法需要设计机制来识别和隔离恶意节点。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;数据一致性&lt;/strong&gt;：在分布式数据库或文件系统中，多个副本需要保持一致。例如，用户在不同节点上更新数据时，系统必须确保所有副本最终反映相同的状态。共识算法通过定义如何选择&amp;quot;正确&amp;quot;的值来解决这一问题。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;想象一群探险者在迷雾森林中寻找宝藏，每个人都有一份不完整的地图（分布式系统中的节点状态）。他们需要通过喊话（网络通信）来决定集合地点，但有人可能迷路（节点故障），有人可能故意指错方向（拜占庭故障），还有人可能听不清喊话（网络延迟）。共识算法就像一个魔法协议，确保所有诚实的探险者最终聚集在同一个地点，即使面对这些挑战。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading&#34; id=&#34;513-共识和一致性&#34;&gt;&#xA;  5.1.3 共识和一致性&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#513-%e5%85%b1%e8%af%86%e5%92%8c%e4%b8%80%e8%87%b4%e6%80%a7&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;受翻译的影响，很多讨论共识算法的中文资料都称它们是&amp;quot;分布式一致性算法&amp;quot;，虽然&amp;quot;一致性&amp;quot;（Consistency）和&amp;quot;共识&amp;quot;（Consensus）这两个词非常相似，但它们在分布式系统中有着明确的区别和紧密的联系。在本节介绍两者的区别和联系，为了规范和严谨，本书中严格区分使用&amp;quot;共识&amp;quot;和&amp;quot;一致性&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;一致性&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;一致性是指多个副本之间的数据是否一致，或者客户端访问系统时是否能看到一致的数据视图。一致性更多是从外部视角来看待系统的状态，强调的是读写操作的结果是否符合预期的行为模型。在 3.2 节中详细讨论了常见的一致性模型。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;共识&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;共识则是一种协议或算法机制，用于在多个节点之间就某个值达成一致。它是实现一致性的一种手段，尤其是在多副本系统中，共识是实现强一致性的重要工具。&lt;/p&gt;&#xA;&lt;p&gt;共识协议通常满足以下几个性质：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;终止性（Termination）&lt;/strong&gt;：所有非故障的节点最终都会做出决定。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;一致性（Agreement）&lt;/strong&gt;：所有非故障的节点对同一个值达成一致。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;有效性（Validity）&lt;/strong&gt;：共识值必须是某个节点提出的合法值。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;共识更偏重于内部逻辑的协调机制，是系统为了达到某种一致状态而采取的具体技术路径。&lt;/p&gt;</description>
    </item>
    <item>
      <title>第四章：复制</title>
      <link>https://www.codedump.info/en/dist-system-cn/replication/</link>
      <pubDate>Thu, 12 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/replication/</guid>
      <description>&lt;p&gt;在分布式系统中，数据复制（Data Replication）是核心设计策略之一，其核心目的是通过冗余存储相同数据副本来提升系统的可靠性、可用性、性能及容错能力：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;高可用性（High Availability）&lt;/em&gt;：如果只有一个节点提供服务，单节点故障会导致服务不可用。通过将数据复制到多个节点，即使一个节点宕机，其他节点仍可继续提供服务。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;容错与灾难恢复（Fault Tolerance and Disaster Recovery）&lt;/em&gt;：硬件故障、网络分区或数据中心灾难可能导致数据永久丢失。采用多副本存储（如跨机房/跨地域复制）确保数据可恢复。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;降低延迟（Reduced Latency）&lt;/em&gt;：用户与数据中心的物理距离导致访问延迟（如访问跨国服务），将数据复制到地理分布的不同节点，使用户可访问最近的副本。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;提升读性能（Read Performance Optimization）&lt;/em&gt;：单一节点可能成为读请求的瓶颈，通过多副本分散读负载以提升读性能。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;按照复制时是否有主节点的介入，分为&lt;em&gt;主从复制&lt;/em&gt;和&lt;em&gt;无主节点复制&lt;/em&gt;，其中主从复制意味着系统中有一个中心节点，负责协调写入数据复制到其它副本，与之对应的，无主节点复制就是去中心化的架构。&lt;/p&gt;&#xA;&lt;p&gt;尽管数据复制带来诸多优势，但由于数据复制延迟等原因，需要面对多个副本间数据的一致性问题。数据复制的引入，在带来可扩展性和可靠性的同时，也引发了如何保证多个副本数据语义一致性的挑战。我们将深入讨论不同一致性模型的特点和实现，不同的一致性模型的实现难度不同，不同的业务场景采用不同的一致性模型，可以看到在很多时候，采用较弱的一致性模型也能满足业务的需求。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;在很多时候，单台机器不足以容纳系统的所有数据，在这种情况下，需要按照某种规则将数据划分到不同的机器中存储，这是分区一章将要讨论的内容。在本章中，我们假设单台机器的容量可以容纳系统的所有数据。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;主从复制&#34;&gt;&#xA;  主从复制&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%b8%bb%e4%bb%8e%e5%a4%8d%e5%88%b6&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;为了保证系统的高可用性，需要将系统的数据保存到多个节点上，每一个保存了完整数据的节点称为&lt;em&gt;副本（replica）&lt;/em&gt;。在主从复制模式中，系统中的副本地位不一致，分为以下两类：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;主节点&lt;/em&gt;：客户端的写请求首先发往主节点，主节点收到客户端的写入数据请求之后，保存到本地存储中。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;从节点&lt;/em&gt;：主节点将客户端写入的数据保存在本地之后，将数据同步到从节点，主、从节点上的数据将保持严格的写入顺序。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;在不同的系统中，对主节点有不同的称呼，例如&lt;em&gt;primary&lt;/em&gt;、&lt;em&gt;master&lt;/em&gt;、&lt;em&gt;leader&lt;/em&gt;等，对从节点也有不同的称呼，例如&lt;em&gt;secondary&lt;/em&gt;、&lt;em&gt;replica&lt;/em&gt;、&lt;em&gt;slave&lt;/em&gt;、&lt;em&gt;follower&lt;/em&gt;等。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;如图[fig:replication/rep-primary-1]是一个简易的主从复制模式的分布式系统。在主从复制模式下，向系统写入的数据，必须首先经过主节点，再复制到从节点。与写操作不同的是，某些系统允许通过从节点读取数据，这种情况下可能读到旧的过期数据，这要看系统需要满足怎样的一致性要求，我们将在”一致性模型“小节中深入讨论这个话题。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;客户端1的写入请求首先发到主节点A，再由主节点同步到其它从节点。如果此时有另外的客户端2从从节点读取数据，那么可能读到旧的过期数据&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/replication/rep-primary-1.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;图: 客户端1的写入请求首先发到主节点A，再由主节点同步到其它从节点。如果此时有另外的客户端2从从节点读取数据，那么可能读到旧的过期数据&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading&#34; id=&#34;sec:replication-mode&#34;&gt;&#xA;  数据复制模式&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#sec%3areplication-mode&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;在图[fig:replication/rep-primary-1]中，我们刻意忽略了一个问题：当客户端写入数据时，何时应答客户端写入成功？是只要求在主节点上写入数据成功就能应答客户端，还是要等待数据复制到从节点成功之后才能应答？根据不同的应答时机，将复制方式分为：&lt;em&gt;同步复制（Synchronous Replication）&lt;/em&gt;、&lt;em&gt;异步复制（Asynchronous Replication）&lt;em&gt;和&lt;/em&gt;半同步复制（Semi-synchronous Replication）&lt;/em&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;同步复制&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;如图[fig:replication/rep-primary-sync.png]，在同步复制中，主节点只有在将数据同步到&lt;em&gt;所有&lt;/em&gt;从节点并且得到所有从节点的确认之后，才向客户端返回写操作成功的响应。这种复制方式的优缺点是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;优点：数据一致性高，没有数据丢失的问题。&lt;/li&gt;&#xA;&lt;li&gt;缺点：系统的应答时间，由响应最慢的那个从节点决定。系统中如果有一个节点宕机或者网络延迟较高，就会影响写入的成功和延迟，系统的可用性随着从节点数量的增加而降低。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;在同步复制中，主节点只有在收到所有从节点的复制成功应答之后，才响应客户端的写入成功。在这种方式下，系统的应答时间，由响应最慢的那个从节点决定。&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/replication/rep-primary-sync.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;图: 在同步复制中，主节点只有在收到所有从节点的复制成功应答之后，才响应客户端的写入成功。在这种方式下，系统的应答时间，由响应最慢的那个从节点决定。&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;异步复制&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;如图[fig:replication/rep-primary-async.png]，与同步复制完全不同的是，在异步复制中，主节点只要在本地持久化写入的数据，就可以响应客户端写入成功，不需要等待数据成功复制到从节点。这种复制方式的优缺点是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;优点：写操作延迟低，客户端响应快。主节点不受从节点故障或网络问题影响，系统可用性高。&lt;/li&gt;&#xA;&lt;li&gt;缺点：数据一致性弱，主从节点间可能存在短暂不一致。如果在数据成功复制到从节点之前，主节点发生故障，未同步的数据将丢失。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;在异步复制中，主节点在数据保存到本地之后就应答客户端写入成功，在此之后才开始复制数据到从节点。如果主节点在主节点应答客户端之后、数据成功复制到从节点之前发生故障，那么应答客户端已经成功写入的数据，可能会丢失。&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/replication/rep-primary-async.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;图: 在异步复制中，主节点在数据保存到本地之后就应答客户端写入成功，在此之后才开始复制数据到从节点。如果主节点在主节点应答客户端之后、数据成功复制到从节点之前发生故障，那么应答客户端已经成功写入的数据，可能会丢失。&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;半同步复制&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;从上面可以看到，同步复制和异步复制都有各自的问题，业界更常使用的是两者折中之后的方案：&lt;em&gt;半同步复制（Semi-synchronous Replication）&lt;/em&gt;[@Semi-Synchronous]，如图[fig:replication/rep-primary-semi-async.png]所示。在这种方案中，主节点并不会在数据保存到本地之后就马上应答客户端写入成功，也不会等待&lt;em&gt;所有&lt;/em&gt;从节点都复制成功从响应客户端，而是在两者之间取一个折中：主节点等待&lt;em&gt;足够多&lt;/em&gt;的从节点应答写入成功。&lt;/p&gt;&#xA;&lt;p&gt;在这种折中方案下，因为系统只需要等待部分从节点应答成功，不受最慢的从节点影响，因此不会像在同步复制中那样，延迟最慢的从节点直接决定系统的响应时间；另外数据也复制到了部分从节点上，这样不会像在异步复制中那样，由于主节点的故障导致数据完全丢失。&lt;/p&gt;&#xA;&lt;p&gt;至于何谓&lt;em&gt;足够多&lt;/em&gt;的从节点，不同一致性要求的系统有不同的需求，我们将在[subsection:quorum]继续讨论这个话题。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;在半同步复制中，主节点只需要等待部分从节点应答复制成功，就可以响应客户端写入数据成功。在图中，假设此时系统要求只要复制到一个从节点就能应答写入成功，因此主节点A在收到从节点B的成功复制应答，就响应客户端写入成功，不必等待从节点C的复制应答。&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/replication/rep-primary-semi-async.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;图: 在半同步复制中，主节点只需要等待部分从节点应答复制成功，就可以响应客户端写入数据成功。在图中，假设此时系统要求只要复制到一个从节点就能应答写入成功，因此主节点A在收到从节点B的成功复制应答，就响应客户端写入成功，不必等待从节点C的复制应答。&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;下表对比总结三种复制模式的工作原理和优缺点。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;模式&lt;/th&gt;&#xA;          &lt;th&gt;工作原理&lt;/th&gt;&#xA;          &lt;th&gt;优点&lt;/th&gt;&#xA;          &lt;th&gt;缺点&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;同步复制&lt;/td&gt;&#xA;          &lt;td&gt;主节点必须等待所有从节点都确认收到数据并写入成功后，才响应客户端。&lt;/td&gt;&#xA;          &lt;td&gt;数据一致性最强，能保证数据已安全存储在多个副本上。&lt;/td&gt;&#xA;          &lt;td&gt;延迟非常高，写入性能最差。任何一个从节点宕机都会导致整个系统无法写入（可用性低）。&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;异步复制&lt;/td&gt;&#xA;          &lt;td&gt;主节点完成本地写入后立即响应客户端，之后再异步地将数据复制给从节点。&lt;/td&gt;&#xA;          &lt;td&gt;延迟极低，写入性能最佳。&lt;/td&gt;&#xA;          &lt;td&gt;数据丢失风险高。如果主节点在日志发送前宕机，已确认的写操作会永久丢失，造成数据不一致。&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;半同步复制&lt;/td&gt;&#xA;          &lt;td&gt;折中方案。主节点只需等待足够多的从节点确认收到数据后，即可响应客户端。&lt;/td&gt;&#xA;          &lt;td&gt;在性能和数据安全之间取得了很好的平衡。&lt;/td&gt;&#xA;          &lt;td&gt;仍比异步复制的延迟高，且如果唯一确认的从节点宕机，会退化为同步模式。&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;em&gt;对比总结三种复制模式的工作原理和优缺点&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>第三章：分布式系统中的时间和顺序</title>
      <link>https://www.codedump.info/en/dist-system-cn/time/</link>
      <pubDate>Tue, 03 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/time/</guid>
      <description>&lt;p&gt;在分布式系统中，多个节点协同工作。客户端请求会发送不同的节点上处理，这些请求就成为了一个一个的节点上的事件。我们将看到，系统的状态由这些一个一个的事件按照&lt;em&gt;特定的顺序&lt;/em&gt;执行之后形成。因此，事件的顺序尤为重要。不同的节点中，看到的事件先后顺序可能会有差别，这也造成了这些节点可能形成不同的状态。&lt;/p&gt;&#xA;&lt;p&gt;由此可见，处理多个事件时，事件的先后顺序将影响节点的状态，因此如何测量事件的顺序就成了分布式系统中的一个核心问题。一个自然的想法是，按照事件发生的&lt;em&gt;物理时间&lt;/em&gt;排序即可。我们将在后续章节了解物理时钟原理，从而遗憾地发现：在一个分布式系统中，通过对比多个节点之间的物理时间来判断事件发生的先后顺序并不精确，甚至有时候会发生错误，而一旦事件的顺序错乱，整个分布式集群的状态也就错乱了。&lt;/p&gt;&#xA;&lt;p&gt;如果物理时间不可行，那么还有什么办法决定一个分布式系统中事件的先后顺序？答案是&lt;em&gt;逻辑时钟&lt;/em&gt;。&lt;/p&gt;&#xA;&lt;p&gt;令人惊讶的是，前面我们一再谈及事件的先后顺序尤为重要，但是在分布式系统中，还可能出现两个事件无法判断其发生先后顺序的情况，这样的事件称为*&amp;ldquo;并发事件&amp;rdquo;*。为了解释事件的顺序，我们还需要了解两个数学中的定义：&lt;em&gt;偏序和全序&lt;/em&gt;。事实上，这两个数学定义应该深植在每个分布式系统工程师的心里，在后面我们还会一再见到这两个定义在分布式系统中的应用。&lt;/p&gt;&#xA;&lt;p&gt;我们将在本章中讨论以下的主题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;事件发生的先后顺序是如何影响系统的状态的；&lt;/li&gt;&#xA;&lt;li&gt;物理时间为何不能在分布式系统中用来衡量事件的先后顺序；&lt;/li&gt;&#xA;&lt;li&gt;逻辑时钟的理论基础和计算规则；&lt;/li&gt;&#xA;&lt;li&gt;偏序和全序的直观解释和定义；&lt;/li&gt;&#xA;&lt;li&gt;基于逻辑时钟延伸出来的向量时钟。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;状态事件和快照&#34;&gt;&#xA;  状态、事件和快照&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e7%8a%b6%e6%80%81%e4%ba%8b%e4%bb%b6%e5%92%8c%e5%bf%ab%e7%85%a7&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;在一些刑侦片里，经常看到这样的桥段：警方跟踪的一个嫌疑人，在某一天突然发生了一些变化，于是警方就会调取记录，来查看过去这一天里，嫌疑人去过的地方、见过的人、做过的事情，以了解到底是什么原因导致了嫌疑人的变化。&lt;/p&gt;&#xA;&lt;p&gt;在这里，&amp;ldquo;去过的地方、见过的人、做过的事情&amp;quot;就是一个一个的&lt;em&gt;事件&lt;/em&gt;，如果把嫌疑人看成一个&lt;em&gt;系统&lt;/em&gt;，昨天的嫌疑人，和经历了前面这些事件的嫌疑人，就是这个系统在不同时间的状态。（由此我们不妨思考一个哲学问题，如图1所示：过去的&amp;quot;我&amp;rdquo;，在经历一系列事件之后，才成为现在的&amp;quot;我&amp;quot;，这两个&amp;quot;我&amp;quot;是同一个&amp;quot;我&amp;quot;吗？）&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;过去的&amp;quot;我&amp;quot;，在经历一系列事件之后，才成为现在的&amp;quot;我&amp;quot;，这两个&amp;quot;我&amp;quot;是同一个&amp;quot;我&amp;quot;吗？&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/time/time-0.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;我们用一个简单的存储服务来继续解释状态和事件。如图2所示的一个存储服务，最初的状态为$\{x = 1\}$，在顺序执行了命令$set \ x = 2$和$set \ x = 3$之后，新的状态变成了$\{x = 3\}$。在这里，这两个命令的执行顺序尤为重要，如果调换这两个事件的执行顺序就变成了另一个状态$\{x = 2\}$。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;同样的初始状态，按照不同的顺序执行相同的事件，得到不同的状态&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/time/time-1.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;从以上的例子中我们也可以看到，在这里并没有提及事件执行的&lt;em&gt;物理时间&lt;/em&gt;，而是更关注事件执行的&lt;em&gt;顺序&lt;/em&gt;。之所以更关注事件之间的相对顺序而不是物理时间，是因为在分布式系统中，并不存在全局统一的物理时间，我们将在后续章节继续深入讨论物理时间。如果把系统看做一个大的状态机，事件就是改变这个状态机状态的操作。在一个状态机中，只要保证每一次能够按照&lt;em&gt;同样的顺序&lt;/em&gt;来执行事件，就能保证这个系统总是能到达同样的状态。&lt;/p&gt;&#xA;&lt;p&gt;按照相同顺序执行同样的事件就能得到相同的结果，这也是*状态机复制（state machine replication）*的核心思想：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;如果两个不同的进程，以同样的初始状态开始运行，按照相同的顺序来处理输入的数据，将得到同样的输出结果。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;把系统看做一个大的状态机，事件就是改变这个状态机状态的操作。在一个状态机中，只要保证每一次能够按照同样的顺序来执行事件，就能保证这个系统总是能到达同样的状态。&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/time/time-state-machine.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;备注&lt;/strong&gt;：我们在讨论了逻辑时钟以后，可以看到事件的&lt;em&gt;执行顺序&lt;/em&gt;就是某种意义上的&lt;em&gt;逻辑时间&lt;/em&gt;。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;另一方面，在不同时间节点上该存储的瞬时状态，就被称为在这个时间点的&lt;em&gt;快照（snapshot）&lt;/em&gt;。例如，在车水马龙的街道上，拍下一张照片，就是这个街道在那一瞬时时间点的快照。做为对比，事件是用于改变系统状态的&lt;em&gt;动态&lt;/em&gt;数据，而快照就是系统状态的&lt;em&gt;静态&lt;/em&gt;数据。&lt;/p&gt;&#xA;&lt;p&gt;到了这里，我们可以给出状态、事件和快照的直观解释：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;状态（status）&lt;/em&gt;：一个系统中所有数据的值，状态会随着事件发生改变；&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;事件（event）&lt;/em&gt;：能够改变系统状态的操作。在一个服务中，所有写请求（写入、更新、删除等），都可以称为事件；&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;快照（snapshot）&lt;/em&gt;：快照是一种特殊的状态，快照是&lt;em&gt;静止&lt;/em&gt;的状态，一旦确定了快照的时间，也就确定了快照的内容。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;如下图所示，如果分别在初始时、执行命令$set \ x = 2$之后和执行命令$set \ x = 3$之后，获取系统的快照数据，则会分别得到$\{x = 1\}$、$\{x = 2\}$和$\{x = 3\}$。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;快照是某一瞬时间的状态数据&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/time/time-2.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;从以上对状态、事件、快照的讲解可以看到，这几个概念都与时间强相关，尤其是事件与时间顺序相关。我们接下来看，如何在分布式系统中衡量在不同节点上事件的先后顺序。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;备注&lt;/strong&gt;：将保存系统的相关数据划分为状态、事件、快照这三类数据，也是分布式系统中进行数据复制时的做法，我们在这里强调了在分布式系统中，事件要在多个副本之间保持同样的&lt;em&gt;顺序&lt;/em&gt;，比这个条件更强的要求是&lt;em&gt;确定性&lt;/em&gt;，我们将在讨论副本间数据复制时继续讨论这个话题。我们还将在后续章节中看到，共识算法本质上就是在多个副本之间维护同样顺序的日志系统。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;物理时钟&#34;&gt;&#xA;  物理时钟&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e7%89%a9%e7%90%86%e6%97%b6%e9%92%9f&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 class=&#34;heading&#34; id=&#34;物理时间源和表示法&#34;&gt;&#xA;  物理时间源和表示法&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e7%89%a9%e7%90%86%e6%97%b6%e9%97%b4%e6%ba%90%e5%92%8c%e8%a1%a8%e7%a4%ba%e6%b3%95&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;如今的大多数计算机时钟采用的是&lt;em&gt;石英钟（Quartz clocks）&lt;/em&gt;。石英钟通常由电池供电，核心是一个石英晶体（通常是音叉形状）。当对石英晶体施加电压时，它会因压电效应产生机械振动，频率非常稳定（通常为32,768 Hz）。这种高精度振荡是石英钟计时的基础。石英钟每振荡H次，计算机内的定时器芯片就中断一次（被称为&lt;em&gt;时钟滴答（clock tick）&lt;/em&gt;）。中断处理程序会递增一个计数器，该计数器会记录从过去（纪元）开始的刻度数。知道了每秒的刻度数，我们就可以计算出年、月、日和时间等。&lt;/p&gt;</description>
    </item>
    <item>
      <title>第二章：分布式系统模型</title>
      <link>https://www.codedump.info/en/dist-system-cn/systemmodel/</link>
      <pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/systemmodel/</guid>
      <description>&lt;p&gt;在第一章中，我们对比过单机系统和分布式系统的区别，单机系统通过共享内存进行通信、有全局唯一的时钟，在出错时有确定的表现，在单机系统上编程是相对容易的。相较而言，分布式系统由多个节点组成，节点之间通过消息进行通信，这导致了分布式系统更加复杂。因此在开始设计分布式系统之前，需要一套描述系统运行模型的理论框架，开发者需要在设计时就明确系统的运行环境所满足的各种条件，不同的条件系统的实现难度不同。&lt;em&gt;分布式系统模型&lt;/em&gt;就是用于描述和分析分布式系统行为、属性和设计的理论框架。它们为研究分布式系统的通信、计算、故障、同步等特性提供了一种抽象方式，帮助设计者和研究者在复杂环境中理解系统行为并解决实际问题。&lt;/p&gt;&#xA;&lt;p&gt;在本章中，我们从两个著名的分布式实验两将军问题和拜占庭将军问题开始，展开对分布式系统模型的讨论。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;两将军问题&#34;&gt;&#xA;  两将军问题&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%b8%a4%e5%b0%86%e5%86%9b%e9%97%ae%e9%a2%98&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;两将军问题（Two Generals&amp;rsquo; Problem）&lt;/em&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;是分布式领域里的一个思想实验，旨在说明：试图通过&lt;em&gt;不可靠的连接&lt;/em&gt;来进行通信，协调分布式系统中的多节点之间的操作，会遇到的陷阱和挑战。&lt;/p&gt;&#xA;&lt;p&gt;实验中假设有两支军队，分别由一位将领负责给军队发号施令，两支军队准备进攻一座城市。这座城市的防守很坚固，需要两支军队一起行动才能胜利，而任何一方单独行动都将失败。两位将领需要协调一个共同发起攻击的时间。两支军队驻扎在城市的两处，中间有一个山谷将两支军队分开，他们之间通信的唯一方式就是派遣信使穿越山谷。然而，山谷被城市的守军控制，途经该山谷传递的消息有可能被俘。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;军队A1和军队A2不能直接看到对方，只能通过信使进行通信，但是他们的信使可能被军队B俘获&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/systemmodel/systemmodel-2-generals.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;虽然两位将军已经就进攻城市达成了共识，但是并没有就进攻时间达成共识。两位将军必须同时让自己的军队一起发起进攻才能取胜。因此，他们之间必须沟通协调一个发起进攻的时间。如果一位将军以为协调了一个进攻时间，而另一位将军却并不知情，导致只有一个军队发起进攻，那么这将是一个灾难性的失败，如下表所示：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;军队A1&lt;/th&gt;&#xA;          &lt;th&gt;军队A2&lt;/th&gt;&#xA;          &lt;th&gt;结果&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;不进攻&lt;/td&gt;&#xA;          &lt;td&gt;不进攻&lt;/td&gt;&#xA;          &lt;td&gt;无事发生&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;进攻&lt;/td&gt;&#xA;          &lt;td&gt;不进攻&lt;/td&gt;&#xA;          &lt;td&gt;军队A1失败&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;不进攻&lt;/td&gt;&#xA;          &lt;td&gt;进攻&lt;/td&gt;&#xA;          &lt;td&gt;军队A2失败&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;进攻&lt;/td&gt;&#xA;          &lt;td&gt;进攻&lt;/td&gt;&#xA;          &lt;td&gt;攻下城市&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;em&gt;表：发起进攻的可能结果&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;在这个实验中，&amp;ldquo;将军&amp;quot;就是分布式系统中的节点，它们之间通信的信使就是节点之间通信的连接，可以看到这个连接并不可靠：消息可能会被丢失。我们来看一下在这种不可靠连接的通信下，两个将军是否能达成一个关于进攻时间的共识。&lt;/p&gt;&#xA;&lt;p&gt;假如A1将军向A2将军传递消息&amp;quot;8月4日早上09:00发起进攻&amp;rdquo;，由于信使可能会被俘获，所以A1将军也并不确定消息可以被A2将军收到。这种不确定性导致A1犹豫不决，因为如果只有他发起进攻会导致失败。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;军队A1并不确定协调时间的消息能否被军队A2收到&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/systemmodel/systemmodel-2-generals-1.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;为了消除这种不确定性，A1和A2约定：收到消息后，要对应发出一个确认消息，让发送方确认消息已经被成功接收。然而，确认消息也同样可能被俘获，也不确定是否能到达接收方。这种不确定性导致A2犹豫不决，因为如果只有他发起进攻会导致失败。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;军队A2并不确定确认协调时间的消息能否被军队A1收到&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/systemmodel/systemmodel-2-generals-2.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;问题到了这里，进入了一个死胡同：因为无论发送多少确认消息，都没有办法保证两位将军有足够的自信，保证自己的信使没有被敌军俘获。&lt;/p&gt;&#xA;&lt;p&gt;从以上两个场景中可以看到，两将军问题中面临以下的难题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;消息传递的不确定性&lt;/em&gt;：将军A1发送消息给将军A2，告知进攻时间，但无法确认A2是否收到。即便A2收到并回复确认，A1也无法确认回复是否送达。这种确认过程可能无限循环，因为每次确认都需要进一步确认。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;无限确认问题&lt;/em&gt;：为了确保双方都同意进攻时间，A1需要A2的确认，A2需要A1确认收到A2的确认，以此类推。由于通信不可靠，每次确认都可能丢失，导致无法达成确定的共识。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;两将军问题最早由E.A.Akkoyunlu, K.Ekanadham和R.V.Huber在1975年发表的论文《SOME CONSTRAINTS AND TRADEOFFS IN THE DESIGN OF NETWORK COMMUNICATIONS》中提出，就在这篇文章的第73页中，描述两个黑帮之间的通信，同时论文也给出了这类问题无解的证明。1978年，在Jim Gray的《Notes on Data Base Operating Systems》一书中，被命名为*&amp;ldquo;两将军悖论（Two Generals Paradox）&amp;quot;*。&lt;/p&gt;&#xA;&lt;p&gt;两将军问题说明，在&lt;em&gt;不可靠通信环境&lt;/em&gt;下，无法通过&lt;em&gt;有限次消息交换&lt;/em&gt;达成绝对的共识。&lt;/p&gt;&#xA;&lt;p&gt;严格来说，两将军问题在理论上是无法完全绕过的，然而，在实际工程中，可以通过以下方法缓解或规避两将军问题的限制，使系统在实际场景中可行：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;以数量赢得概率&lt;/strong&gt;：这种方案的思路是，同一时间同时发送多个确认消息，如果接收方收到了其中的部分消息，就认为可以发起行动。例如在两将军问题中，任意一方不再是派出一名信使来传递消息，而是同时派出多名信使，例如同时派出100名信使，如果接收方能收到其中的10条消息（即1/10的概率），就认为收到消息。然而，从工程上而言，这样的做法性能不友好。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;引入超时和超时后的默认动作&lt;/strong&gt;：接收方可以设定一个默认接收消息时间，在这个时间仍然收不到对方的消息，就采取一个默认的安全动作。例如在两将军问题中，发出开始进攻的军队约好在早上九点发起进攻，但是如果在9点十分之前都没有收到进攻信号，就默认放弃进攻。这样的默认行为，虽然无法攻下城市，但是却不用冒单方面进攻导致失败的风险。在分布式系统的设计中，也有不少超时之后采用默认行为的设计，例如在Raft算法中，Follower节点在选举超时之前没有收到来自Leader节点的消息，就默认Leader节点出现故障，这时会采取发起新一轮的选举流程。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;考虑现实中两台计算机需要进行通信的场景。在这种情况下，通信的挑战仍然是不可靠的通信信道，所以这也是两将军问题的现实场景之一，而&lt;em&gt;TCP（Transmission Control Protocol）&lt;/em&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;协议可以认为是两将军问题的（部分）工程解。&lt;/p&gt;&#xA;&lt;p&gt;本书中并不打算完整阐述TCP协议的实现原理，感兴趣的可以参考相关书籍，这里重点阐述TCP如何在一个不可靠的通信信道上进行通信，以达到部分解决两将军问题的目标。&lt;/p&gt;&#xA;&lt;p&gt;TCP协议中，每个数据包都有唯一的*序列号（Sequence Number）*与之对应，接收端收到数据包之后，通过发送确认包文（Acknowledgment, ACK）来应答收到数据包。通过这个机制，TCP协议解决了数据包乱序到达的问题。考虑如下的场景：如果发送端向接收端分别发送了序列号为4和5的数据包，但是接收端首先收到的是序列号为5的数据包，如果马上向应用层投递seq=5的数据包，将导致数据乱序问题。为了避免乱序投递数据，内核会首先判断该数据包前面的所有数据包是否都已经被投递给应用层，只有在该数据包是当前最早的未投递数据包的情况下才会被投递。在这个场景中，seq=4的数据包还未收到，于是内核会首先将seq=5的数据包缓存起来，等待收到seq=4的数据包到来再一起投递。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;TCP协议通过序列号解决乱序问题。接收端首先接到seq=5的数据包，由于在它之前的seq=4数据包还没有接到，所以需要首先将seq=5的数据包缓存下来，直到收到seq=4的数据包，再将这两个数据包一起投递给应用层。&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/systemmodel/systemmodel-tcp-1.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;备注&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;在 TCP 协议栈的具体实现中，上述&amp;quot;缓存乱序数据&amp;quot;的行为并非无限进行的，而是受到*接收窗口（Receive Window, 简称rwnd）*的严格限制。&lt;/p&gt;&#xA;&lt;p&gt;TCP 接收端维护着一个滑动窗口结构。当收到乱序报文（如 seq=5）时，内核会检查该报文的序列号是否落在当前允许的接收窗口范围内：&lt;/p&gt;</description>
    </item>
    <item>
      <title>第一章：分布式系统概述</title>
      <link>https://www.codedump.info/en/dist-system-cn/introduction/</link>
      <pubDate>Tue, 24 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://www.codedump.info/en/dist-system-cn/introduction/</guid>
      <description>&lt;p&gt;在现代软件工程的演进历程中，从单机应用迈向分布式架构是一个关键的分水岭。这一跨越并非简单的硬件堆叠或代码迁移，而是一场涉及思维模式、设计哲学乃至对物理规律重新认知的深刻变革。&lt;/p&gt;&#xA;&lt;p&gt;作为全书的开篇，本章将带领读者走出单机系统的舒适区，直面分布式环境下的真实挑战。我们将首先厘清分布式系统的核心定义，剖析其相较于集中式系统的本质差异与优势；随后，我们将重点探讨这一领域中不可回避的技术难题——从不可靠的网络通信到破碎的全局时钟，再到部分失效带来的不确定性。最后，本章将阐述架构师在转型过程中所需完成的心智转变，从追求绝对的确定性转向在一致性与可用性之间寻求平衡的权衡艺术。理解这些基础概念与思维范式，是掌握后续章节中复杂算法与一致性模型的前提，也是构建高可靠、高扩展系统的基石。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;什么是分布式系统&#34;&gt;&#xA;  什么是分布式系统&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%bb%80%e4%b9%88%e6%98%af%e5%88%86%e5%b8%83%e5%bc%8f%e7%b3%bb%e7%bb%9f&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;在展开介绍分布式系统之前，我们先来看分布式系统的对立面：集中式的&lt;em&gt;单机系统（Monolithic）&lt;/em&gt;。在单机系统中，所有计算、存储和处理任务都集中在一台计算机或一个中心节点上。单机系统的主要特点有：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;单一节点&lt;/em&gt;：所有功能（如计算、数据存储、用户请求处理）都在一台机器或单一服务器上完成。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;共享资源&lt;/em&gt;：使用共享内存和本地资源，通信速度快，无需网络消息传递。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;单点控制&lt;/em&gt;：所有决策和数据管理由中心节点控制，无需节点间协调。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;单一时钟&lt;/em&gt;：机器上的所有运行进程，使用同一个时钟的时间。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;集中式单机系统的优点是：系统简单，请求、计算、存储、处理都在同一台节点上完成，不存在网络延迟、系统时间不一致等分布式系统才会出现的问题，但是缺点也很明显：单一机器节点的故障会导致整个系统的不可用，缺乏容错性；同时性能受限于单台机器的处理能力，只能通过升级硬件（&lt;em&gt;垂直扩展（scale up）&lt;/em&gt;）来提升性能。&lt;/p&gt;&#xA;&lt;p&gt;集中式系统的例子有：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;传统的单机数据库。&lt;/li&gt;&#xA;&lt;li&gt;单服务器的Web应用，处理所有的请求和数据存储。&lt;/li&gt;&#xA;&lt;li&gt;个人电脑运行的本地应用程序。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;然而，很多应用场景和要求并不是集中式系统能够解决的，例如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;高性能&lt;/em&gt;：集中式系统可以通过&lt;em&gt;垂直扩展&lt;/em&gt;的方式提升机器的硬件性能（采用更快的CPU、更大的内存等），以提升系统的处理能力。然而，垂直扩展的方式始终无法突破单一机器物理限制的天花板。与之对应的，分布式系统通过*横向扩展（scale out）*的方式，通过将多台机器组合在一起形成一个系统对外提供服务。当发现当前系统扔不满足需求时，可以继续在这个系统上增加机器提升处理能力。例如，在2003年的论文[GFS]中，Google讲述了使用廉价的硬件搭建分布式文件系统的经验。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;高扩展性&lt;/em&gt;：和性能上遇到的瓶颈一样，随着数据量的增大，集中式系统在数据存储上也会遇到瓶颈。而分布式系统通过将数据分布存储在多台机器上，可以在系统无法承载更多数据的时候通过增加节点以提供更大的存储容量。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;高可用性&lt;/em&gt;：现在绝大多数的服务，都要求系统能够保持7x24小时运行，表 system-avaliability 给出了不同的系统可用性需要满足的全年不可用时间。集中式系统中，只有一台机器节点提供服务，这很容易导致*单点故障（single point of failure）*问题：一台机器的故障就导致系统整体不可用。在分布式系统中，通过多台机器构成一个系统，提供了更多的冗余度，在某些机器出现故障时，系统也能够继续运行。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;在地理位置上离用户更近&lt;/em&gt;：在许多情况下，要考虑用户所处的地理位置。例如，一个社交应用，既有来自亚洲的用户，也有来自欧洲的用户，在这种情况下，可能需要分别在不同的大洲建立服务，这些分布于不同地区的服务之间需要协同：例如，用户ID必须是全局唯一的。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;合规要求&lt;/em&gt;：随着全球数据隐私法规的日益严格，为了满足*数据驻留（Data Residency）&lt;em&gt;与&lt;/em&gt;数据主权（Data Sovereignty）*的合规性要求，系统架构必须支持跨地域的分布式部署。通过在不同国家或司法管辖区内建立独立的数据中心节点，实现数据的本地化存储与处理，从而确保用户数据严格限制在其所属的地理边界内。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;资源共享&lt;/em&gt;：这里的&amp;quot;资源&amp;quot;不仅指硬件资源，也包括软件资源。例如，在进行大量计算时，为了计算计算流程，需要将计算划分为多个任务，分发到多个机器上同时执行；通过在线共享文档这样的产品，多个用户可以同时浏览、修改文档，无需将文件下载到硬盘上进行修改，同时也不再需要在个人电脑上安装处理软件。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;业务属性&lt;/em&gt;：最后，除去以上集中式系统由于物理限制，要求使用分布式架构以外，某些业务天然就必须满足分布式特性。例如，电商服务中浏览、购买商品在一个平台，但是支付账单要到另外的系统（例如支付平台、银行等）完成，在这种业务中，系统天然就必须是分布式的。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;垂直扩展（scale up）通过增强单机的性能（采用更快的CPU、更大的内存、更大的硬盘）来提升单机系统的处理能力，水平扩展（scale out）通过添加更多的机器来提升系统的处理能力。&#34; src=&#34;https://www.codedump.info/media/dist-system-cn/introduction/scale-up-out.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;有了和单机系统的横向对比，我们可以来看看分布式系统的定义了。本书采用了在[Coulouris]中提到分布式系统的定义：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;分布式系统是指各组件分布于网络互联的计算机上，通过相互传递消息来实现通信并协调运作的系统（A distributed system is a system whose components are located on different networked computers, which communicate and coordinate their actions by passing messages to one another.）。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;从上面的定义可以看出：*分布式系统（Distributed System）*是由一组通过网络连接的独立计算机（节点）组成的系统，这些节点协作完成共同任务。每个节点拥有自己的处理器、内存和存储，节点间通过消息传递通信，无共享内存。&lt;/p&gt;&#xA;&lt;p&gt;组成分布式系统中的每台计算机被称为&lt;em&gt;节点（node）&lt;/em&gt;。在这里，&amp;ldquo;计算机&amp;quot;的概念非常宽泛：可能是个人电脑，也可能是数据中心的服务器、移动设备、联网的各类型IOT设备（如汽车、智能家电等等）。总而言之，&amp;ldquo;节点&amp;quot;可能是任何联网通信的设备。&lt;/p&gt;&#xA;&lt;p&gt;分布式系统旨在实现高可用性、可扩展性和容错性，分布式系统通常具有以下特点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;多节点协作&lt;/em&gt;：分布式系统由多个物理上分散的节点组成来共同工作，这些节点可能位于不同地理位置，也可能是独立的进程。节点之间的地址空间互不关联，因此不会将共享内存的多处理器做为分布式系统的代表。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;网络通信&lt;/em&gt;：节点通过网络（如互联网或局域网）进行通信，网络消息的延迟情况取决于信息链路的具体情况。例如，光缆可能被挖断、网络可能会分区、消息延迟会突然间陡增，这些都是在分布式通信中常见的故障情况。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;无全局时钟&lt;/em&gt;：节点间缺乏统一时间，需通过协议同步。&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;协同工作&lt;/em&gt;：组成一个分布式系统中的多个节点，相互通信协同完成一个共同的目标。例如，三个节点组成的存储系统，共同提供强一致的数据服务，在一个节点宕机的情况下，仍然能继续工作；再比如，将一个大的任务划分多个小任务，由多个节点共同执行，最后再汇总得到最后的执行结果。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;表 vs-dist-system 和图 introduction/vs 列举了分布式系统和集中式系统的关键特性对比。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Rockraft: A Strongly-Consistent KV Storage Framework Based on OpenRaft and RocksDB</title>
      <link>https://www.codedump.info/en/post/20260412-rockraft/</link>
      <pubDate>Sun, 12 Apr 2026 09:59:41 +0800</pubDate>
      <guid>https://www.codedump.info/en/post/20260412-rockraft/</guid>
      <description>&lt;h2 class=&#34;heading&#34; id=&#34;motivation&#34;&gt;&#xA;  Motivation&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#motivation&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The Redis protocol has become the de facto standard for key-value storage protocols. Beyond the official Redis implementation, we have seen various compatible alternatives:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/valkey-io/valkey&#34;&gt;Valkey&lt;/a&gt;: The Linux Foundation&amp;rsquo;s official fork of Redis 7.2, licensed under BSD. It is the community&amp;rsquo;s true open-source alternative after Redis switched to SSPL, fully compatible with the Redis protocol and persistence mechanisms.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/dragonflydb/dragonfly&#34;&gt;Dragonfly&lt;/a&gt;: A modern multi-threaded in-memory database pursuing extreme performance, offering up to 25x the throughput of Redis with lower tail latency, but licensed under BSL (transitioning to Apache 2.0 after 4 years).&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/snapchat/keydb&#34;&gt;KeyDB&lt;/a&gt;: A multi-threaded Redis fork maintained by Snapchat, adding Active Replication and Flash storage extensions on top of 100% Redis API compatibility, though updates have slowed down.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/apache/kvrocks&#34;&gt;Kvrocks&lt;/a&gt;: An Apache top-level project, a distributed KV storage based on RocksDB that persists data to disk, supporting dozens of terabytes at 1/5–1/10 the cost of in-memory solutions, suitable for large-capacity, low-cost scenarios.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;However, none of the above implementations have gone further on strong consistency in distributed systems. In this dimension, they all adopt the eventual consistency of the native Redis implementation.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
