我的上一篇文章探究了單鏈公鏈系統在性能和容量上受限的本質原因。本來我以為文章過於「技術」,會遭到普通讀者的嫌棄。完全出乎我意料的是,竟然有上萬的讀者通過各種渠道閱讀了這篇文章,並且,我收到了不少朋友和讀者的反饋。他們鼓勵我繼續寫下去,把我自己在分佈式系統研究和區塊鏈投資領域的經驗和心得分享出來。
我非常感謝這些朋友,還有每一位讀者。但是我還是非常擔心,我的文章有太多技術性語言和技術上的分析——比如今天這一篇。不過,我會努力用最簡單的詞句、最直白的語言,把區塊鏈領域看似很深奧的理論和方案,用淺顯易懂的方式介紹給大家。
通過上一篇文章「區塊鏈公鏈如何才能快起來」,希望讀者可以從文中解釋的那些原因出發,明白這樣一個結論:安全的共識機製本質上並不跟系統的吞吐性能以及容量相矛盾。在區塊鏈的系統裡面,所謂的「不可能三角」——安全、去中心化、性能,這三者只能取其二的說法,並沒有技術邏輯的論證,而只是對現有解決方案的觀察。
不過,對於現有的單鏈系統,確實有一個不可逾越的物理限制,那就是全節點平均帶寬。以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的處理能力。對於容量來說,內存是最主要的瓶頸根源。
分片應該做到什麼?
從上面對瓶頸根源進行分析之後,我們可以得到這樣的結論:特定的分片方案至少要切分系統的網絡帶寬、內存容量相關的工作量,才有可能大幅提高全網的伸縮性,才有可能打破所謂的「不可能三角」。必須再次強調,這個結論非常重要。因為只有真正了解這一點,才能進一步討論分片方案是否真的可以打破「不可能三角」。
雖然在前面的分析中,帶寬和內存是短板,但實際上,其他部分的約束也並不富餘。理想的情況下,分片系統能夠切分上述四個方面所有的負擔。對於一個有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)這個概念來,大家討論一下理想的分片設計究竟應該是什麼樣的。
我先來解釋一下,什麼是異步共識組。
異步共識組由多個同質的、功能上完全一致、地位上也完全平等,並邏輯上盡量隔離的獨立共識系統的實例所構成,他們並行工作,分攤全網的吞吐壓力,分攤全網狀態的維護工作。
0. 具備獨立的相對穩定的節點集合,邏輯上不要求一個節點參與到多個共識組
1. 具備獨立的賬簿,承載全網的一部分用戶(組內用戶)。各個共識組的組內用戶沒有交集
2. 具備獨立的Chain of Blocks,僅記錄已經確認的和組內用戶相關的交易
3. 具備獨立的非阻塞的出塊過程,各個組之間沒有任何同步的需要(如需要互斥鎖定特定資源)
4. 具備獨立的未確認交易集合,僅有和組內用戶相關的未確認交易會被暫存
5. 具備獨立的出塊候選或競爭機制,礦工僅限於組內競爭,和其他組的礦工無直接競爭關係
6. 具備獨立的Gossip網絡,完成區塊和未確認交易的廣播,不波及其他共識組的節點
異步共識組中每一個共識組採用具體的共識算法可以是PoW/PoS,也可以是BFT類算法,或這些算法的改進。異步共識組的設計是如何讓他們完全獨立地並行工作,由於各個共識組在IT資源的消耗上是互相獨立的,那麼全網的吞吐性能和賬簿容量將隨著全網共識組的數量增加而線性增加,而參與其中的全節點的工作壓力依舊是單個共識組的負荷。
異步共識組是共識算法中立的,並不限定具體的在各個分片中採用的共識算法。有很多團隊在研究和改進共識算法,無論共識算法被改進到什麼程度,任何已知的共識算法或者是今後的改進版本,都可以被應用在異步共識組這個技術架構之中,從而得幾個數量級的性能和容量的提升。
假設我們用比特幣的PoW算法,用同樣的配置參數(1MB的塊,10 分鐘的出塊間隔)作為共識組的共識算法,對於一個具有1024個共識組的網絡,全網將獲得7000多TPS的吞吐性能。分片的全節點按照一台普通的計算機來算,在13Mbps的互聯網連接、E5-1620@3.5GHz的CPU、16G內存、SSD 硬盤(250MB/s)的情況下,全網將具備等效的13Gbps帶寬、16TB內存、4096個Core的計算能力,以及250GB/s的I/O能力,用來承載所有用戶的賬簿和DApp的狀態,以及其中涉及的計算。而對於每個共識組裡面的每個節點,它們所負擔的通訊、計算和存儲的壓力,僅同運行一個比特幣系統的全節點的負荷相當。無論全網擴張到多大的程度,網絡中的單個參與者始終不會有明顯増大的負擔。
異步共識組的挑戰有哪些?
異步共識組是一個理想方式的模型,不僅概念簡單,而且可以完美突破全網的性能瓶頸和容量瓶頸,實現高伸縮性的區塊鏈系統,並且對去中心化程度沒有任何的犧牲。但是,這個模型要成為切實可行的方案,正如上文提到的,需要讓這樣的一個系統正確工作並保障安全性。這裡需要兩個方面的設計巧思:
第一, 如何正確高效地完成跨共識組的交易;
第二, 如何保障每個共識組的安全性。
只有,解決了這兩個挑戰,才能使得異步共識組系統能夠正確安全地工作,釋放其強大的性能和容量,打破所謂的「不可能三角」,服務於大體量的區塊鏈應用。