1. 程式人生 > >The Science of the Blockchain筆記(一)

The Science of the Blockchain筆記(一)

一、分散式系統

1. 什麼是分散式系統

       分散式系統(distributed system)是建立在網路之上的軟體系統。今天的計算和資訊系統本質上都是分散式的,比如我們的手機,具有與雲分享資料以及存在多個處理器和儲存單元的特性。

2. 分散式的原因

(1) 地理位置:現在大的公司都會分佈在不同的地方,每個地方會有很多臺的計算機用於處理計算等事務。
(2) 並行性:為了加速計算,還會使用多核處理器和計算叢集。
(3) 可靠性:資料在不同機器上都有備份,有效防止資料丟失。
(4) 可用性:備份的資料使得我們能夠快速訪問。

3. 分散式的優缺點

       分散式系統有很多的優點:增加了儲存、計算能力以及連線空間分離位置的可能性。但同時也具有缺點:一致性問題,此問題在分散式系統中經常發生,一臺機器可能在幾年內發生一次故障,對於一個有著數百萬節點的分散式系統來說,可能每分鐘就會發生一次故障。

二、 容錯性&Paxos

       物理上我們不能改變分散式系統會頻繁發生故障的事實,但可以希望系統可以容忍一些故障並繼續工作。那麼如何建立具有容錯性(fault-tolerance)的分散式系統呢?

1. 簡單的客戶端-伺服器演算法
Algorithm 1. Naive Client-Server Algorithm
1: Client sends commands one at a time to server

       一個分散式系統由很多節點組成,每個節點可以執行本地計算,還可以傳送訊息給其他的節點(訊息傳遞,message passing)。

       演算法1則是實現了客戶端與伺服器之間進行訊息傳遞,但是它存在兩個問題:
      (1)訊息損壞(message corruption),即成功接收訊息但其內容被損壞,在訊息中增加附加資訊,如校驗和,就可以解決這個問題。
      (2)訊息丟失(message loss),即訊息未能成功抵達接收器。這樣演算法1則不能正確執行,於是需要對它改進。

2. 具有確認的客戶端-伺服器演算法
Algorithm 2. Client-Server Algorithm with Acknowledgments
1: Client sends commands one at a time to server
2: Server acknowledges every command
3: If the client does not receive an acknowledgment within a reasonable time, the client resends the command

       演算法2實現了只有收到上一條命令的確認後才會傳送下一條(Line 1),但確認也會丟失,於是客戶端會重新發送命令(Line 3)。此演算法是很多可靠協議的基礎,如TCP。
       該演算法可以很容易地擴充套件到多個伺服器:客戶端將命令傳送到所有的伺服器,一旦客戶端收到來自所有伺服器的確認,該命令即被認為是成功執行的。
       但是對於多個客戶端的情況呢?這時就會出現“可變訊息延遲(variable message delay)”的問題,即儘管在兩個相同節點傳輸,也可能會有不同的傳輸時間,導致伺服器以不同的順序執行命令,從而會出現狀態不一致。舉例說明,假如演算法2應用於有2個客戶端s1和s2,2個伺服器u1和u2的系統,兩個客戶端都發布更新伺服器上變數x的命令,初始時x=0。客戶端u1傳送命令x=x+1,u2傳送命令x=2*x,由於有可變訊息延遲,可能存在此種情況:s1先接收到u1的訊息,s2先接收到u2的訊息,因此s1計算x = (0 + 1) * 2 = 2,s2計算x = 2 * 0 + 1 = 1。這樣就導致狀態不一致的問題。

3. 狀態複製

       為了解決可變訊息延遲導致的狀態不一致問題,提出了狀態複製(state replication)的方法:如果所有節點以相同順序執行命令c1, c2, c3,…,那這一組節點就實現了狀態複製。有兩種演算法可以實現狀態複製:
(1) 使用序列器進行狀態複製

Algorithm 3. State Replication with a Serializer
1: Clients send commands one at a time to the serializer
2: Serializer forwards commands one at a time to all other servers
3: Once the serializer received all acknowledgments, it notifies the client about the success

       由於單個伺服器的狀態複製很簡單,因此可以將某個伺服器指定為序列器(serializer),通過它分發命令,就可以自動實現狀態複製。演算法3則是使用序列器進行狀態複製。
       序列器的作用是轉發,但是如果序列器出現故障怎麼辦?由於序列器聯絡著整個系統,一旦故障,整個系統就陷入癱瘓!那麼有沒有一個更分佈化的方式來實現狀態複製呢?
(2) 兩階段協議

Algorithm 4. Two-Phase Protocol
    Phase 1
1: Client asks all servers for the lock
    Phase 2
2: if client receives lock from every server then
3:    Client sends command reliably to each server, and gives the lock back
4: else
5:    Clients gives the received locks back
6:    Client waits, and then starts with Phase 1 again
7: end if

       演算法4使用互斥鎖實現狀態複製,但是次演算法並沒有解決節點故障問題,事實上比使用序列器的演算法3還要糟糕,因為演算法3只要求序列器這一個節點響應,而演算法4要求所有節點都響應!
       假如將演算法的Phase 1修改為嘗試獲取大多數鎖,是否可行?這時就會有一些問題:如果2個或更多的客戶端同時嘗試獲取大多數鎖,這時就需要其中1個或幾個客戶端放棄他們已經獲得的所有鎖,以防死鎖。但是如果在這些客戶端釋放鎖之前出現故障,這樣系統就會進入死鎖狀態。那麼為了解決這些問題,我們是否需要一個不同的概念呢?

4. 簡單的票證協議

票(ticket)的定義:票是比鎖的更弱的一種形式,它有以下的特點:
(1) 可重新發行:即使之前的票未被返還,伺服器可發行票;
(2) 過期票:伺服器只接受最新發行的票t。

Algorithm 5. Naive Ticket Protocol
     Phase 1
 1: Client asks all servers for a ticket
     Phase 2
 2: if a majority of the servers replied then
 3:     Client sends command together with ticket to each server
 4:     Server stores command only if ticket is still valid, and replies to client
 5: else
 6:    Client waits, and then starts with Phase 1 again
 7: end if
     Phase 3
 8: if client hears a positive answer from a majority of the servers then
 9:     Client tells servers to execute the stored command
10: else
11:    Client waits, and then starts with Phase 1 again
12: end if

       此演算法存在這樣的問題:假如有2個客戶端u1和u2,3個伺服器s1, s2和s3,①u1已經成功在s1, s2和s3上儲存命令c1;②u2在u1的第三階段之前傳送命令c2給s2和s3,併成功儲存;③u1和u2都通知伺服器執行命令。這時一些伺服器執行c1一些執行c2,就造成了不一致的狀態。

5. Paxos
Algorithm 6. Paxos
      Client (Proposer)                                                 Server (Acceptor)
      Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
      c          ~ command to execute                              Tmax = 0     ~ largest issued ticket
      t = 0     ~ ticket number to try
                                                                                    C = ⊥          ~ stored command
                                                                                    Tstore = 0     ~ ticket used to store C
      Phase 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  1: t = t + 1
  2: Ask all servers for ticket t
                                                                                    3: if t > Tmax then
                                                                                    4:     Tmax = t
                                                                                    5:     Answer with ok(Tstore, C)
                                                                                    6: end if
      Phase 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  7: if a majority answers ok then
  8:     Pick (Tstore, C) with largest Tstore
  9:     if Tstore > 0 then
10:         c = C
11:     end if
12:     Send propose(t, c) to same majority
13: end if
                                                                                    14: if t = Tmax then
                                                                                    15:     C = c
                                                                                    16:     Tstore = t
                                                                                    17:     Answer success
                                                                                    18: end if
      Phase 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19: if a majority answers success then
20:     Send execute(c) to every server
21: end if

       Paxos演算法改進了票證協議演算法,此演算法中伺服器在第一階段不再只是分發票證,還會通知客戶端它目前儲存的命令。如果u2已經得知u1已經成功儲存了c1,就不再試圖儲存c2,而是支援u1傳送儲存c1的命令,這樣客戶端都執行相同的命令,伺服器收到命令的順序就不再會導致不一致問題。
       不過Paxos要求伺服器崩潰數目少於一半,如果有一半(或更多)的伺服器崩潰,Paxos將無法得到進展,由於客戶端無法達到大多數。
       最後,我們就得到了一個相對較好的演算法 - Paxos,即使系統中有少數節點崩潰,也能實現狀態複製,達成一致狀態。