本文介绍了Shardines,一个旨在实现区块链执行引擎水平可扩展性的项目,能够处理超过100万的每秒交易(TPS)。它通过动态分区和微批处理、流水线等创新策略,减少跨分区的网络通信和等待时间。此架构的目标是支持全球规模的Web3应用,并提升系统的灵活性和资源优化。
By Manu Dhundi , Sital Kedia , Igor Kabiljo , Zhoujun Ma , Jan Olkowski
TLDR:
可扩展性长期以来一直是区块链的一个重大挑战。随着Web3的采用不断增长和用例的扩展,传统区块链往往在吞吐量(以每秒交易数衡量)上成为瓶颈,导致高延迟和高昂的费用。以以太坊为代表的第一代区块链通过采用单线程执行模型开创了Web3,但这限制了吞吐量仅为15 TPS,并成为了一个主要瓶颈。为了应对以太坊的可扩展性挑战,像Aptos和Solana这样的下一代区块链采用了并行执行模型。在Aptos上,我们有Block-STM——一个高效的多线程内存并行执行引擎,利用乐观并发控制的Software Transactional Memory技术。Block-STM已成为并行事务执行的行业标准,激励其他几条链采用类似的方法。
今天,我们很高兴地揭示Shardines,这是区块链执行引擎的下一次演变,旨在实现水平可扩展性。通过Shardines,我们可以实现接近线性的吞吐量扩展,在我们的30台机器集群上超过100万TPS。这个里程碑并不是终点,而是向前迈出的重要一步,展示了一个能够支持全球规模应用程序和适应Web3不断变化需求的区块链架构。
典型的区块链堆栈包含三个主要组件:共识、执行和存储。
为了实现水平可扩展性,我们的愿景是对这些组件进行分片并独立扩展。也就是说,可以通过增加数据分发服务的分片数量来扩展共识,这与执行和存储的分片数量无关。同样,如果执行成为瓶颈,可以通过增加更多的CPU核心来扩展,而不需要增加存储能力和数据分发服务能力。此外,这些分片服务被设计为逻辑服务,与物理机器布局解耦。这种解耦允许验证节点运营商灵活选择机器类型,优化资源使用,同时增强系统弹性。
独立可扩展的区块链架构
我们基于Narwhal的Quorum Store的实现已经显示了通过增加节点来扩展共识的方式。Quorum Store将数据传输和数据元排序解耦,允许数据传输进行横向扩展。上面的架构显示了多个数据传播分片,负责将交易数据传播到其他验证节点,并获取存储证书的证明。共识协调员负责对元数据进行排序——由于元数据的规模比事务数据小几个数量级,这允许共识支持非常高的吞吐量而不会成为瓶颈。
Aptos使用Jellyfish Merkle Tree(JMT)设计来存储区块链状态。JMT是一种具有空间和计算效率的稀疏Merkle树,针对基于日志结构合并树(LSM树)的键值存储(如RocksDB)进行了优化。从高层来看,存储分片意味着对JMT进行分片以进行状态存储。我们已经实现了JMT数据结构的分片,并且最近在主网中生产环境中部署了分片。由于JMT的设计确保了键值对的均匀分布,这种分片技术确保了负载在不同分片之间的均匀分配。
分片Merkle树
执行本质上受到CPU的限制,由于可用核心的有限数量,单台机器能够获得的TPS受到严格限制。因此,跨多台机器的扩展成为唯一实用的解决方案。
大多数现有的区块链分片解决方案,包括L2汇总,将执行和存储紧密耦合在单台机器上。虽然这种方法似乎提供了强大的可扩展性,但它导致了用户体验较差和碎片化的问题,因为没有单一的共享状态。我们扩展执行的方法是将其与存储解耦,从而允许两者独立扩展。实际上,我们相信在任何实际的分片区块链系统中,要实现100%的执行 ←→ 数据共存是不可能的。获取与执行不共同存在的数据始终会带来成本。为了解决这个问题,我们采用了动态分区器和一种新颖的“微批处理和管道化”策略(稍后会详细讨论),以摊销远程存储读取的成本,确保高效性能。
扩展区块链执行的一个主要挑战是处理事务之间的读写冲突。当一个区块中的一个事务(如T2)读取由另一个事务(如T1)修改的状态时,就会发生读写冲突。
冲突事务
在共享状态上发生冲突的事务对性能构成了重大挑战,因为它们无法并行执行。必须对对共享状态的访问进行序列化以保持一致性,从而有效地创建了一个瓶颈,减缓了执行。当这些冲突的事务分布在不同的分片(节点)上时,问题变得更加明显。在这种情况下,需要进行跨分片通信以协调对共享状态的序列化访问,增加了延迟和扩展执行的复杂性。
处理冲突的一个幼稚的方法是创建只包含无冲突事务的区块。然而,如果可能没有足够的无冲突事务可填充区块空间,这样做并不实用。更关键的是,这对那些支付更高手续费以便其事务可上链的用户是不公平的。例如,在热门NFT铸造期间,许多高手续费的冲突事务可能需要立即在链上包含。
冲突事务是像Aptos这样被广泛采用的区块链的一个固有方面。意识到这一点,我们设计我们的系统将冲突视为一等优先事项。在单节点执行器上,Block-STM有效地管理冲突,而对于水平分片执行,我们通过下面描述的超图分区手段来最小化跨分片冲突。
当两个事务在不同的分片(节点)上发生读写冲突时,需要额外的网络跳数,其中更新值通过网络传输,这导致后续事务等待才能开始执行。分区的主要目标是最小化这种跨分片通信及其相关的等待时间。
减少跨分片通信的最直接方法是对事务进行分区,以便每个资源只需在最少的机器上进行处理,然后重新排序事务,以消除需要在两个节点之间来回发送资源的情况,同时减少等待时间。为此,我们将冲突建模为一个超图,其中节点是事务,每个资源是一个超边,连接所有访问或写入该资源的事务。在下面的示例中,我们将超图表示为二分图,事务位于一侧,资源位于另一侧。
在分片中分区事务
在这样的图中,我们希望最小化一个称为fanout的度量——它代表一个超边跨越的分片数量(在我们的情况下,是一个资源需要的分片数量)。fanout(f)= 1的资源不需要在分片之间进行协调,而其他则需要。随着资源的fanout增加,需要协调该资源的分片数量也会增加。为了实现这一目标,我们选择并实施来自‘Social Hash Partitioner的策略,因为它是完全可扩展的,同时保持高质量。通过将fanout推广为“概率性fanout”,该策略平滑了目标函数,使简单的局部搜索算法能够找到高质量的解决方案。最后,我们使用单次启发式算法确定事务顺序,以最小化分片之间的来回通信以及相应的等待时间,从而生成优化的执行顺序和分片。
这种方法生成的分片是动态的,即从不同分片访问资源的情况在一个区块内有所变化。这导致在不断变化的工作负载下实现高吞吐量。
Shardines的高层设计
如上图所示,事务执行是在多个节点之间扩展的,每个节点作为“执行分片”功能运作。每个分片运行一个Block-STM的实例,以有效地执行分配给它的事务。执行分片的数量是可配置的,可以根据预期工作负载进行设置。一个执行分片接收待执行事务并向远程存储服务请求所需的数据。对于对其他分片中的事务有数据依赖关系的事务,称为跨分片依赖或冲突,分片之间交流跨分片消息以获取更新的数据。执行完成后,执行分片将结果发送回协调器。
为了展示我们分片设计的能力,我们抽象出其他系统部分,如共识(区块生成)、分区、读写集生成、存储、聚合以及基准测试,形成一个名为“执行协调器”的单节点应用程序。一旦区块生成器生成事务区块,协调器就在区块内对事务进行分区,并将分区后的事务分发到它们各自的执行分片进行执行。一旦分片完成执行,协调器收集结果,包括更新的状态和元数据,以便后续处理,例如聚合和最终确认。它最终将输出结果更新到存储服务中,以确保一致性和持久性。
在此设计中,存储服务并未在多个节点之间进行分片,协调器托管着远程存储服务。在达到100万TPS时,单节点的存储服务变成了瓶颈。我们计划在未来解决存储瓶颈的问题。
预测事务的读写集对于我们的分区方法至关重要。它们通过在运行时进行静态分析生成,具体是在字节码验证时。在我们的实验范围内,我们只考虑那些通过查看事务的发送方和接收方参数容易推断出读写集的事务。我们正在建立一个静态分析框架,用于提取实时的事务依赖,这将适用于所有类型的事务。
为了应对因网络延迟导致的性能瓶颈,我们在整个执行流程中采用了一种新颖的微批处理和管道化的方法。这一思想是将区块中的事务分割成微批次,并将这些微批次依次通过多个执行阶段。这些执行阶段如下:
在执行过程中微批处理和管道化事务
执行涉及五个阶段,每个批次需要总共5次网络跳跃。微批处理减少了因序列化和反序列化大型数据块而产生的延迟,以及由于传输大数据包而造成的网络延迟。此外,将管道化与微批处理结合起来确保了高效的资源利用,通过将各个微批处理的网络延迟与执行任务重叠,因此,在标准数据中心环境中,单跳延迟低于1毫秒,多个批次的总网络延迟有效地减少到每个区块大约5毫秒(5次网络跳跃)(而不是每个批次5次网络跳跃)。这一策略即使在数据和执行不共同存在时,也能保持高吞吐量。
向分片扩展执行的一个有趣挑战是需要聚合。有些资源,比如手续费代币的总供应量,会被所有事务修改。如果这些资源包含在冲突图中,每个事务都会发生冲突,使得所有分片之间出现顺序执行的局面。为了解决这个问题,每个事务在其各自的分片执行期间计算其对总供应量的增量,并在最后聚合这些增量以更新结果中的总供应量。这个聚合过程也是经过管道化的,对整体执行时间只产生微不足道的开销。
从分片中聚合执行结果
虽然聚合器可能看起来完全是序列化的,但管道化架构确保聚合不会产生显著的开销。
Shardines的架构展示了能够将区块链执行吞吐量扩展至超过100万交易每秒(TPS)以处理无冲突事务模式的能力。为了实现这一目的,我们抽象了非执行组件(如区块生成和存储),并在名为执行协调器的单节点应用程序中实现了基准测试。通过扩展协调器,我们确保执行的水平扩展受这些抽象组件的影响最小化。分片执行时间是将事务分发到执行器分片、执行事务、收集结果以及进行任何必要的额外处理所花费的时间。
实验设置包括以下规范:
Shardines的可扩展性使用两种不同的工作负载进行了评估:一种模拟无冲突的工作负载,另一种模拟存在冲突的工作负载。这些工作负载展示了架构如何高效地处理各种事务模式。
为了评估我们的架构的扩展能力而几乎没有额外开销,我们设计了一个工作负载,由以下事务组成:
这些事务更新单一账户的状态,因此本质上与来自不同账户的其他事务无冲突。
我们在50个区块上测试了这个工作负载,每个区块包含500,000个事务。结果展示了线性吞吐量扩展,在30个分片的条件下实现了大约103.3万交易每秒(TPS)。在分片级别,大多数配置下性能保持在每个分片约40,000 TPS。随着分片数量增至30个,TPS轻微降低至每个分片约35,000 TPS,反映出由于协调增强带来的开销。
这些结果突显了我们的扩展架构在处理高吞吐量工作负载时的高效率,随着系统的扩展性能几乎没有下降。
区块链拥有多个去中心化应用程序(DApp)的活跃生态系统,满足各种用例,如游戏、金融、社交网络和供应链管理。在一个区块的边界内,用户通常与单个“主DApp”进行交互,主DApp与他们的主要兴趣相符,而频繁接触多个DApp的情况则相对较少。这种工作负载模式创建了一个图,其中用户(通过他们的事务)主要聚集在单个DApp的资源上,跨集群的资源访问则相对较少。
为了模拟这种工作负载,我们生成了一个包含500,000个事务的区块,涉及400个DApp,其中每个DApp管理5个资源。事务的分布在DApp之间遵循标准模式,而每位用户的事务数量呈对数正态分布,平均为5。每个用户都有一个主要的DApp,具有99.9%的概率仅访问该DApp内的资源。这意味着大多数与同一DApp交互的用户将发生冲突的事务——即在同一DApp资源上进行写入和读取。
在50个区块中,我们超过了500,000 TPS。虽然在30个分片上的吞吐量扩展并不完全线性,但依然令人印象深刻。每个分片的TPS随着分片数量的增加而下降,这是由于不太理想的分区所导致的增加的跨分片依赖事务。我们计划进一步完善我们的分区算法,同时探索其他替代方法以实现优化分区,从而实现更佳的线性扩展。
有趣的是,执行吞吐量最终并不是由执行层本身约束的,而是由存储层的扩展限制所约束。
在我们的实验设置中,执行协调器管理所有非执行任务,而存储是其中的主要组件。当前,在单节点协调器上运行的存储服务的读/写吞吐量有限。这一限制成为了瓶颈,限制了执行在30个分片之外的水平扩展。这表明执行的扩展架构还有进一步增长的空间。未来的改进将专注于扩大存储层,以进一步推动这一边界。
Aptos致力于提供开源和可验证的性能结果,促进区块链领域的透明性。水平分片执行器代码和性能基准都是完全开源的,我们鼓励区块链爱好者探索、实验,并分享他们的改进建议。
通过Shardines,我们已经展示了在Aptos上可以实现水平可扩展性,达成了区块链性能的一个显著里程碑。我们已经在使用30个分片的情况下实现了100万TPS,但这仅仅是一个开始。通过进一步提升单节点Block-STM的性能并扩大到超过30个分片,Aptos有望开启更大的事务处理能力,并重新定义区块链性能的边界。
展望未来,我们将专注于水平扩展存储层,以消除当前的瓶颈并解锁更高的吞吐量。同时,作为BlockSTM的V2的一部分,我们还将致力于优化单线程(AptosVM)和单节点(BlockSTM)性能。此外,我们致力于在Aptos主网实现端到端的水平可扩展性,架构支持共识、存储和执行层的独立扩展。
欢迎迈向我们所知的区块链技术未来的新范式转变。
- 原文链接: medium.com/aptoslabs/sha...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!