10 月 19 日下午,由百度超级链学院与金色财经联合主办的百度超级链学院线下技术沙龙《区块链与数据库的融合碰撞》在北京科技寺创业空间滚石店顺利举行。Conflux 研究总监杨光进行了主题为 《面向未来公链的数据库技术发展方向》的分享,以下为演讲整理。
杨光,Conflux 研究总监。本科毕业于清华大学姚班,并在清华大学交叉信息研究院获得计算机科学博士学位。曾在丹麦奥胡斯大学、中科院计算所、比特大陆等从事研究工作。
因为我之前主要研究工作是计算复杂性和密码学这些偏理论的方向,对于数据库我并不熟悉,甚至“关系数据库”具体是什么,我都是临时查了一下才知道的。在这个场合来讲数据库,我一开始是拒绝的,因为有一种“班门弄斧”的感觉。不过后来我仔细想了想,觉得还是应该来。因为虽然说我不懂数据库,但是我比较懂公链啊,我可以代表公链的应用场景来提需求嘛,免得大家光讨论联盟链听起来有些单调。数据库方面的问题我解决不了,提点需求总还是会的。刚才孙君意的演讲里有张图片特别好,讲了什么时候应该用数据库,什么时候应该用区块链。使用区块链特别是公链的话,场景跟之前的传统数据库的使用场景肯定是非常不一样的。所以我这次就主要代表公链的应用场景和应用方向给面向区块链的数据库提一下需求,讲一下在这个场景下做数据库的话,我们希望能够做到什么样的功能,希望往哪个方向发展。顺便也分享一下我们在做项目过程中进行的一些探索和取得的一些小的成果。
首先说一下从传统数据库到比特币有什么不同好。有人说比特币是“有史以来最慢的分布式数据库”,这个说法是不是对的?严格来说,这个说法不能算错,如果你真的把比特币当数据库去用的话,的确是最慢的,这是没有问题。但是比特币并不是作为数据库设计的,也不是作为数据库成功的。这就像你拿一个坦克过来,说这是“有史以来最耗油的车”,这个说法没错,但是坦克设计出来并不是为了当一般的车用,油耗也根本不是设计时首要考虑的问题,这样的比较是不公平也没有意义的。
所以要评价比特币,我们还是先来看看比特币的设计到底用到了哪些技术,用这些技术要实现什么样的目的。我总结的比特币最关键技术有三个:一个是区块链,一个是工作量证明,还有一个最长链规则。
这些技术里面,区块链是比较早的,1991 年就有了。最早的区块链技术是为了给文件加时间戳,并且防止篡改中间的某一部分。采用了区块链技术以后,要是想改文件中间的地方,那么从改的地方到文件的最后都要重新做一遍才行。这个技术是在比特币诞生前 20 多年就有的老技术了。
工作量证明,中本聪在比特币白皮书里引用的是 1997 年的论文,但是实际上工作量证明的想法早在 1992 年就被提出来了。工作量证明解决的是怎么样抵抗别的人用伪造的账户发起女巫攻击的问题。最早提出工作量证明其实也是跟区块链没有任何关系,主要目的是为了防止垃圾邮件。比如说不想收到很多垃圾邮件怎么办?可以要求发邮件的时候都附带做一个工作量证明,这样的话要群发大量邮件的话做工作量证明的成本也会变得非常高,但是正常用户以比较低的频率发邮件时候这个成本相对来说就可以接受。
最长链规则,这是中本聪在比特币里原创提出来的,我认为也是比特币设计中最精华的部分。这个规则就把上面的区块链和工作量证明整合在一起形成整个共识系统。因为有工作量证明,所以出块的速度会控制在一定范围之内,产生区块成本相对比较高;把区块串联成区块链以后,你想在链的最后面新加区块就比较容易,但是如果要想改前面的东西,就必须把从修改的地方到最后所有区块全部重新做一遍。在每个区块都有工作量证明的前提下这个成本就非常高。比特币上的交易历史很难被篡改就是因为这一点。
最长链规则解决的就是看到链发生分叉的时候应该怎么选择,到底哪个分支是有效的。如果没有这个规则,在比特币里大家会看到有很多分枝的一棵树,谁也不知道哪个分支是有效的,坏人就容易浑水摸鱼。有了最长链规则以后,就可以保证诚实的人聚集在一起,最后大家会对选择哪个分支达成共识,对于哪些数据有用哪些数据没有用都有一个共同的看法。
我认为比特币提供的最有意义的贡献,首先是有一个无许可/去中心化的、匿名的参与机制。任何一个人,不需要任何别的人同意,只要有电脑——现在或许需要矿机,就可以参与进去;只要如果能算出来一个新区块的工作量证明就可以把自己选择的交易写在比特币的账本上。
我认为无许可和去中心化也是比特币价值最高的一点,这也是公链的价值核心所在。如果是联盟链或者其它有许可的机制,大家事先就可以知道有哪些节点,哪些人会参与共识,有好处也有坏处。好处就是实现起来非常方便,性能也比较容易做。这样的联盟链在基础上和传统分布式系统和分布式数据库就会比较像,区别就是之前所有的节点和机器都是一家,你可以无条件信任别的机器,只需要担心机器会不会宕掉,而联盟链更新数据的时候需要根据一些条件验证一下。但是在联盟链或者其它有许可的链的场景,至少你知道都有谁参与共识。知道这点以后最大的好处就是投票比较方便,投票以后知道多少票表示多数,多少票意味着比如三分之二的参与者同意。只要数够了票数就可以保证结果是很难被篡改的了。但是这样做也有坏处,失去了无许可的去中心化也就丢掉了比特币的灵魂,在安全性上的隐患很大。
但是比特币最大的问题,或者说公链最大的问题,就是不知道具体有多少人参与共识,因为谁都可以参与,而且任何时间想来就来想走就走,最终谁也没有办法知道到底有多少人在这个系统里面。但是即便如此,我们依然要得到一个比较可靠的投票结果,或者说是共识。这也是公链要解决的核心问题。
我们常说以太坊是区块链 2.0,它与比特币相比主要是增加了智能合约。为了支持智能合约,以太坊每个节点都要记录每个账户的状态,这个比 UTXO 模型只需要记录余额要复杂很多;同样是因为支持智能合约,以太坊里面访问数据的方式、更新数据的方式也比比特币灵活很多。但是因为以太坊吞吐量并不是很高,即便是修改数据的方式更灵活,记录的状态更复杂了,但是实际上实现起来在数据库技术这方面的难度并不算特别大。以太坊现在的吞吐量也就是大约二十 TPS 的样子,用传统的数据库做一下就很容易在节点上运行起来以太坊的节点。
对于什么是区块链 3.0,现在有很多种说法,区块链 3.0 要有更高的性能,要分片或者跨链之类的各种观点都有。但是最起码的一条要求就是更高的性能。因为比特币和以太坊的性能实在是太低了。把它们作为一个实验性的系统或者作为一个不太大规模的应用载体是可以的,但是要做大规模的应用,无论以太坊还是比特币这样的主流公链性能都是远远达不到要求的。所以区块链 3.0 一定要有比较高的吞吐量和比较快的确认时间。比特币通常来说确认一笔交易要等一个小时左右,以太坊大概十分钟这样的量级。这样的速度对于日常使用来说,很多用户场景都完全没有办法用。但是如果可以在一分钟以内甚至几秒内确认交易,那么现在很多的场景就变得可以用这个去结账,使用体验跟支付宝之类的中心化应用就没有那么大了。现在的一个小时确认时间就会有明显的差距——能用跟不能用的差距。
说到区块链 3.0 性能,实际上几千 TPS 的性能对于硬件环境,处理器技术来说或者对于数据库的技术来说并不是一个特别大的问题。现在网络带宽 10Mbps 不算很高的要求,这足以支持几千 TPS 的交易的传输了;存储方面,现在的 SSD 的性能可以支持两千到一万都是可以的;计算方面,如果 CPU 拿出一个核用来专门验证每笔交易的签名,每秒可以验证几千甚至到一万笔交易。这是还没有上高性能服务器或者分布式计算的情况下,用单台的计算机就可以达到的性能。既然单台计算机都可以达到这样的性能,如果有人再说要做到几千甚至区区几百 TPS 就必须在安全性或者去中心化方面放弃一些东西,比如说要有 21 个超级节点,你就会觉得这件事有一点不那么让人信服了。我们根本不需要放弃什么才能把性能提高到几千 TPS 这样一个程度。当然,如果要提得更高,要超过单台计算机的处理能力的同时保持去中心化,就需要比较高级的技术才可能做到了。
公链场景用到的数据库和通常互联网或者传统场合用到的数据库,使用的环境是不一样的,数据库操作的特点也很不同。在区块链上,我们知道越往后面的区块越容易被修改。以比特币为例,如果我想把最后的一个区块修改掉,我只要能快速地出两个区块,同时原来的最后那个区块后面还没有新的区块,主链就会回滚,之前那个区块就变得无效了。另一方面,如果我想要修改很久以前的某个区块,那就会非常困难。相比之下,传统的数据库通常假设每个数据被修改的可能性都是差不多的,花的成本也都差不多,因此要对修改各个数据的成本做一个平衡。但是如果这个数据库只有最后部分数据比较容易改,靠前的数据会读不会改,去做一个针对性的优化是不是可以把性能做得更好一些?
而且实际上对于高性能的公链,因为要实现比较快的确认时间,一定要提高出块速度。如果像比特币那样 10 分钟出一个块,实际的确认时间还是出块时间的若干倍,确认速度肯定快不起来。但是如果出块速度快的话,即使在没有人刻意攻击的情况下也会频繁地发生分叉和回滚的情况。相当于数据库最后面的部分被反复重写的频率会比较高。
我们来看一下为什么区块链里面会出现分叉,即使没有人攻击的情况下区块链也会自然地出现分叉。这个图里的横轴表示时间轴,每条线表示一个节点的时间线。某一个节点生成了区块后,需要把区块广播出去,而广播出去需要一定时间,经过一定时间以后才能保证其他的节点都可以看到新生成的区块。当其他节点处于绿色的部分时,他们是看不到生成的新的区块的,即便此时实际时间已经晚于区块生成时间了。如果一个节点在这块绿色的区域里就生成了另一个新的区块,那么是不可能引用正在广播的区块的,因为我还没有看到。在无许可的系统里面这是肯定会发生的事,只是概率大小的问题。为了解决这个问题,我们可以看到在后面的黄色部分都是可以安全地生成新的区块的,这时候如果一个诚实的节点生成区块的话肯定会引用之前的最新区块,因为他们都看到了那个区块。如果设计系统的时候希望出现分叉频率比较低,就要尽可能降低绿色的部分面积所占的比例。黄色的比例面积越大就意味着这个系统出现分叉,有时候也称为孤块,的概率就越低。
当然区块在节点中传播不大可能是这样的锥形,但是大致是这么个意思。
比特币是怎么解决的?只要把出块间隔拉长,大家会看到相对来说黄色的部分比例就会变大。所以比特币采用了十分钟的出块间隔,区块广播的时间是比这个短得多的。只要不是在那么短的广播时间内同时挖出两个新的区块,最后就不会出现孤块,系统的安全性就会比较好。
但是这么做在性能上就会有一些问题。我们来考虑比特币对系统资源,特别是对带宽的使用。总的带宽可以分成三个部分,一个部分是真正用来传输交易的有效带宽;一部分是协议消耗的,比如大家同步信息需要用的一些带宽;还有一部分就是大家闲着在等别的消息,是空闲的带宽。如果用最长链规则,像比特币这样十分钟就传 1M 大小的区块,就算再加上转发加上隔离见证,十分钟内最多也就发送十几兆、二十兆这样的数据量,除此以外的大部分时间是闲着的。我们本来算的是 10Mbps 带宽如果一刻不停地发,可以支持几千 TPS 的吞吐量。但是如果大部分时间大家都闲着,带宽都浪费掉,当然吞吐量不可能上去。
如果提高区块的生成速度,广播得更快,区块生成速度也更快,这样也是可以的。例如采用致密区块技术或者用更好的网络条件缩短广播时间,可以相应地缩短区块之间的间隔同时保持安全性。但是这样提升的性能也是非常有限的。我们可以看到在一个区块广播的这段时间内,依然是大部分带宽处于闲置的状态。如果很多节点可以并发广播很多区块,肯定对带宽的利用率更好,对资源的使用更有效率,也更有利于提升性能。
如果我们真的想把带宽、处理能力,把这些资源充分利用起来,肯定希望能够尽可能快地出块,最后就会很频繁地出现分叉,出现分叉以后有可能回滚。最终意味着区块链数据最后的那部分可能会经常遇到修改。
以我们的项目 Conflux 为例,为了实现极致的性能,我们是基于图去做共识的。Conflux 的共识规则允许节点并发地产生很多没有相互引用关系的区块,并且这些区块里面的交易都算是有效的,最后会按照一定规则全部放在一起排序。这样排出来的顺序最后的一段就比较不稳定,出现变化的可能性很大,对应于还没有被确认的区块和交易。
为了解决这个问题,我们在实现节点的时候使用了延迟执行和延迟写入的策略。比如说在很多其他的区块链系统,每个区块都会把该区块执行完以后的状态写在自己区块里面。但是其实这个做法,大家想一想就会发现并不是那么必要。就算把这个状态写在区块里,别人看到以后就知道执行到这里的状态是这样的,但是这个区块本身都还没有被确认,一个没有确认的状态实际上价值并不是很高,因为它将来还有可能会被改掉。如果被改掉的话你之前写的状态就没有人看了。
为了节约计算资源,我们做一个延迟执行的设计。在区块里写的状态不是当前区块执行完以后的状态,而是往前若干的区块执行完的状态。这样的话只要出现的分叉不太长,即使出现了两个分叉,但是往前走若干步以后仍然是同一个祖先区块,两个分叉的区块里面写的状态就是一致的。这样,如果从这个分支跳到那个分支上,主链会出现回滚,但是出现回滚的时候并不会影响之前计算的状态。
只需要保证延迟执行的延迟程度比确认需要等待的时间要短,就可以保证对于所有确认的区块我们都知道确认以后执行的状态是什么样的。这样的话使用的时候大部分情况下都已经足够了。至于还没有确认的区块执行以后状态是什么样的,其实暂时并不需要那么在意,还是要等到确认以后才可以用。
关于延迟写入,把达成共识的交易历史存到一个数据库里,相对来说前面确认的部分很少修改,基本上不会动,但是后面这部分会经常修改。修改的话可能会出现回滚,回滚以后需要把交易重新排序和执行。为此,我们把后面没有确认的部分单独存在比较小的临时的数据库里面,这样的话在这个小数据库里面进行查找和各种操作都会比较容易。等到确认以后再把小数据库的信息合并到前面稳定的数据库里,基本上写入以后就不可能再被修改。这样的话,每次短的回滚都只需要在后面很小的数据库进行操作,可以比较高效率地进行操作。
刚才说的技术,主要是我们在实现高性能公链节点的时候需要用到的技术。还有另外一个层面,是把区块链本身看作一个数据库,如果公链想做去中心化的数据库需要实现哪些功能。
我认为一个非常重要的方向是降低链上信息处理和信息存储的成本。比如像以太坊上面的存储,定价和收费方式其实是非常不合理的。主要原因是它是一次收费永远存储,而存储的成本是随着时间增加的,存的越久成本越高,如果只收一次费的话定价肯定是有问题的。其次以太坊存储费用是付给打包区块的矿工一个人的,但是成本却是所有节点都要承担,这也很不合理。实际上,一个矿工可以通过把存储费用付给自己的方式在实际上免费存放数据。
如何要解决这个问题?现有的提出来降低存储成本方法,一个方法是分片,也就是把整个网络分成若干部分,每一片上面的节点只处理自己这一片的数据和交易。这样肯定可以在整体上提升整个系统性能,并且降低成本。但是分片在安全性上有很多的问题。比如说分片以后每一个片上去验证交易的节点数量就会变少,验证过以后在上面做工作量证明或者其他方式证明的强度也会变弱,因此分片系统的可信度、安全性会变低。特别是对于 POW 系统会分散算力,这就给安全性带来一个很大的问题。
为了降低信息存储成本,我们希望有办法实现既可以分布式存,不要求每个节点都存储所有数据,例如节点可以不存自己不关心的数据,但是同时又可以在没有所有数据的情况下正常更新。在传统的领域可以用 RAID 实现类似的功能,但是如果节点互相不信任的话肯定要用别的方法。
还有一个很重要的点是批量更新状态。我们知道现在大部分区块链用的数据存储结构就是默克尔树(Merkle Tree)或者它的变种。默克尔树最大的好处是结构非常简单易懂——一个合格的程序员十分钟就能看懂原理,一天就能给你写出一个代码的实现。但是默克尔树在使用时候也有很多缺点。比如说证明某个数据属于一个默克尔树对应的状态,需要花的代价量级是对数级别的,数据越多、树越深,需要花的成本越高。
还有一个问题,如果在同时更新很多的数据,对于默克尔树来说是很难整合的。即便在公共节点上能省掉一些东西,但是基本上随着数据的条目数量线性增加。这个成本对于比特币和以太坊来说还不是特别大的问题,因为它们足够慢。
但是如果一个吞吐量很高的系统在跑了一两年以后有新的节点要加入,新节点要重放以前的所有交易,这个时候问题就很大了。因为本身这个系统就一直在全速跑,你用一个性能没有比它高多少的机器,恐怕需要花很久的时间才能跟上过去一年的数据。然后这段时间里,又产生了好几个月的新数据。
所以能不能有一个可验证的方式去批量更新状态,让我们可以很容易验证这个状态更新是对的,而且不需要重复每笔交易,这也是一个未来公链需要解决的问题。尤其想实现非常高的性能的话,这个问题是必须要解决的。
还有一个发展方向是关于隐私和加密方向的。因为现在有一些像是 ZCash,Monero,Grin 等,上面的交易具有很高的隐私性。在这些带有隐私保护的链上,矿工看不到谁转给谁钱,也看不到转了多少,同时他们还要验证每笔交易对不对。这件事现在能做但是成本比较高。而如果是更复杂的,比如说要验证一个智能合约执行是不是对的,成本基本上高到没有办法做的程度。
在具有隐私保护功能的数据库上,更新数据库的时候既要验证这个人是不是有权更新数据库,他更新的数据是不是合法,又要保护数据的隐私性,做起来也是很难做的一件事。更难的是,在数据库保持加密的情况下,要在上面做一些计算任务。这件事理论上有一些成果可以实现,但是特别慢。比如用全同态加密可以做任何运算,只不过大概要花出五个数量级的成本,也就是十万倍的代价。这实际上就导致很多应用根本没有实现的意义。比如说外包计算,外包的计算成本一下子高十万倍以后,肯定没有本地计算便宜了。
最后在密码学方面也有很多相关的研究成果,比如密码学的累加器(Cryptographic Accumulator)。基于 RSA 的累加器可以实现常数大小的成员性/非成员性证明,也可以实现批量添加数据或者删除。并且最大的好处是即使我不知道当前整个数据库的状态,也可以往里面插入新的数据,这是默克尔树实现不了的。因为时间关系这里就不再详细介绍了,推荐有兴趣的朋友去阅读相关论文,也欢迎关注我们公众号的科普文章。
如果说累加器这个技术能被做得比较好一些,至少可以解决分布式存储状态的问题。而且对于新加入的节点来说,因为可以批量更新状态,所以再去执行之前状态验证操作的时候,性能也会快很多个数量级。
累加器最大的缺点就是在于它太复杂了,需要很多数论和密码学的知识才能搞明白。尽管在工程上实现一个方便好用的累加器还需要很多年,但是这依然是一个很有前途也很有趣的方向。
这次分享的内容就到这里,谢谢大家!
评论 3 条评论