如何设计一个分布式ID生成器保证ID按时间有序?

很多业务有生成唯一 ID 并作为数据库主键的需求。数据库会在这个字段上建立聚集索引(参考 MySQL InnoDB),即该字段会影响各条数据再物理存储上的顺序。 ID还要尽可能短,节省内存,让数据库索引效率更高。基本上64位整数能够满足绝大多数的场景,但是如果能做到比64位更短那就更好了。需要根据具体业务进行分析,预估出ID的最大值,这个最大值通常比64位整数的上限小很多,于是我们可以用更少的bit表示这个ID。 查询的时候,往往有分页或者排序的需求,所以需要给每条数据添加一个时间字段,并在其上建立普通索引(Secondary Index)。但是普通索引的访问效率比聚集索引慢,如果能够让ID按照时间粗略有序,则可以省去这个时间字段。为什么不是按照时间精确有序呢?因为按照时间精确有序是做不到的,除非用一个单机算法,在分布式场景下做到精确有序性能一般很差。 这就引出了 ID 生成的三大核心需求: 全局唯一 按照时间粗略有序 尽可能短 下面介绍一些常用的生成 ID 的方法。 UUID UUID 是一类算法的统称,具体有不同的实现。UUID 的优点是每台机器可以独立产生 ID,理论上保证不会重复,所以天然是分布式的;缺点是生成的 ID 太长,不仅占用内存,而且索引查询效率低。 MongoDB 的 ObjectId 使用的就是 UUID 算法。生成的 ObjectId 占 12 个字节,由以下几个部分组成, 4 个字节表示的 Unix timestamp 3 个字节表示的机器的 ID 2 个字节表示的进程 ID 3 个字节表示的计数器 使用数据库 可以使用数据库中的自增主键来生成ID。将ID生成的过程交给数据库管理,每个节点向数据库插入记录时,数据库会自动分配一个唯一的ID。通过使用数据库的自动递增功能,可以保证ID的唯一性和粗略有序性。 在分布式环境下,可以使用多台数据库协同工作生成 ID。假设用 8 台MySQL服务器协同工作,第一台 MySQL 初始值是 1,每次自增 8,第二台 MySQL 初始值是 2,每次自增 8,依次类推。在数据库前面添加一个负载均衡,每来一个请求,由负载均衡随机地将请求发给 8 台 MySQL 中的任意一个,然后返回一个ID。 Flickr就是这么做的,仅仅使用了两台 MySQL 服务器。可见这个方法虽然简单无脑,但是性能足够好。不过要注意,在 MySQL 中,不需要把所有 ID 都存下来,每台机器只需要存一个 MAX_ID 就可以了。这需要用到 MySQL 的一个 REPLACE INTO 特性。 Flickr 的实现方式如下。 Tickets64 表结构如下: CREATE TABLE `Tickets64` ( `id` bigint(20) unsigned NOT NULL auto_increment, `stub` char(1) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `stub` (`stub`) ) ENGINE=InnoDB SELECT * from Tickets64 返回一行,如下所示:...

分布式基础知识

什么是分布式系统? 分布式系统是指由多个独立的计算机节点(或服务器)通过网络相互连接和协作,共同完成某个任务或提供某个服务的系统。在分布式系统中,各个节点可以同时进行计算、存储和通信,并通过消息传递等方式进行协调和同步。 分布式系统的设计目标是提高系统的性能、可靠性和可扩展性,同时减少单点故障和提高系统的容错性。通过将任务和数据分布到多个节点上,分布式系统可以实现更高的并行性和处理能力。此外,分布式系统还可以提供更好的负载均衡,以应对不断增长的工作负载。 分布式系统的主要特征 分布式系统具有以下主要特征: 分布性:分布式系统中的计算机节点分布在不同的物理或逻辑位置上,可以是同一局域网内的多台机器,也可以是分布在全球不同地区的服务器。 并行性:分布式系统中的节点可以同时进行计算和处理任务,从而实现并行处理和提高系统的性能。并行性可以通过将任务划分为子任务,并在不同节点上并行执行来实现。 通信:分布式系统通过网络进行节点之间的通信和数据传输,以实现协作和信息交换。节点之间的通信可以通过消息传递、远程过程调用(RPC)或分布式共享内存等方式实现。 缺乏全局时钟:由于节点之间的通信延迟和不可靠性,分布式系统往往无法依赖全局时钟来进行同步。因此,分布式系统需要采用一些分布式算法来实现一致性和协调,如分布式锁、一致性协议等。 容错性:分布式系统需要具备容错机制,以应对节点故障、网络故障或其他异常情况。容错性的实现通常包括备份和冗余,例如使用冗余节点、数据复制和副本机制,以确保系统的可用性和数据的完整性。 可扩展性:分布式系统应具备良好的可扩展性,即能够方便地扩展节点数量和处理能力,以适应不断增长的工作负载。可扩展性的实现可能包括水平扩展、垂直扩展、负载均衡等技术手段。 不确定性:由于节点之间的通信延迟和不可靠性,分布式系统中的操作可能存在不确定性。例如,消息传递可能会有延迟,网络可能会发生分区,导致节点之间的信息不一致。因此,分布式系统需要考虑和处理这种不确定性情况。 这些特征使得分布式系统能够实现高性能、高可用性和可扩展性,但也带来了挑战,如一致性问题、并发控制、故障处理等。因此,在设计和开发分布式系统时,需要考虑这些特征,并选择合适的技术和算法来解决相关问题。 分布式系统面临的问题 分布式系统面临的问题包括以下几个方面: 一致性问题:在分布式系统中,数据的复制和同步是一个挑战。节点之间的数据复制可能存在延迟和不一致性,需要采用合适的复制策略和同步机制,如主从复制、多主复制、一致性哈希等。 并发控制:在分布式系统中,多个节点同时对共享资源进行读写操作可能导致并发冲突和数据不一致。并发控制机制,如分布式锁、版本控制、乐观并发控制等,用于确保对共享资源的访问是安全和有序的。 故障处理和容错性:分布式系统中的节点可能会发生故障,如节点崩溃、网络分区等,这可能导致数据丢失或系统不可用。为了保证系统的可用性和数据的完整性,需要采用故障检测和恢复机制,如心跳检测、故障转移、数据备份等。 分布式系统的可扩展性:随着用户和数据量的增长,分布式系统需要能够方便地扩展节点数量和处理能力。设计和实现具有良好可扩展性的分布式系统需要考虑负载均衡、数据分片、分布式缓存等技术手段。 监控和管理:在分布式系统中,由于节点数量众多,监控和管理变得更加复杂。需要建立有效的监控系统来收集和分析系统的运行状态和性能指标,并采用自动化的管理工具来管理节点、配置和部署系统。 衡量分布式系统的指标 衡量分布式系统性能和质量的指标可以包括以下几个方面: 可用性:指系统处于正常运行状态的时间比例。如果用户无法访问系统,则称系统不可用。通常以百分比(如99.9%)表示。较高的可用性意味着系统更可靠,用户能够更稳定地访问和使用系统。 从技术角度来看,可用性主要与容错性有关。因为故障发生的概率随着组件数量的增加而增加,系统应该能够进行补偿,以确保随着组件数量的增加,系统的可靠性不会降低。 容错性是指系统在发生故障时仍能以明确定义的方式继续运行的能力。 **可扩展性:衡量分布式系统在面对不断增长的工作负载时,能够方便地扩展节点数量和处理能力的能力。**可扩展性可以包括水平扩展(增加节点数量)和垂直扩展(增加节点的处理能力)。 **一致性:表示分布式系统中的数据副本在不同节点之间保持一致的程度。**较高的一致性意味着系统中的数据在不同节点上的访问结果是相同的,而较低的一致性可能导致数据不一致的情况。 **可靠性:表示分布式系统在面对节点故障、网络故障或其他异常情况时能够继续正常运行的能力。**可靠性通常与容错性相关,包括故障检测、故障转移、数据备份等机制。 性能:是指计算机系统在使用的时间和资源相对于所完成的有用工作量来衡量的特征。 吞吐量:表示分布式系统在单位时间内能够处理的请求或事务数量。吞吐量越高,系统的处理能力越强,能够更高效地处理用户请求和数据处理任务。 响应时间:表示分布式系统对于用户请求的响应速度。较低的响应时间意味着系统能够更快地响应用户请求,提供更好的用户体验。 并发性能:衡量系统在处理并发请求时的能力。较好的并发性能意味着系统能够同时处理多个请求,并保持较低的响应时间和高吞吐量。 总结 任何计算机系统都需要完成两个基本任务: 存储 计算 随着任务规模变大: 1、使用单台计算机,硬件升级,成本过高 2、使用多台计算机,使用中档、大众化的硬件,成本降低 使用多台计算机存在如下特征: 分布性。计算机节点分布在不同的位置。 并行性。计算机节点可以同时进行计算和处理任务。 可扩展性:可以添加节点,提高处理能力。 不确定性。由于节点之间通信有延迟或者存在故障,会导致消息传递有延迟、节点信息存在不一致 使用多台计算机面临的问题: 节点的数量。 数量变多,增加系统故障概率,可能导致数据丢失或系统不可用。 数量变多,并行读写数据,会导致并发冲突和数据不一致。 节点之间的距离。 每个节点的时钟不同步,会导致网络延迟 节点之间数据需要复制和同步,会导致数据不一致性。 节点之间距离变远,会降低某些操作的性能。 所以,分布式系统要提供以下能力: 可用性。 可扩展性。可以增加节点数量和提高节点处理能力。 一致性。提高数据一致性。 可靠性。系统故障时,仍然能正常运行。 性能。

[译]给年轻的工程师们的关于分布式系统的一些笔记

我一直在思考分布式系统工程师在工作中学到的教训。我们大部分的教导都来自于在生产环境中犯过的错误留下的伤痕。这些伤痕固然是有用的提醒,但让更多的工程师能够完整地保留手指会更好。 新的系统工程师在自我学习中会遇到分布式计算的谬论和CAP定理。但这些都是抽象的概念,缺乏针对经验不足的工程师直接可行的建议。让人惊讶的是,新工程师在开始工作时所了解到的背景信息是如此之少。 下面是我作为一名分布式系统工程师学到的一些经验教训,值得告诉新工程师。其中一些经验是微妙的,一些是令人惊讶的,但没有一条是有争议的。这个列表是为了引导新的分布式系统工程师思考他们所从事领域的问题,虽然不是全面的,但是是一个很好的开始。 这个列表最糟糕的特点是它主要关注技术问题,很少讨论工程师可能遇到的社交问题。由于分布式系统需要更多的机器和资本,它们的工程师往往需要与更多的团队和更大的组织合作。社交问题通常是任何软件开发者工作中最困难的部分,也许对于分布式系统的开发来说尤其如此。 我们的背景、教育和经验使我们倾向于采用技术解决方案,即使社交解决方案可能更高效、更令人满意。让我们试着纠正这一点。与计算机相比,人们并不那么挑剔,即使他们的接口没有那么标准化。 好了,我们开始吧。 分布式系统是不同的,因为它们经常失败。 当被问及是什么将分布式系统与软件工程的其他领域区分开来时,这位新工程师经常引用延迟,认为这是使分布式计算变得困难的原因。 但他们错了。分布式系统工程的区别在于失败的概率,更糟糕的是,部分失败的概率。如果格式良好的互斥锁解锁失败并出现错误,我们可以假设该过程不稳定并使其崩溃。但是,分布式互斥锁解锁的失败必须内置到锁定协议中。 没有从事过分布式计算的系统工程师会想出一些想法,比如“好吧,它只是将写入发送到两台机器”或“它会不断重试写入,直到它成功”。这些工程师还没有完全接受(尽管他们通常在理智上认识到)网络系统比只存在于一台机器上的系统更容易失败,而且故障往往是部分的而不是全部的。 其中一个写入可能会成功,而另一个写入失败,那么现在我们如何获得一致的数据视图呢?这些部分故障更难推理。 交换机故障、垃圾回收暂停导致领导者“消失”、套接字写操作似乎成功但实际上在另一台机器上失败、一台机器上的慢速磁盘驱动引起整个集群中的通信协议变慢等等。从本地内存读取比通过几个交换机读取更稳定。。 为失败而设计! 编写健壮的分布式系统比编写健壮的单机系统成本更高。 与单机解决方案相比,创建强大的分布式解决方案需要更多的资金,因为只有许多计算机才会发生故障。虚拟机和云技术使分布式系统工程更便宜,但不像能够在您已经拥有的计算机上进行设计、实施和测试那样便宜。并且存在难以在单台机器上复制的故障条件。 无论是因为它们只发生在比共享机器上可以容纳的数据集大小大得多的数据集上,还是在数据中心的网络条件下,分布式系统往往需要实际的(而不是模拟的)分发来清除它们的错误。当然,模拟非常有用。 健壮的开源分布式系统远不如健壮的单机系统常见。长时间运行多台机器的成本是开源社区的负担。业余爱好者和业余爱好者是开源软件的引擎,他们没有可用的财务资源来探索或解决分布式系统将遇到的许多问题。业余爱好者在空闲时间使用他们已经拥有的机器编写开源代码以取乐。 要找到愿意启动、维护和支付一堆机器的开源开发人员要困难得多。 为公司实体工作的工程师已经填补了部分空缺。但是,其组织的优先级可能与组织的优先级不一致。 虽然开源社区中的一些人已经意识到了这个问题,但它还没有得到解决。这很难。 协调非常困难。 尽可能避免协调机器。这通常被描述为“水平可伸缩性”。水平可扩展性的真正诀窍是独立性——能够将数据传送到机器上,从而将这些机器之间的通信和共识保持在最低限度。每当两台机器必须就某件事达成一致时,服务就会变得更难实现。 信息的传播速度是有上限的,网络通信比你想象的要脆弱,你对什么是共识的想法可能是错误的。在这里,了解 Two Generals 和 拜占庭将军 的问题很有用。(哦,Paxos真的很难实现;这不是脾气暴躁的老工程师认为他们比你更了解。) 如果你能把你的问题放在内存中,那可能是微不足道的。 对于分布式系统工程师来说,一台机器的本地问题很容易解决。当数据距离几个开关而不是几个指针取消引用时,弄清楚如何快速处理数据会更难。在分布式系统中,自计算机科学开始以来就记录的陈旧效率技巧不再适用。 对于在单台机器上运行的算法,有大量的文献和实现,因为大部分计算都是在单一的、不协调的机器上完成的。对于分布式系统来说,存在的数量要少得多。 “很慢”是你调试过的最难的问题。 “速度慢”可能意味着执行用户请求所涉及的一个或多个系统速度较慢。这可能意味着跨多台计算机的转换管道的一个或多个部分速度较慢。“它很慢”很难,部分原因是问题陈述没有提供很多关于缺陷位置的线索。部分故障,即那些没有出现在你通常查找的图表上的故障,潜伏在一个黑暗的角落里。 而且,在退化变得非常明显之前,您将无法获得那么多的资源(时间、金钱和工具)来解决它。Dapper 和 Zipkin 的出现是有原因的。 **在整个系统中实现反压机制。**反压是服务系统向请求系统发出故障信号,并由请求系统处理这些故障以防止自身和服务系统过载。设计反压意味着在负载过重和系统故障时限制资源使用。这是创建健壮的分布式系统的基本构建块之一。 实现反压通常涉及以下两种方式之一:要么将新消息丢弃,要么在资源受限或发生故障时将错误返回给用户(并在两种情况下增加指标)。对于与其他系统的连接和请求,超时和指数退避也是至关重要的。 如果没有反压机制,可能会发生级联故障或意外消息丢失。当一个系统无法处理另一个系统的故障时,它倾向于将故障传播给依赖它的另一个系统。 寻找实现部分可用性的方法。 部分可用性是指即使系统的某些部分发生故障,仍能返回一些结果。 搜索是一个理想的案例来探讨这个问题。搜索系统在结果质量和用户等待时间之间进行权衡。一个典型的搜索系统会设置一个时间限制,如果在搜索所有文档之前超过了时间限制,它会返回已经收集到的结果。这使得搜索在面对间歇性减速和错误时更容易扩展,因为这些故障被视为无法搜索所有文档的情况。系统允许返回部分结果给用户,并增加了其弹性。 再以Web应用程序中的私密消息功能为例。无论你做什么,私密消息的存储机器都可能同时宕机,用户会注意到这一点。那么在这个系统中,我们希望出现什么样的部分故障呢? 这需要一些思考。一般来说,人们对于无法使用私密消息功能(或许是其他一些用户也无法使用)会更容忍,而对于所有用户中有一些消息丢失则更为不满意。如果服务过载或其中一台机器故障,只让一小部分用户无法使用比让更大比例的用户丢失数据更可取。除此之外,我们可能不希望一个无关的功能(比如公共图片上传)受到影响,只因为私密消息功能出现问题。我们愿意付出多少努力来保持这些故障域的独立? 能够在部分可用性中识别这些权衡是很有帮助的。 指标是完成工作的唯一途径。 公开指标(如延迟百分比、特定操作的计数器增加、变化速率等)是弥合您对系统在生产环境中所做的假设与实际情况之间差距的唯一途径。了解系统在第20天的行为与第15天的行为有何不同,是成功工程和失败巫术之间的区别。当然,指标是了解问题和行为的必要手段,但并不足以知道接下来该做什么。 稍微提一下日志记录。日志文件是很有用的,但它们往往会欺骗人。例如,很常见的情况是几个错误类别的日志记录占据了日志文件的很大比例,但实际上在请求中的比例非常低。因为在大多数情况下记录成功是多余的(并且在大多数情况下会耗尽磁盘空间),而且工程师经常错误地猜测哪些错误类别是有用的,所以日志文件中充斥着各种奇怪的信息。最好以一种假设有人会阅读日志但没有看过代码的方式进行日志记录。 我见过很多次由于另一位工程师(或者我自己)过于强调日志中的一些奇怪现象而导致故障延长,而没有先将其与指标进行对比。我还见过另一位工程师(或者我自己)从少数几行日志中推断出整套失败行为的情况。但请注意:a) 我们之所以记住这些成功案例,是因为它们非常罕见;b) 除非指标或实验证实了故事,否则你并不是福尔摩斯(Sherlock)。 使用百分位数而不是平均值。 在绝大多数分布式系统中,百分位数(50th、99th、99.9th、99.99th)比平均值更准确、更有信息量。使用平均值假设正在评估的指标遵循正态分布曲线,但在实践中,这只适用于少数工程师关心的指标。 “平均延迟” 是一个常见的报告指标,但我从未见过一个延迟遵循正态分布曲线的分布式系统。如果指标不遵循正态分布曲线,平均值就没有意义,会导致错误的决策和理解。通过使用百分位数来避免这个陷阱。默认使用百分位数,你将更好地了解用户真正看待你的系统的方式。 学会估算你的容量。 因此,你将会知道一天有多少秒。知道你需要多少台机器来执行一个任务是一个持久系统和一个在工作开始3个月后需要被替换的系统之间的区别。或者更糟糕的是,在你完成将其投入生产之前就需要被替换。 以推文为例。在一台普通的机器上,你可以将多少个推文ID存放在内存中? 嗯,到2012年底,一台典型的机器有24 GB的内存,你需要4-5 GB的开销来运行操作系统,另外还需要至少几个GB来处理请求,而一个推文ID占用8个字节。这是你可能会进行的粗略计算。Jeff Dean的《每个人都应该知道的数字》幻灯片是一个很好的期望设定工具。 特性标志(Feature flags)是基础设施推出的方式。 特性标志是产品工程师在系统中推出新功能的常用方式。特性标志通常与前端A/B测试相关联,用于向部分用户展示新的设计或功能。但它们也是替换基础设施的强大方式。 很多项目因为选择了“大切换”或一系列“大切换”,然后由于发现了太晚的错误而被迫回滚,从而导致失败。通过使用特性标志,你将增强对项目的信心并减轻失败的成本。 假设你要从单一数据库迁移到一个隐藏了新存储解决方案细节的服务。使用特性标志,你可以逐步将写操作转移到新服务,与对旧数据库的写操作并行进行,以确保其写路径的正确性和速度足够快。在写路径达到100%并将数据回填到服务的数据存储完成后,你可以使用单独的特性标志开始从该服务读取,而不在用户响应中使用该数据,以检查性能问题。另一个特性标志可以用于比较从旧系统和新系统读取的数据。最后一个标志可以用于逐步增加从新系统进行“真实”读取操作。 通过将部署拆分为多个步骤,并通过特性标志提供快速和部分反应,你可以更容易地在扩展过程中发现错误和性能问题,而不是在“一次性发布”时发现。如果出现问题,你只需立即将特性标志设置降低到较低(可能是零)的设置。通过调整速率,你可以在不同的流量量级下进行调试和实验,知道任何问题都不会造成灾难。使用特性标志,你还可以选择其他迁移策略,例如基于每个用户的方式将请求转移到新系统,以提供对新系统的更好洞察。当你的新服务仍在原型阶段时,你可以将特性标志设置为较低,以减少新系统的资源消耗。 现在,特性标志对于经典训练的开发人员或新工程师来说可能听起来像是一堆条件语句的可怕混乱。而使用特性标志意味着接受多个基础设施和数据版本是一种常态,而不是罕见情况。这是一个深刻的教训。在单机系统中有效的方法在面对分布式问题时有时会失败。 特性标志最好被理解为一种权衡,以在代码和一个系统中交换局部复杂性,以获得全局的简单性和弹性。 **明智地选择ID空间。 **你为系统选择的ID空间将塑造你的系统。 要获取数据所需的ID数量越多,就越有选择将数据进行分区的选项。要获取数据所需的ID数量越少,消费你的系统输出就越容易。 以Twitter API的第一个版本为例。所有获取、创建和删除推文的操作都是基于每个推文的单个数字ID进行的。推文ID是一个简单的64位数字,不与任何其他数据相关联。随着推文数量的增加,人们意识到,如果将同一用户的所有推文存储在同一台机器上,可以有效地构建用户的推文时间线和其他用户订阅的时间线。 但公共API要求每个推文只能通过推文ID进行访问。要按用户对推文进行分区,需要构建一个查找服务,它知道哪个用户拥有哪个推文ID。如果必要,这是可行的,但成本不可忽视。 另一种选择是在任何推文查找时要求用户ID,并且最初只是使用推文ID进行存储,直到用户分区存储上线。另一种选择是在推文ID本身中包含用户ID,这样做的代价是推文ID不再具有k-sortable和数字的特性。 要注意在ID中明确和隐含地编码了哪种类型的信息。客户端可能利用ID的结构来去匿名化私人数据,以意想不到的方式爬取你的系统(自增ID通常是一个痛点),或进行其他一系列攻击。 利用数据局部性。 将数据的处理和缓存与其持久存储保持靠近,处理效率更高,同时保持缓存一致性和快速性更容易。与指针解引用和fread(3)相比,网络故障和延迟更多。 当然,数据局部性意味着在空间上靠近,但也意味着在时间上靠近。如果多个用户几乎同时进行相同的昂贵请求,也许可以将它们的请求合并为一个请求。如果在相近的时间内发出了多个相同类型的数据请求,可以将它们合并为一个更大的请求。这样做通常可以降低通信开销并更容易进行故障管理。 **将缓存数据写回持久存储是不好的。 **这种情况在比想象中更多的系统中发生。尤其是那些最初由缺乏分布式系统经验的人设计的系统。你将继承许多具有此缺陷的系统。如果实施者谈到“俄罗斯套娃缓存”,你很有可能遇到非常明显的错误。这个条目本可以从列表中省略,但我对此特别痛恨。这种缺陷的常见表现是用户信息(例如屏幕名称、电子邮件和哈希密码)神秘地恢复到先前的值。 计算机的能力超乎你的想象。 现在的现场存在很多关于机器能力的错误信息,这些信息来自于没有太多经验的从业者。 在2012年底,轻型Web服务器拥有6个或更多处理器,24GB内存和比你能使用的更多磁盘空间。在现代语言运行时环境中,一个相对复杂的CRUD应用程序在单个机器上可以在几百毫秒内轻松处理数千个请求每秒。这还只是下限。在大多数情况下,每台机器每秒处理数百个请求并不值得夸耀。 获得更高的性能并不难,尤其是如果你愿意对应用程序进行性能分析,并根据测量结果引入效率。 **利用CAP定理对系统进行批判。 **CAP定理不能用作构建系统的基础。它不是一个可以作为第一原则并从中推导出一个可行系统的定理。它的适用范围过于广泛,可能的解决方案空间也过于宽泛。 然而,CAP定理非常适合用于对分布式系统设计进行批判,并理解需要做出的权衡。通过对系统设计进行迭代,考虑CAP对其子系统施加的约束,最终可以得到更好的设计。作业中,将CAP定理的约束应用于俄罗斯套娃缓存的实际实现。 最后需要注意的是:在一致性(C)、可用性(A)和分区容忍性(P)中,不能选择CA。 **提取服务。 **这里的"服务"指的是"一个包含高级逻辑的分布式系统,通常具有请求-响应式的API"。要留意那些如果存在于一个单独的服务中而不是你的系统中,将更容易进行的代码更改。 提取出一个服务提供了封装的好处,通常与创建库相提并论。然而,提取出一个服务改进了创建库的方式,因为它允许更快、更容易地部署变更,而不像升级客户系统中的库那样麻烦。(当然,如果提取出的服务难以部署,那么客户系统将变得更容易部署。)这种便利是由于较小、提取出的服务中的代码和操作依赖较少,并且其创建的严格边界使得很难"走捷径",而库则允许这种走捷径。这些走捷径通常会使迁移内部或客户系统到新版本变得更加困难。 当存在多个客户系统时,使用服务的协调成本也比使用共享库要低得多。即使不需要进行API更改,升级库也需要协调每个客户系统的部署。如果部署次序颠倒,可能会导致数据损坏(而且很难预测这种情况),这使得升级库变得更加困难。如果客户系统由不同的维护者负责,升级库的社交协调成本也比部署服务更高。让其他人意识到并愿意升级是非常困难的,因为他们的优先事项可能与你的不一致。 典型的服务使用案例是隐藏一个将要进行变更的存储层。提取出的服务具有更方便且表面积更小的API,与其前端的存储层相比。通过提取服务,客户系统无需了解迁移到新的存储系统或格式的复杂性,只需要评估新服务中肯定会发现的与新存储布局相关的错误。 在执行此操作时,需要考虑许多操作和社交问题。在这里无法对它们进行充分阐述。需要撰写另一篇文章对此进行详细说明。 我对我的审稿人Bill de hÓra、Coda Hale、JD Maturen、Micaela McDonald和Ted Nyman表示衷心感谢。你们的见解和关心是无价的。...

[译]《分布式系统:为了乐趣和利益》6.进一步阅读和附录

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者Mikito Takada撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 6. 进一步阅读和附录 如果您已经做到了这一点,谢谢您。 如果您喜欢这本书,请在 Github(或 Twitter)上关注我。我很高兴看到我产生了某种积极的影响。 “创造的价值比你获取的价值更多”等等。 非常感谢:logpath、alexras、globalcitizen、graue、frankshearar、roryokane、jpfuentes2、eeror、cmeiklejohn、stevenproctor eos2102 和 steveloughran 的帮助!当然,任何错误和遗漏都是我的错! 值得注意的是,我关于最终一致性的章节相当以伯克利为中心;我想改变这一点。我还跳过了一个重要的时间用例:一致的快照。我还应该扩展几个主题:即,对安全性和活性属性的明确讨论以及对一致性哈希的更详细讨论。不过,我要去《Strange Loop 2013》了,所以无论如何。 如果这本书有第六章,它可能是关于如何利用和处理大量数据的。似乎最常见的“大数据”计算类型是通过单个简单程序传递大型数据集的计算。我不确定后续章节会是什么(也许是高性能计算,因为当前的重点是可行性),但我可能会在几年后知道。 有关分布式系统的书籍 Distributed Algorithms (Lynch) 这可能是最常推荐的分布式算法书籍。我也推荐它,但有一个警告。它非常全面,但是是为研究生读者编写的,因此在了解从业者最感兴趣的内容之前,您将花费大量时间阅读同步系统和共享内存算法。 Introduction to Reliable and Secure Distributed Programming (Cachin, Guerraoui & Rodrigues) 对于一个修炼者来说,这是一件有趣的事。它很短并且充满了实际的算法实现。 Replication: Theory and Practice 如果您对复制感兴趣,这本书非常棒。关于复制的章节主要基于对本书有趣部分以及最近阅读的内容的综合。 Distributed Systems: An Algorithmic Approach (Ghosh) Introduction to Distributed Algorithms (Tel) Transactional Information Systems: Theory, Algorithms, and the Practice of Concurrency Control and Recovery (Weikum & Vossen) 本书介绍的是传统的交易信息系统,例如:本地 RDBMS。最后有两章介绍分布式事务,但本书的重点是事务处理。 Transaction Processing: Concepts and Techniques by Gray and Reuter 经典之作。我发现 Weikum & Vossen 更更新。 开创性论文 每年,Edsger W. Dijkstra 分布式计算奖都会颁发给有关分布式计算原理的杰出论文。查看完整列表的链接,其中包括经典内容,例如: “Time, Clocks and Ordering of Events in a Distributed System” - Leslie Lamport “Impossibility of Distributed Consensus With One Faulty Process” - Fisher, Lynch, Patterson “Unreliable failure detectors and reliable distributed systems” - Chandra and Toueg Microsoft 学术搜索有一个分布式和并行计算领域的顶级出版物列表,按引用次数排序 - 这可能是一个有趣的列表,可以浏览更多经典著作。...

[译]《分布式系统:为了乐趣和利益》5.复制:弱一致性模型协议

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者 Mikito Takada 撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 5. 复制:弱一致性模型协议 现在,我们已经研究了一些可以在越来越现实的故障情况下实施单副本一致性的协议,让我们转向当我们放弃单副本一致性的要求时所打开的选择世界。 总的来说,很难找到一个单一的维度来定义或描述允许副本发散的协议。大多数这样的协议都具有高可用性,关键问题更多地在于最终用户是否发现这些保证、抽象和 API 对他们的目的有用,尽管在节点和/或网络故障发生时副本可能发散。 为什么弱一致性系统没有更受欢迎呢? 正如我在介绍中所述,我认为分布式编程很大程度上涉及处理分布的两个结果所带来的影响: 信息以光速传播; 独立的事物独立地发生故障。 由于信息传输速度受限,节点以不同且独特的方式体验世界。在单个节点上进行计算很容易,因为一切都按照可预测的全局总序发生。在分布式系统上进行计算很困难,因为没有全局总序。 长期以来(例如几十年的研究时间),我们通过引入全局总序来解决这个问题。我已经讨论了许多实现强一致性的方法,通过在没有自然总序的情况下以容错方式创建顺序的方法。 当然,问题在于强制执行顺序是昂贵的。这在大规模的互联网系统中特别突出,因为系统需要保持可用性。强一致性的系统不像分布式系统那样运行,而是像单个系统,这对于分区期间的可用性是不利的。 此外,对于每个操作,通常需要联系大多数节点,而且通常不止一次(正如您在关于 2PC 的讨论中所看到的)。这在需要在地理上分布以为全球用户提供足够性能的系统中尤其困难。 因此,默认情况下像单个系统一样运行可能并不理想。 也许我们希望拥有一种可以编写不使用昂贵协调的代码,但仍返回一个“可用”值的系统。我们将允许不同的副本彼此发散-既为了保持效率,也为了容忍分区-然后尝试以某种方式处理这种发散。 最终一致性表达了这个想法:节点在一段时间内可以相互发散,但最终它们将达成一致的值。 在提供最终一致性的系统集合中,有两种类型的系统设计: 带有概率保证的最终一致性。这种类型的系统可以在以后的某个时间点检测到冲突的写操作,但不能保证结果与某个正确的顺序执行等效。换句话说,冲突的更新有时会导致将较新的值覆盖为较旧的值,并且在正常操作(或分区)期间可能会出现一些异常情况。 近年来,最有影响力的提供单副本一致性的系统设计是亚马逊的 Dynamo,我将以它作为提供带有概率保证的最终一致性系统示例进行讨论。 带有强保证的最终一致性。这种类型的系统保证最终结果会收敛到一个共同的值,该值等同于某个正确的顺序执行。换句话说,这样的系统不会产生任何异常结果;在没有任何协调的情况下,您可以构建相同服务的副本,并且这些副本可以以任何模式进行通信并以任何顺序接收更新,只要它们都看到相同的信息,它们最终会就最终结果达成一致。 CRDT(收敛复制数据类型)是一种数据类型,它保证在网络延迟、分区和消息重排序的情况下收敛到相同的值。它们可以被证明是收敛的,但可以实现为 CRDT 的数据类型是有限的。 CALM(一致性作为逻辑单调性)猜想是相同原则的另一种表达方式:它将逻辑单调性与收敛等同起来。如果我们可以得出某个东西在逻辑上是单调的,那么在没有协调的情况下运行它也是安全的。收敛分析-特别是在 Bloom 编程语言中的应用-可用于指导程序员在何时何地使用强一致性系统的协调技术以及在何时可以安全地执行无需协调的操作。 协调不同的操作指令 不强制执行单副本一致性的系统是什么样子呢?让我们通过几个例子来更具体地了解。 也许最明显的非强制执行单副本一致性系统的特征是它们允许副本彼此发散。这意味着没有严格定义的通信模式:副本可以相互分离,但仍然保持可用并接受写操作。 让我们想象一个由三个副本组成的系统,每个副本彼此分离。例如,这些副本可能位于不同的数据中心,并因某种原因无法通信。在分离期间,每个副本仍然可用,可以接受一些客户端的读写操作: [Clients] - > [A] --- Partition --- [Clients] - > [B] --- Partition --- [Clients] - > [C] 一段时间后,分区会修复并且副本服务器会交换信息。他们从不同的客户那里收到了不同的更新,并且彼此存在分歧,因此需要进行某种协调。我们希望所有的副本都收敛到相同的结果。 [A] \ --> [merge] [B] / | | [C] ----[merge]---> result 考虑具有弱一致性保证的系统的另一种方法是想象一组客户端按某种顺序向两个副本发送消息。由于没有强制执行单一总顺序的协调协议,因此消息可以在两个副本上以不同的顺序传递: [Clients] --> [A] 1, 2, 3 [Clients] --> [B] 2, 3, 1 从本质上讲,这就是我们需要协调协议的原因。例如,假设我们尝试连接一个字符串,消息 1、2 和 3 中的操作为: 1: { operation: concat('Hello ') } 2: { operation: concat('World') } 3: { operation: concat('!...

[译]《分布式系统:为了乐趣和利益》4.复制

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者 Mikito Takada 撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 4. 复制 复制问题是分布式系统中的众多问题之一。与诸如领导者选举、故障检测、互斥、共识和全局快照等其他问题相比,我选择关注复制问题,因为这通常是人们最感兴趣的部分。并行数据库在复制特性方面的差异化就是一个例子。此外,复制为许多子问题提供了一个上下文,例如领导者选举、故障检测、共识和原子广播。 复制是一个组通信问题。什么样的安排和通信模式能够满足我们所期望的性能和可用性特性?在面对网络分区和同时节点故障的情况下,我们如何确保容错性、耐久性和非发散性? 同样,有许多方法可以解决复制问题。我在这里采用的方法只是看一下具有复制功能的系统可能具有的高级模式。从可视化的角度来看,有助于将讨论集中在整体模式上,而不是具体涉及的消息传递。我在这里的目标是探索设计空间,而不是解释每个算法的具体细节。 让我们首先定义一下复制是什么样子。我们假设我们有一些初始数据库,并且客户端发出请求来改变数据库的状态。 这种安排和通信模式可以分为几个阶段: (请求)客户端向服务器发送请求 (同步)进行复制的同步部分 (响应)将响应返回给客户端 (异步)进行复制的异步部分 根据这些阶段,我们可以创建不同的通信模式。我们选择的模式将对性能和可用性产生影响,具体取决于所选择的算法。 同步复制 第一个模式是同步复制(也称为主动复制、急切复制、推送复制或悲观复制)。让我们画出它的示意图: 在这里,我们可以看到三个明显的阶段:首先,客户端发送请求。接下来,我们所称的同步复制的部分发生。这个术语指的是客户端被阻塞 - 等待系统的回复。 在同步阶段期间,第一个服务器会联系其他两个服务器,并等待收到所有其他服务器的回复。最后,它向客户端发送一个响应,告知其结果(例如成功或失败)。 这一切似乎很简单。在不讨论同步阶段算法的细节的情况下,我们能从这个特定的通信模式安排中得出什么结论呢?首先,注意这是一种 N-对-N 的写入方式:在返回响应之前,它必须被系统中的每个服务器看到并确认。 从性能的角度来看,这意味着系统的速度将取决于其中最慢的服务器。该系统还对网络延迟的变化非常敏感,因为它要求在继续之前每个服务器都必须回复。 考虑到 N-对-N 的方式,系统无法容忍任何服务器的丢失。当一个服务器丢失时,系统无法再写入所有节点,因此无法继续进行。在这种设计中,它可能能够对数据提供只读访问,但在节点故障后不允许进行修改。 这种安排可以提供非常强大的耐久性保证:当返回响应时,客户端可以确信所有 N 个服务器都已接收、存储和确认了请求。为了丢失一个已接受的更新,所有 N 个副本都需要丢失,这是一个非常好的保证。 异步复制 让我们将其与第二种模式进行对比 - 异步复制(也称为被动复制、拉式复制或惰性复制)。正如您可能已经猜到的,这与同步复制相反: 在这种情况下,主节点(也称为领导者或协调者)立即向客户端返回响应。它最多只会在本地存储更新,但不会同步执行任何重要工作,客户端也不需要等待更多的服务器之间的通信轮次。 在稍后的阶段,复制任务的异步部分发生。在这里,主节点使用某种通信模式联系其他服务器,并更新它们的数据副本。具体的实现取决于所使用的算法。 在不涉及算法细节的情况下,我们对这种具体的安排能得出什么结论呢?嗯,这是一种写 1-对-N 的方式:立即返回响应,更新传播在稍后发生。 从性能的角度来看,这意味着系统很快:客户端不需要额外花费时间等待系统内部完成工作。系统对网络延迟更具容忍性,因为内部延迟的波动不会导致客户端需要额外等待。 这种安排只能提供弱的或概率性的耐久性保证。如果没有出现问题,数据最终会复制到所有 N 台机器上。然而,如果在此之前包含数据的唯一服务器丢失,数据将永久丢失。 考虑到 1-对-N 的方式,只要至少有一个节点正常运行,系统就可以保持可用性(理论上至少如此,但实际上负载可能会太高)。这种纯粹的惰性方式不提供耐久性或一致性保证;你可以向系统写入数据,但如果发生任何故障,不能保证能够读取你所写入的内容。 最后值得注意的是,被动复制无法确保系统中的所有节点始终包含相同的状态。如果在多个位置接受写入操作,并且不要求这些节点同步一致,那么就会存在发散的风险:不同位置的读取可能返回不同的结果(特别是在节点故障和恢复后),并且无法强制执行全局约束(需要与所有人通信)。 我没有详细讨论读取(而不是写入)时的通信模式,因为读取模式实际上是根据写入模式来确定的:在读取过程中,你希望与尽可能少的节点进行联系。我们将在仲裁机制的背景下进一步讨论这个问题。 我们只讨论了两种基本的安排,并没有涉及具体的算法。然而,我们已经能够了解可能的通信模式以及它们的性能、耐久性保证和可用性特性的一些信息。 主要复制方法概述 在讨论了同步和异步复制这两种基本复制方法之后,让我们来看一下主要的复制算法。 有很多不同的方式来对复制技术进行分类。在同步与异步之后,我想引入的第二个区别是: 防止发散的复制方法(单副本系统)和 存在发散风险的复制方法(多主系统) 第一组方法具有“行为像单一系统”的特性。特别是在部分故障发生时,系统确保只有一个副本处于活动状态。此外,系统确保副本始终保持一致。这被称为共识问题。 当多个进程(或计算机)就某个值达成共识时,它们实现了共识。更具体地说: 一致性:每个正确的进程必须就同一个值达成一致。 完整性:每个正确的进程最多决定一个值,并且如果它决定了某个值,则该值必须由某个进程提出。 终止性:所有进程最终都会达成决策。 有效性:如果所有正确的进程提议相同的值 V,则所有正确的进程都决定 V。 互斥、领导者选举、组播和原子广播都是共识问题的更一般实例。维护单一副本一致性的复制系统需要以某种方式解决共识问题。 维护单一副本一致性的复制算法包括: 1n 消息(异步主/备份) 2n 消息(同步主/备份) 4n 消息(两阶段提交,多 Paxos) 6n 消息(三阶段提交,带有重复领导者选举的 Paxos) 这些算法在容错性方面有所不同(例如,它们可以容忍的故障类型)。我根据算法执行期间交换的消息数量进行了简单的分类,因为我认为尝试回答“通过增加消息交换我们得到了什么?”这个问题很有趣。 下面的图表,改编自 Google 的 Ryan Barret,描述了不同选项的一些方面: 上述图表中的一致性、延迟、吞吐量、数据丢失和故障转移特性实际上可以追溯到两种不同的复制方法:同步复制(例如,在响应之前等待)和异步复制。当您等待时,性能会变差,但可以获得更强的保证。在讨论分区(和延迟)容忍性时,2PC 和仲裁系统之间的吞吐量差异将变得明显。 在该图表中,强制弱(/最终)一致性的算法被归类为一类(“gossip”)。然而,我将更详细地讨论弱一致性的复制方法 - gossip 和(部分)仲裁系统。“事务"行实际上更多地涉及全局谓词评估,而在具有弱一致性的系统中不支持(尽管可以支持本地谓词评估)。...

[译]《分布式系统:为了乐趣和利益》3.时间及顺序

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者 Mikito Takada 撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 3. 时间及顺序 什么是顺序,为什么它很重要? 你说的“什么是顺序”是什么意思? 我的意思是,为什么我们对顺序如此着迷?为什么我们关心 A 是否发生在 B 之前?为什么我们不关心其他属性,比如“颜色”? 好吧,我的疯狂朋友,让我们回到分布式系统的定义来回答这个问题。 你可能还记得,我将分布式编程描述为使用多台计算机解决单台计算机可以解决的同一个问题的艺术。 事实上,这正是对顺序如此着迷的核心所在。任何只能一次执行一项任务的系统都会创建操作的总体顺序。就像人们通过一扇门一样,每个操作都有明确定义的前任和后继。这基本上是我们努力保留的编程模型。 传统的模型是:一个程序,一个进程,一个在一个 CPU 上运行的内存空间。操作系统抽象了可能存在多个 CPU 和多个程序的事实,以及计算机内存实际上是多个程序共享的。我并不是说线程编程和事件驱动编程不存在;只是它们是“一个/一个/一个”模型之上的特殊抽象。程序被编写成按照一定顺序执行:从上往下执行。 顺序作为一种属性受到了如此多的关注,是因为定义“正确性”的最简单方法是说“它的工作方式与单台计算机上的工作方式相同”。而通常这意味着 a)我们运行相同的操作,b)我们按照相同的顺序运行它们 - 即使有多台计算机。 保持顺序(如单个系统定义的顺序)的分布式系统的优点在于它们是通用的。你不需要关心操作是什么,因为它们将与在单台计算机上完全相同的方式执行。这很棒,因为你知道无论操作是什么,你都可以使用相同的系统。 实际上,分布式程序在多个节点上运行,具有多个 CPU 和多个操作流。你仍然可以分配一个总体顺序,但这需要准确的时钟或某种形式的通信。你可以使用完全准确的时钟为每个操作标记时间戳,然后利用它来确定总体顺序。或者你可以使用某种通信系统,使得可以分配类似总体顺序的连续编号。 全序和偏序 在分布式系统中,自然状态是偏序。网络和独立节点都不对相对顺序做出任何保证,但在每个节点上,你可以观察到一个局部顺序。 总序是一种二元关系,它为某个集合中的每个元素定义了一个顺序。 当两个不同的元素可比较时,其中一个大于另一个。在偏序集中,某些元素对不可比较,因此偏序并不指定每个项目的确切顺序。 总序和偏序都是传递性和反对称性的。对于集合 X 中的所有 a、b 和 c,以下陈述在总序和偏序中都成立: If a ≤ b and b ≤ a then a = b (antisymmetry); If a ≤ b and b ≤ c then a ≤ c (transitivity); 然而,总序是完全的: a ≤ b or b ≤ a (totality) for all a, b in X 而偏序是自反的: a ≤ a (reflexivity) for all a in X 注意,全序性蕴含自反性;因此,偏序是总序的一种较弱的变体。在偏序中的某些元素上,全序性不成立 - 换句话说,其中一些元素不可比较。...

[译]《分布式系统:为了乐趣和利益》2.抽象层次的上下

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者 Mikito Takada 撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 2. 抽象层次的上下 在本章中,我们将在抽象层次之间穿梭,探讨一些不可能性结果(CAP 和 FLP),然后出于性能考虑回归到更低层次。 如果你有进行过编程,抽象层次的概念可能对你来说很熟悉。你总是在某个抽象层次上进行工作,通过某个 API 与较低层次的接口进行交互,并可能为用户提供一些更高层次的 API 或用户界面。计算机网络的七层 OSI 模型就是一个很好的例子。 分布式编程很大程度上涉及处理分布的后果(显而易见!)。也就是说,我们面临着现实中存在许多节点的现实和我们希望系统“像一个单一系统一样工作”的愿望之间存在着紧张关系。这意味着需要找到一个良好的抽象,平衡可能性、可理解性和性能。 当我们说 X 比 Y 更抽象时,我们是指 X 没有引入任何与 Y 根本不同的新内容。事实上,X 可能会去除 Y 的某些方面或以更易于处理的方式呈现它们。其次,X 在某种意义上比 Y 更容易理解,假设 X 从 Y 中去除的内容对于当前问题并不重要。 如尼采所写: 每个概念都是通过我们将不相等的事物等同起来形成的。没有一片叶子完全等同于另一片叶子,概念“叶子”是通过对这些个体差异进行任意抽象而形成的,通过遗忘区别;现在它产生了一个想法,即在自然界中可能存在除了叶子之外的东西,这些东西将是“叶子”的一种原始形式 - 所有叶子都已经被编织、标记、复制、着色、卷曲和绘制,但是由于技术不熟练,没有一份副本能够成为原始形式的正确、可靠和忠实的图像。 抽象本质上是虚构的。每种情况都是独特的,每个节点也是如此。但是抽象使得世界变得可管理:简化的问题陈述 - 不受现实约束 - 更易于分析,并且只要我们没有忽略任何重要的东西,解决方案就是广泛适用的。 事实上,如果我们保留下来的东西是重要的,那么我们可以得出的结果就会具有广泛的适用性。这就是为什么不可能性结果如此重要:它们采用了问题的最简单可能的表述,并证明在一些约束或假设条件下无法解决该问题。 所有的抽象都会忽略一些与现实独特的东西,以便将它们等同起来。关键是要摆脱一切非必要的东西。你如何知道什么是必要的?嗯,你可能事先不知道。 每次我们在系统规范中排除系统的某个方面时,我们都存在引入错误和/或性能问题的风险。这就是为什么有时我们需要朝着相反的方向前进,并有选择性地引入一些真实硬件和现实世界问题的方面。重新引入一些特定的硬件特性(例如物理顺序性)或其他物理特性可能足以获得足够好的性能的系统。 系统模型 在分布式系统中,分布是一个关键特性。具体而言,分布式系统中的程序具有以下特点: 在独立节点上并发运行… 通过可能引入非确定性和消息丢失的网络连接… 没有共享内存或共享时钟。 这有许多含义: 每个节点并发执行程序。 知识是局部的:节点仅能快速访问本地状态,对于全局状态的任何信息都可能过时。 节点可以独立发生故障并进行恢复。 消息可能会延迟或丢失(与节点故障无关;很难区分网络故障和节点故障)。 而且节点之间的时钟不同步(本地时间戳与全局实际时间顺序不对应,很难观察到)。 系统模型列举了与特定系统设计相关的许多假设。 系统模型是关于实现分布式系统的环境和设施的一组假设。 系统模型在其对环境和设施的假设方面存在差异。这些假设包括: 节点的能力和故障方式 通信链路的操作方式以及可能的故障方式 整个系统的属性,例如有关时间和顺序的假设 健壮的系统模型是对假设最弱的模型:针对这种系统编写的任何算法都对不同的环境非常容忍,因为它有非常少且非常弱的假设。 另一方面,我们可以通过进行强假设来创建一个易于推理的系统模型。例如,假设节点不会发生故障意味着我们的算法不需要处理节点故障。然而,这样的系统模型是不现实的,因此在实践中很难应用。 让我们更详细地看一下节点、链路、时间和顺序的属性。 我们系统模型中的节点 节点作为计算和存储的主机。它们具有以下特点: 能够执行程序。 能够将数据存储到易失性内存(在故障时可能丢失)和稳定状态(在故障后可以读取)。 时钟(可以被假设为准确或不准确)。 节点执行确定性算法:局部计算、计算后的本地状态和发送的消息是根据接收到的消息和接收消息时的本地状态唯一确定的。 有许多可能的故障模型描述了节点可能发生的故障方式。在实践中,大多数系统假设使用崩溃恢复故障模型:也就是说,节点只能通过崩溃来发生故障,并且可以在稍后某个时间点(可能)进行恢复。 另一种选择是假设节点可以以任意方式发生故障。这被称为拜占庭容错。拜占庭故障在实际的商业系统中很少处理,因为对任意故障具有弹性的算法运行成本更高,实现更复杂。在这里我不会讨论拜占庭容错。 我们系统模型中的通信链路 通信链接将各个节点彼此连接,并允许消息在任意方向上发送。许多讨论分布式算法的书籍假设每对节点之间都有独立的链接,这些链接为消息提供了先进先出(FIFO)的顺序,只能传递已发送的消息,并且已发送的消息可能会丢失。 某些算法假设网络是可靠的:消息永远不会丢失,也不会无限期地延迟。这在某些实际情况下可能是合理的假设,但一般来说,更倾向于将网络视为不可靠的,可能会发生消息丢失和延迟的情况。 当网络发生故障而节点本身仍可运行时,就会发生网络分区。在这种情况下,消息可能会丢失或延迟,直到网络分区被修复。分区的节点可能对某些客户端是可访问的,因此必须与崩溃的节点进行不同处理。下图说明了节点故障和网络分区的区别: 通常很少对通信链接做进一步的假设。我们可以假设链接只能单向工作,或者可以为不同的链接引入不同的通信成本(例如由于物理距离引起的延迟)。然而,在商业环境中,除了长距离链接(广域网延迟)之外,这些很少是关注的问题,因此我在这里不会讨论它们;成本和拓扑的更详细模型可以在复杂性的代价下实现更好的优化。 时间/顺序假设 物理分布的一个结果是每个节点以独特的方式体验世界。这是无法避免的,因为信息只能以光速传播。如果节点之间的距离不同,那么从一个节点发送到其他节点的任何消息都会在其他节点以不同的时间到达,并有可能以不同的顺序到达。 时间假设是捕捉我们在多大程度上考虑这个现实的便捷方式。主要的两种选择是: 同步系统模型。进程以同步方式执行;消息传输延迟有已知的上界;每个进程具有准确的时钟。 异步系统模型。没有时间假设 - 例如进程以独立的速率执行;消息传输延迟没有上界;没有可靠的时钟存在。 同步系统模型对时间和顺序施加了许多限制。它基本上假设节点有相同的体验:发送的消息总是在特定的最大传输延迟内接收,并且进程以同步方式执行。这很方便,因为它允许您作为系统设计者对时间和顺序做出假设,而异步系统模型则不允许。 异步性是一种非假设:它只是假设您不能依赖于时间(或“时间传感器”)。...

[译]《分布式系统:为了乐趣和利益》1.高层分布式系统

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者 Mikito Takada 撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 1. 高层分布式系统 分布式编程是利用多台计算机解决在单台计算机上可以解决的相同问题的一种技术。 任何计算机系统都需要完成两个基本任务: 存储 计算 分布式编程是一种艺术,通过利用多台计算机解决在单台计算机上可以解决的相同问题,通常是因为该问题已经超出了单台计算机的处理能力。 实际上,并没有强制要求我们使用分布式系统。如果拥有无限的资金和无限的研发时间,我们就不需要分布式系统。所有的计算和存储可以在一个魔盒上完成,这是一台单一的、极其快速和可靠的系统,你可以支付给别人来为你设计。 然而,很少有人拥有无限的资源。因此,他们必须在现实世界的成本效益曲线上找到合适的位置。在小规模情况下,升级硬件是一种可行的策略。然而,随着问题规模的增加,你会达到一个阶段,在这个阶段,要么不存在可以让你在单个节点上解决问题的硬件升级,要么成本过高。在这一点上,我欢迎你进入分布式系统的世界。 当前的现实是,只要通过容错软件将维护成本控制在较低水平,中档、大众化硬件提供了最佳的性价比。 计算主要受益于高端硬件,尤其是在能够通过内部内存访问取代缓慢的网络访问时。在需要节点间大量通信的任务中,高端硬件的性能优势有限。 正如 Barroso、Clidaras 和 Hölzle 的上图所示,假设所有节点都采用统一的内存访问模式,高端硬件和商用硬件之间的性能差距会随着集群规模的扩大而缩小。 理想情况下,添加一台新的机器将线性增加系统的性能和容量。但是,现实情况并非如此,因为由于存在独立的计算机,会产生一些开销。数据需要在计算机之间进行复制,计算任务需要进行协调等等。 这就是为什么值得研究分布式算法的原因——它们提供了针对特定问题的高效解决方案,以及关于可能性、正确实现的最低成本以及不可能性的指导。 这段文字的重点是在一个平凡但商业相关的环境中,即数据中心的分布式编程和系统。例如,我不会讨论由于具有异乎寻常的网络配置或在共享内存设置中出现的专门问题。此外,重点是探索系统设计空间,而不是优化任何特定设计——后者是一个更专门的文本主题。 我们想要实现的目标:可扩展性和其他好的东西 从我看来,一切都始于处理规模的需求。 在小规模下,大多数事情都是微不足道的,而同样的问题一旦超过一定的大小、容量或其他物理限制,就会变得更加困难。举起一块巧克力很容易,但举起一座山就很困难。数一下房间里有多少人很容易,但数一下国家里有多少人就很难。 所以一切都始于规模——可扩展性。非正式地说,在一个可扩展的系统中,当我们从小规模向大规模过渡时,事情不应该逐渐变得更糟。以下是另一种定义: 可扩展性是指系统、网络或进程处理不断增长的工作负载的能力,或者说它能够被扩大以适应这种增长的能力。 什么是在增长呢?嗯,你可以用几乎任何方式来衡量增长(人数、用电量等)。但有三个特别有趣的方面值得关注: 规模可扩展性:增加更多节点应该使系统线性加快;增加数据集的大小不应增加延迟。 地理可扩展性:应该可以利用多个数据中心来缩短响应用户查询所需的时间,同时以某种合理的方式处理跨数据中心的延迟。 管理可扩展性:添加更多节点不应增加系统的管理成本(例如管理员与机器的比率)。 当然,在真实的系统中,增长同时发生在多个不同的轴上;每个指标仅反映增长的某些方面。 可扩展的系统是一种随着规模的增加而持续满足用户需求的系统。有两个特别相关的方面——性能和可用性——可以通过多种方式来衡量。 性能(和延迟) 性能是指计算机系统在使用的时间和资源相对于所完成的有用工作量来衡量的特征。 根据具体情况,这可能涉及实现以下一项或多项: 对于给定的工作,响应时间短/延迟低 高吞吐量(处理工作率) 计算资源利用率低 针对任何这些结果进行优化都需要权衡。例如,系统可以通过处理更大批量的工作来实现更高的吞吐量,从而减少操作开销。由于批处理,权衡将是个别工作的响应时间更长。 我发现低延迟(实现较短的响应时间)是性能中最有趣的方面,因为它与物理(而不是财务)限制密切相关。使用财务资源来解决延迟问题比性能的其他方面更难。 对于延迟有很多非常具体的定义,但我真的很喜欢这个词的词源所唤起的想法: 延迟是指潜伏状态,延迟的或在某事物开始和发生之间的一段时间。 “潜在的”是什么意思? 潜在的是指某物存在或出现,但被隐藏、隐蔽或处于不活动状态。它描述了一种存在却不容易察觉或可见的状态,但它仍以隐藏或潜在的形式存在。 这个定义非常酷,因为它强调了延迟是指某件事发生到它产生影响或变得可见之间的时间。 例如,假设您感染了一种空气传播的病毒,该病毒会将人变成僵尸。潜伏期是指从你被感染到变成僵尸之间的时间。这就是潜伏期:已经发生的事情被隐藏起来的时间。 让我们假设我们的分布式系统只执行一项高级任务:给定一个查询,它会获取系统中的所有数据并计算一个结果。换句话说,将分布式系统视为一个数据存储,能够对其当前内容运行单个确定性计算(函数): result = query(all data in the system) 那么,对延迟来说重要的不是旧数据的数量,而是新数据在系统中“生效”的速度。例如,延迟可以根据写入对读者可见所需的时间来衡量。 基于这个定义的另一个关键点是,如果什么都没有发生,就没有“潜伏期”。数据不改变的系统不会(或不应该)存在延迟问题。 在分布式系统中,存在一个无法克服的最小延迟:光速限制了信息传输的速度,而硬件组件每个操作都会产生一定的最小延迟成本(例如内存、硬盘以及 CPU)。 最小延迟对查询的影响程度取决于这些查询的性质以及信息需要传输的物理距离。 可用性(和容错) 可扩展系统的第二个方面是可用性。 可用性是指系统处于正常运行状态的时间比例。如果用户无法访问系统,则称系统不可用。 分布式系统使我们能够实现在单一系统上很难实现的理想特性。例如,单个机器无法容忍任何故障,因为它要么发生故障,要么正常运行。 分布式系统可以采用一堆不可靠的组件,并在它们之上构建一个可靠的系统。 没有冗余的系统只能达到其底层组件的可用性。而具备冗余的系统可以容忍部分故障,从而提高可用性。 值得注意的是,“冗余”可以在不同层面上有不同的含义,比如组件、服务器、数据中心等。 从公式上讲,可用性为: Availability = uptime / (uptime + downtime) 。 从技术角度来看,可用性主要与容错性有关。因为故障发生的概率随着组件数量的增加而增加,系统应该能够进行补偿,以确保随着组件数量的增加,系统的可靠性不会降低。 例如: 可用性 % 90%(“一个九”) 一个多月了 99%(“两个九”) 少于 4 天 99.9%(“三个九”) 不到 9 小时 99....

[译]《分布式系统:为了乐趣和利益》介绍

《分布式系统:为了乐趣和利益》是一本广受欢迎的资源,用于理解和学习分布式系统。该书由作者Mikito Takada撰写,介绍了构建分布式系统的基本概念、原则和挑战。 这本书涵盖了与分布式系统相关的广泛主题,包括网络、容错性、一致性模型、分布式算法、可扩展性等等。它旨在以清晰易懂的方式解释复杂的概念,适合初学者和有经验的分布式系统从业者阅读。 在整本书中,作者提供了各种实际案例和案例研究,以说明分布式系统的实际应用和实践方面。它还强调了构建分布式系统涉及的权衡和设计考虑,帮助读者全面理解这个主题。 《分布式系统:为了乐趣和利益》作为开源资源,可以免费在线获取,非常适合任何对学习分布式系统感兴趣的人。 原文链接:Distributed systems: for fun and profit 介绍 我想要一本能够汇集许多最新分布式系统(例如 Amazon 的 Dynamo、Google 的 BigTable 和 MapReduce、Apache 的 Hadoop 等系统)背后的思想的文本。 在本文中,我试图提供更易于理解的分布式系统介绍。对我来说,这意味着两件事:介绍您需要的关键概念,以便您可以愉快地阅读更严肃的文本,并提供足够详细的叙述,以便您了解正在发生的事情的要点,而不会陷入困境关于细节。现在是 2013 年,您已经有了互联网,您可以有选择地阅读更多您认为最感兴趣的主题。 在我看来,分布式编程的大部分内容都是关于处理分布式的两个后果的影响: 信息以光速传播 独立的事物会独立失败 换句话说,分布式编程的核心是处理距离(废话!)并且拥有不止一件事(废话!)。这些约束定义了可能的系统设计空间,我希望读完本文后您将更好地了解距离、时间和一致性模型如何相互作用。 本文重点介绍理解数据中心商业系统所需的分布式编程和系统概念。试图涵盖一切将是疯狂的。您将学习许多关键协议和算法(例如,涵盖该学科中许多被引用次数最多的论文),包括一些尚未进入大学教科书的最终一致性的令人兴奋的新方法 - 例如 CRDT和 CALM 定理。 我希望你喜欢它!如果您想表达感谢,请在 Github(或 Twitter)上关注我。如果发现错误,请在 Github 上提交拉取请求。 1. 基础知识 第一章通过介绍一些重要的术语和概念,从高层次上介绍了分布式系统。它涵盖了高级别目标,例如可扩展性、可用性、性能、延迟和容错;这些是如何难以实现的,以及抽象和模型以及分区和复制如何发挥作用。 2. 抽象层次的上下 第二章更深入地探讨抽象和不可能性的结果。它以尼采的名言开始,然后介绍系统模型以及典型系统模型中所做的许多假设。然后讨论了 CAP 定理并总结了 FLP 不可能性结果。然后转向 CAP 定理的含义,其中之一是人们应该探索其他一致性模型。然后讨论了许多一致性模型。 3. 时间及顺序 理解分布式系统的一个重要部分是理解时间和顺序。如果我们无法理解和建模时间,我们的系统就会失败。第三章讨论时间和顺序、时钟以及时间、顺序和时钟的各种用途(例如矢量时钟和故障检测器)。 4. 复制:防止发散 第四章介绍了复制问题以及执行该问题的两种基本方法。事实证明,大多数相关特征都可以通过这个简单的表征来讨论。然后,从最低容错(2PC)到Paxos讨论了维持单副本一致性的复制方法。 5. 复制:接受分歧 第五章讨论了具有弱一致性保证的复制。它引入了一个基本的协调场景,其中分区副本尝试达成一致。然后,它讨论了 Amazon 的 Dynamo 作为具有弱一致性保证的系统设计的示例。最后,讨论了无序编程的两个观点:CRDT 和 CALM 定理。 Appendix 附录 附录包含进一步阅读的建议。