面向未來公鏈的數據庫技術發展方向

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 的累加器可以實現常數大小的成員性/非成員性證明,也可以實現批量添加數據或者刪除。並且最大的好處是即使我不知道當前整個數據庫的狀態,也可以往裏面插入新的數據,這是默克爾樹實現不了的。因爲時間關係這裏就不再詳細介紹了,推薦有興趣的朋友去閱讀相關論文,也歡迎關注我們公衆號的科普文章。

如果說累加器這個技術能被做得比較好一些,至少可以解決分佈式存儲狀態的問題。而且對於新加入的節點來說,因爲可以批量更新狀態,所以再去執行之前狀態驗證操作的時候,性能也會快很多個數量級。

累加器最大的缺點就是在於它太複雜了,需要很多數論和密碼學的知識才能搞明白。儘管在工程上實現一個方便好用的累加器還需要很多年,但是這依然是一個很有前途也很有趣的方向。

這次分享的內容就到這裏,謝謝大家!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章