不过,对于现有的单链系统,确实有一个不可逾越的物理限制,那就是全节点平均带宽。
以 13Mbps 的带宽为例 这是互联网带宽中位数,4K 电影在线播放带宽下限要求为 15Mbps,无论基于什么共识技术,即使完全丧失去中心化,并忽略传送协议,以及层层封包等额外引入的代价,以比特币网络现在的每秒处理 7 笔交易的吞吐量来算,其吞吐量理论上限为6825TPS(7TPS × 13Mbps / 8Mb × 600s)。
否则就会出现部分全节点脱网,即长时间无法赶上全网的区块增长,而离最新头部块越来越远。
我的上一篇已经提到,性能瓶颈和容量瓶颈在现在的结构下无法突破,因为单个全节点的带宽和存储资源总是有限的,并且我们还不能要求部署全节点的机器都具备顶级配置,因为只有保证了参与全网的壁垒较低,才能真正具备有良好的去中心化特性。从这个意义上来说,要获得大幅度的伸缩性,我们必须要能够有一个合理的设计,使的单个全节点仅仅负责整个网络吞吐量和容量的一部分,这样,才有可能在维持全节点的负荷比较小的前提下,使得全网的性能和容量有大幅提升。
分片 Sharding 技术就是针对这一思路提出的。
分片最初是基于数据库系统提出的,泛指横向切分数据库服务器的负荷,用多个数据库服务器平行服务的方式,以提升整体的服务吞吐量和数据容量。
实际上,分片并不单指一种特定的技术,而是一大类横向切分系统负荷,多实例并行,以提高整体性能和容量的技术。
不过,分片这个提法在区块链技术中过于宽泛。在这篇文章的最后部分,我会通过引入「异步共识组」这个概念,谈谈我认为什么会是区块链中理想的分片方案。
在讨论我认为的理想方案之前,我还是用我习惯的逻辑,先向读者解释为什么我们需要分片技术、理想的分片技术究竟应该解决什么问题这些最基本的知识;
然后,我会根据自己看过的大量的分片方案,谈谈目前市面上已有的分片方案存在的五个误区或谎言。
为什么需要分片:从全节点的负担谈起
在分析具体技术方案之前,先来看看一个全节点都有哪些负荷:
网络带宽,这个部分同吞吐量 TPS 线性相关
1. 新区块的广播
全节点需要下载周期性出现的新区块,并进一步上载给其他需要的节点。对于现在 7TPS 的比特币区块链来说,平均下载带宽消耗是 13.6Kbps,完全不是负担。但是如果 TPS 提高到 7000TPS,则至少需要 13.6Mbps 的下载带宽才能承受——请注意,这个是关键数据,并且有序,如果区块数据得不到及时传输,区块链将无法持续构造,并且无法重建全网的账簿状态。
2. 未确认新交易的广播
全节点需要持续发现,并下载尚未被确认的新交易,然后上载给其他节点。在全网不拥堵的情况下,这个数据量和新区块的数据量相当。相对的,这个数据不需要保序,即使下载不全也不会直接导致全节点本身工作异常。但是,如果这种情况大规模地发生,最终会导致部分交易始终没有被矿工看到,而长时间不被确认,最终超时。
内存容量,这个部分同用户量 address 数量 以及智能合约数量线性相关
账簿的存储占据主要的内存消耗,其他部分的消耗不多,并且基本不会随着网络扩展而增加。
区块链的基本账簿, 至少需要维护每个用户 每个 address 上的余额,每个用户至少需要 16+32 个字节。对于支持智能合约的区块链,每个用户至少需要额外 32 个字节记录每个智能合约的代币的余额。智能合约自身还可以自定义更多的字段,可以占用远超 32 字节的内容。
当然,并不是所有的用户都参与了所有的智能合约,所以我们并不以用户量和智能合约数量的乘积作为内存消耗的估量。现实的情况是,我们按照以太坊的现状来估计,约 4500 万个地址,至少占据了约 3GB 的内存。每个地址平均消耗了 68 个字节。这里并不是说以太坊有了 4500 万个用户,通常而言,一个用户会拥有好多地址。
磁盘 I/O,这个部分同吞吐量 TPS 线性相关
全节点收到区块并确认成链之后,区块会被写入磁盘归档起来。这个部分的吞吐量大体和下载新区块的带宽一致。
这里需要注意的是,按照以太坊的观点 账户模型,账簿是指全网的某一时刻的全量状态,而区块(若干交易的集合)是对账簿的增量修改信息。在磁盘实际存储的是区块,而不是账簿。节点在启动的时候,需要重放所有已经记录的区块,以构造账簿的最新状态。
截至目前,以太坊的全部归档区块数据量已经超过 160GB。这里的一个优化是定期将账簿全量快照也记录下来 即 checkpoint,这样只需要从最近的 checkpoint 开始重放就可以了。
而 UTXO 模型是比特币的观点,这是一个非常有意思的结构,它的区块既是若干交易的集合,同时也是部分账簿的状态,即特定地址上的余额。整条链上余额不为 0 的 UTXO 都是账簿的最新状态。至今比特币区块链的全部归档区块数据量已经超过 220GB。不过从磁盘 I/O 的角度来看,重建全网账簿还是需要扫描整条链找出全部余额不为 0 的 UTXO,并没有什么优势。当然 checkpoint 技术对 UTXO 一样有效。
在这个领域,最近我看到了一些被热炒的改进,尤其是在已确认交易集合的聚合表示方面。我注意到斯坦福大学的一个团队最近提出,RSA累加器(RSA Accumulator)可以代替Merkle Tree,用来表示一个已确认交易的集合。
这个想法的优异之处在于,可以直接用一个交易的Hash值来校验一个交易是否存在于已确认交易的集合,而Merkle Tree不仅需要一个交易的Hash,还需要从树根到该交易沿途路径上的所有兄弟节点上的Hash。不过,这个做法的代价是,这个表示本身要1.5K字节,Merkle Tree的根仅需要20或32个字节。同时,这并不意味着全节点可以不再保存历史交易,原因在于,首先集合的聚合表示是不具备枚举的能力的,也就是说,在交易未知的情况下,集合的聚合表示无法告诉你里面具体的交易是什么,所以你也无法判读这个集合本身是不是正确的,所以全节点也无法仅凭这个信息确认链头的正确性。
全节点在自举的时候还是需要从创始区块(genesis block)开始下载,逐块验证全链的完整性和正确性。但是,当这个验证过程完成之后,为了节省空间全节点,是可以删除掉区块中的实际交易内容的,其代价是丧失枚举交易的能力,同时也丧失了帮助其他全节点自举的能力。
另外,通过验证一个交易是否在链头上,来确定账户余额的做法,仅对于UTXO交易模型有效。对于以太网这样每个交易仅为增量修改信息的交易模型,仅靠验证一个交易是否在链头上,是无法得知账户的余额的。
CPU 处理能力,这个部分同吞吐量 TPS 线性相关
1. 密码学相关计算
对于每一个收到的新区块,无论最终时候成功成为链的一部分,全节点都需要验证其携带的每一个交易是否被正确签名。同时会验算每个区块的 Hash,以确保其达到了当前工作量证明PoW 难度的要求。对于拜占庭容错 BFT 算法,还需要验证各个委员会成员的签名是否正确。
这里主要的工作量是验证签名。验证签名在一台普通计算机上 E5-1620@3.5GHz, C++ 并行实现 的速度可达 40K op/s,也就是 4 万 TPS 每个交易需要验签一次。
2. 交易验证相关计算
对于每个交易,如果是支付交易,将需要至少查找一次账户的索引结构 通常为哈希表,并做一次加法和大于的判断;为了更新账簿,还需要至少一次加法和减法。这样的一系列操作,在一台普通计算机上的速度可以轻松达到 1M op/s。如果是智能合约,会需要在虚拟机中执行对应的智能合约指令,对于现有的金融本质的应用 包括类似 Cryptokittes 类或 Fomo3D 类的区块链游戏,至少可以达到 100K op/s。
这意味着什么?
针对上面的分析,我来做一个小结:对于一台普通的计算机,拥有 13Mbps 的互联网连接,E5-1620@3.5GHz 4Core 的 CPU,16G 内存,512G SSD 硬盘 250MB/s。网络带宽导致的 TPS 理论上限约为 7 千 TPS,硬盘文件 I/O 导致的 TPS 理论上限为 5 万 TPS,CPU 处理能力导致的的 TPS 理论上限约为 5 万 TPS。另外,内存大小导致的地址理论上限约为 2.5 亿个地址。
这里可以看到,对于性能来说,网络带宽是最首要的瓶颈根源,其次是硬盘的I/O和CPU的处理能力。对于容量来说,内存是最主要的瓶颈根源。
下一篇:《分片应该做到什么?分片方案的一些误区》
欢迎大家通过我的微信公众号「王嘉平」和知乎专栏「去中心化数字世界随想」,就这个话题展开更多讨论。