分片应该做到什么?
从上面对瓶颈根源进行分析之后,我们可以得到这样的结论:特定的分片方案至少要切分系统的网络带宽、内存容量相关的工作量,才有可能大幅提高全网的伸缩性,才有可能打破所谓的「不可能三角」。
必须再次强调,这个结论非常重要。因为只有真正了解这一点,才能进一步讨论分片方案是否真的可以打破「不可能三角」。
虽然在前面的分析中,带宽和内存是短板,但实际上,其他部分的约束也并不富余。理想的情况下,分片系统能够切分上述四个方面所有的负担。对于一个有 n 个分片的系统,其每一个分片仅需要承载 1/n 的全网负荷,将网络带宽、硬盘文件 I/O、CPU 处理和内存容量的消耗都降到原来的 1/n。
在分布式系统中,这个是高伸缩性能带来的性能和容量提升的上界。一个分片系统如果可以实现这个,即性能和容量可能随着分片个数 n 而线性提升,那么理论上,可以使得全网的性能和容量能够被无限地提升。
而现实情况是,分片系统为了实现应用逻辑的正确性,协调各个分片之间的运作以及实现整体的安全性,需要每个全节点引入额外的工作量 overhead。
这些额外的工作量是个常数 O(1),即和分片个数 n 无关,那么全网线性提升仍旧可以保证。
如果额外的工作量的阶 order 小于线性,例如 O(log n),那么就意味着随着 n 的增加,全网提升比额外工作量的增加要来得快,全网线性提升还是可以实现的。但是,如果额外的工作量的阶大于或等于线性,例如 O(n log n) 或 O(n^2),那么基本上全网提升到一定程度,就无法继续了。因为全节点额外引入的工作量成为了新的首要瓶颈。
跨分片交易的原子性保证
区块链中的每一个交易都是原子的,必须保证其涉及到的操作或者全部完成,或者一个都不开始,才能使得最终状态是正确的。
对于涉及到多个分片的交易,我们不得不协调发生在各个分片中的操作,保证它们都能被没有错误地完成,并且不受其他交易的干扰。这里看起来可以简单借用在多线程编程中常用的线程同步概念,用诸如临界区 critical section 等方式,锁住涉及到的状态,阻止其他交易触碰这些状态,在完成交易所有操作后释放。但是,这会导致部分分片的执行被阻塞,而导致实际性能大打折扣。并且随着全网规模扩大,分片数量增多,跨分片交易的比例必然会极剧上升,从而使得加锁变得非常频繁。
一个设计良好的分片系统,必须具备高效的跨分片交易处理算法,并且算法的开销应该和分片数量 n 无关。
单个分片的安全性保证
分片系统中,如果每个分片有独立工作的共识系统,也就是每个分片自己一条链,全网能最大获得的吞吐量的 n 倍提升。
共识系统的安全性依赖于出块权力的大多数 majority 是可信的,这个大多数在 PoW 系统中为 50%,在 BFT 系统中为 2/3,甚至更高。而当全网有 n 个独立工作的共识系统时,这时平均分到每一个分片的大多数变会降低到原来的 1/n。这样攻击这些分片中的共识系统的壁垒,对于 PoW 系统会降低到全网的 50/n % , 对于 BFT 系统则会降低到 1/(3n)。从而导致,仅需要非常低的成本,就可以攻击某一个特定的分片,例如构造双花攻击 double spend。而在一个分片系统中,只要有一个分片被攻击,其后果会藉由跨片交易迅速污染到其他的分片。
这就是为什么每一个分片系统都必须处理好安全问题,使得攻击特定分片的代价同攻击全网一样高。
分片方案的一些误区
分片的颗粒度不够细
分片的切分依据需要有足够的颗粒度,这样才能使得分片个数 n 取到足够大的值,并且不容易产生单点过热 hotspot。例如按照交易参与方的地址分,或者按照交易本身的 hash 值来分。这些是可行的方案,因为地址和交易的数量本身足够的多。
早期我还看到有些分片的方案按照业务来切分的,这是不可取的,颗粒度不够细,并且如果单个业务活跃度很高,需要更高的性能和容量,却无法获得分片系统所带来的提升。
在全网分片结构不发生改变的前提下 例如给定分片数量 n,分片的切分依据应该是确定的,和账簿状态无关的,并且是可以被简单计算的。
对于提交交易的轻量节点,可以根据交易内容唯一确定这个交易应该被发送到对应的某个分片。早期看到动态的调整地址归属,试图将经常交互的地址划分在同一个分片,从来减少跨分片交易发生的概率。这种做法是不切实际的,因为本质上同分片数量相矛盾。就以太坊 ERC20 的历史支付交易来看,在 4 个分片的时候,跨分片交易比例为 75%,在 32 个分片的时候,跨分片交易比例为 97% 按支付发起者的地址 , 平均随机划片。
鼓吹智能合约的计算复杂性
从前面的分析可以看到,CPU 处理并不是短板。不过我却在现实中看到若干方案,强调智能合约非常复杂,不仅 GPU 不够用,甚至还需要 GPU 乃至集群来计算。
在分片设计中,每个分片依旧承载全网全部的吞吐和容量,只是将交易的验证以及状态更新分开在各个分片做,并且之后引入一个 O(n^2) 的通讯量,将部分更新过的状态传播到其他分片。而事实上,即使是现实世界中的业务,各种交易、清算其实际计算一点都不复杂,就是一些加减乘除,而更多的工作量是在安全验证以及通讯上面。
当然我们可以假想一种奇特的应用,需要巨大的计算量来完成逻辑层面的交易验证和状态更新。这会使得 CPU 处理最先成为瓶颈,并且使得 GPU 加速变得可能有意义。不过我会很怀疑这样的应用 或者应用的这个部分 是不是区块链的真实需求,是不是应该由区块链来承载。
忽略容量瓶颈
近期我看到一些分片方案,认识到了性能瓶颈的本质是带宽的约束,并从这一点切入划分工作量,让每个分片负责全网的一部分交易,包括其相关的计算。
不过非常遗憾,我看到的这些方案没有切分和用户容量相关的负荷,每个分片仍旧需要维护全网所有用户所有 DApps 的状态。并且,由于所有用户状态需要每个分片都维护,导致某用户在特定分片中的更新必须广播到其他的分片,这将额外引入一个 O(n) 的通讯量。由于用户容量相关的负荷没有被切分,导致系统无法承载更多的用户和DApp在链上发生交互,从而使得性能的提升无法被充分利用,约束上层应用的发展。
忽略安全问题 被稀释的可信大多数
前面的分析已经提到,在分片之后可信大多数被稀释,攻击成本将极大下降。但是,到目前为止,我还没有看到有可靠的方案,能切实解决这个问题。
可能会有团队说,攻击成本即使降个几倍,也是很难攻击的,攻击要花很多资金,没人会真的去攻击。我不认同这种看法。
我的观点是,即使在不分片系统中,直接的暴力算力攻击已经屡有发生,更别说攻击成本被降低的分片系统了。如果我们不处理这个问题,只是祈祷没有人来攻击,那么在这样的系统设计之下,性能和安全就构成了直接的矛盾,越高的性能,即越多的分片,就会导致更低的攻击成本。这其中安全风险巨大。
双层设计
有不少分片方案,在各个独立的分片之外,引入了一个根链 有些叫做主链或母链,由其完成各个分片之间的沟通和协调,或者由其来保障各个分片的安全。
对于这类方案,需要比较细致的个例分析,不排除有可行的方案出现。但是总体上,当跨分片交易的比例很高的时候,根链有极大的可能成为系统的瓶颈。而利用根链作为安全保障的系统,其实本质上很可能就是一个单链系统。
在我看来,优异的分片设计方案,不应有分层的结构,而是各个分片应该是同质的,在功能上完全一致,地位上也完全平等。
下一篇《异步共识组 Asynchronized Consensus Zones》
欢迎大家通过我的微信公众号「王嘉平」和知乎专栏「去中心化数字世界随想」,就这个话题展开更多讨论。