mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
update zh-tw.py to handle the translation issues with 髮 and 發
This commit is contained in:
parent
14f7fce7a7
commit
7bce759c44
@ -4,7 +4,13 @@ import os, sys, opencc
|
|||||||
def convert(src_path, dst_path, cfg='s2twp.json'):
|
def convert(src_path, dst_path, cfg='s2twp.json'):
|
||||||
converter = opencc.OpenCC(cfg)
|
converter = opencc.OpenCC(cfg)
|
||||||
with open(src_path, "r", encoding='utf-8') as src, open(dst_path, "w+", encoding='utf-8') as dst:
|
with open(src_path, "r", encoding='utf-8') as src, open(dst_path, "w+", encoding='utf-8') as dst:
|
||||||
dst.write("\n".join(converter.convert(line.rstrip()).replace('(img/', '(../img/') for line in src))
|
dst.write("\n".join(
|
||||||
|
converter.convert(line.rstrip()).replace('(img/', '(../img/')
|
||||||
|
.replace('髮送', '傳送')
|
||||||
|
.replace('髮布', '釋出')
|
||||||
|
.replace('髮生', '發生')
|
||||||
|
.replace('髮出', '發出')
|
||||||
|
for line in src))
|
||||||
print("convert %s to %s" % (src_path, dst_path))
|
print("convert %s to %s" % (src_path, dst_path))
|
||||||
|
|
||||||
|
|
||||||
|
2
ch5.md
2
ch5.md
@ -265,7 +265,7 @@ PostgreSQL和Oracle等使用这种复制方法【16】。主要缺点是日志
|
|||||||
> *Mr. Poons*
|
> *Mr. Poons*
|
||||||
> Mrs. Cake,你能看到多远的未来?
|
> Mrs. Cake,你能看到多远的未来?
|
||||||
|
|
||||||
对于观察者来说,看起来好像Cake夫人在Poons先生发问前就回答了这个问题。
|
对于观察者来说,看起来好像Cake夫人在Poons先生提问前就回答了这个问题。
|
||||||
这种超能力让人印象深刻,但也会把人搞糊涂。【25】。
|
这种超能力让人印象深刻,但也会把人搞糊涂。【25】。
|
||||||
|
|
||||||
![](img/fig5-5.png)
|
![](img/fig5-5.png)
|
||||||
|
@ -154,7 +154,7 @@
|
|||||||
|
|
||||||
***釋出推文***
|
***釋出推文***
|
||||||
|
|
||||||
使用者可以向其粉絲髮布新訊息(平均 4.6k請求/秒,峰值超過 12k請求/秒)。
|
使用者可以向其粉絲釋出新訊息(平均 4.6k請求/秒,峰值超過 12k請求/秒)。
|
||||||
|
|
||||||
***主頁時間線***
|
***主頁時間線***
|
||||||
|
|
||||||
@ -187,7 +187,7 @@
|
|||||||
|
|
||||||
推特的第一個版本使用了方法1,但系統很難跟上主頁時間線查詢的負載。所以公司轉向了方法2,方法2的效果更好,因為發推頻率比查詢主頁時間線的頻率幾乎低了兩個數量級,所以在這種情況下,最好在寫入時做更多的工作,而在讀取時做更少的工作。
|
推特的第一個版本使用了方法1,但系統很難跟上主頁時間線查詢的負載。所以公司轉向了方法2,方法2的效果更好,因為發推頻率比查詢主頁時間線的頻率幾乎低了兩個數量級,所以在這種情況下,最好在寫入時做更多的工作,而在讀取時做更少的工作。
|
||||||
|
|
||||||
然而方法2的缺點是,發推現在需要大量的額外工作。平均來說,一條推文會發往約75個關注者,所以每秒4.6k的發推寫入,變成了對主頁時間線快取每秒345k的寫入。但這個平均值隱藏了使用者粉絲數差異巨大這一現實,一些使用者有超過3000萬的粉絲,這意味著一條推文就可能會導致主頁時間線快取的3000萬次寫入!及時完成這種操作是一個巨大的挑戰 —— 推特嘗試在5秒內向粉絲髮送推文。
|
然而方法2的缺點是,發推現在需要大量的額外工作。平均來說,一條推文會發往約75個關注者,所以每秒4.6k的發推寫入,變成了對主頁時間線快取每秒345k的寫入。但這個平均值隱藏了使用者粉絲數差異巨大這一現實,一些使用者有超過3000萬的粉絲,這意味著一條推文就可能會導致主頁時間線快取的3000萬次寫入!及時完成這種操作是一個巨大的挑戰 —— 推特嘗試在5秒內向粉絲傳送推文。
|
||||||
|
|
||||||
在推特的例子中,每個使用者粉絲數的分佈(可能按這些使用者的發推頻率來加權)是探討可伸縮性的一個關鍵負載引數,因為它決定了扇出負載。你的應用程式可能具有非常不同的特徵,但可以採用相似的原則來考慮它的負載。
|
在推特的例子中,每個使用者粉絲數的分佈(可能按這些使用者的發推頻率來加權)是探討可伸縮性的一個關鍵負載引數,因為它決定了扇出負載。你的應用程式可能具有非常不同的特徵,但可以採用相似的原則來考慮它的負載。
|
||||||
|
|
||||||
@ -234,7 +234,7 @@
|
|||||||
|
|
||||||
**排隊延遲(queueing delay)** 通常佔了高百分位點處響應時間的很大一部分。由於伺服器只能並行處理少量的事務(如受其CPU核數的限制),所以只要有少量緩慢的請求就能阻礙後續請求的處理,這種效應有時被稱為 **頭部阻塞(head-of-line blocking)** 。即使後續請求在伺服器上處理的非常迅速,由於需要等待先前請求完成,客戶端最終看到的是緩慢的總體響應時間。因為存在這種效應,測量客戶端的響應時間非常重要。
|
**排隊延遲(queueing delay)** 通常佔了高百分位點處響應時間的很大一部分。由於伺服器只能並行處理少量的事務(如受其CPU核數的限制),所以只要有少量緩慢的請求就能阻礙後續請求的處理,這種效應有時被稱為 **頭部阻塞(head-of-line blocking)** 。即使後續請求在伺服器上處理的非常迅速,由於需要等待先前請求完成,客戶端最終看到的是緩慢的總體響應時間。因為存在這種效應,測量客戶端的響應時間非常重要。
|
||||||
|
|
||||||
為測試系統的可伸縮性而人為產生負載時,產生負載的客戶端要獨立於響應時間不斷髮送請求。如果客戶端在傳送下一個請求之前等待先前的請求完成,這種行為會產生人為排隊的效果,使得測試時的佇列比現實情況更短,使測量結果產生偏差【23】。
|
為測試系統的可伸縮性而人為產生負載時,產生負載的客戶端要獨立於響應時間不斷傳送請求。如果客戶端在傳送下一個請求之前等待先前的請求完成,這種行為會產生人為排隊的效果,使得測試時的佇列比現實情況更短,使測量結果產生偏差【23】。
|
||||||
|
|
||||||
> #### 實踐中的百分位點
|
> #### 實踐中的百分位點
|
||||||
>
|
>
|
||||||
|
@ -618,7 +618,7 @@ Spark,Flink和Tez避免將中間狀態寫入HDFS,因此它們採取了不同
|
|||||||
|
|
||||||
回想一下在MapReduce中,Mapper在概念上向Reducer的特定呼叫“傳送訊息”,因為框架將所有具有相同鍵的Mapper輸出集中在一起。 Pregel背後有一個類似的想法:一個頂點可以向另一個頂點“傳送訊息”,通常這些訊息是沿著圖的邊傳送的。
|
回想一下在MapReduce中,Mapper在概念上向Reducer的特定呼叫“傳送訊息”,因為框架將所有具有相同鍵的Mapper輸出集中在一起。 Pregel背後有一個類似的想法:一個頂點可以向另一個頂點“傳送訊息”,通常這些訊息是沿著圖的邊傳送的。
|
||||||
|
|
||||||
在每次迭代中,為每個頂點呼叫一個函式,將所有傳送給它的訊息傳遞給它 —— 就像呼叫Reducer一樣。與MapReduce的不同之處在於,在Pregel模型中,頂點在一次迭代到下一次迭代的過程中會記住它的狀態,所以這個函式只需要處理新的傳入訊息。如果圖的某個部分沒有被髮送訊息,那裡就不需要做任何工作。
|
在每次迭代中,為每個頂點呼叫一個函式,將所有傳送給它的訊息傳遞給它 —— 就像呼叫Reducer一樣。與MapReduce的不同之處在於,在Pregel模型中,頂點在一次迭代到下一次迭代的過程中會記住它的狀態,所以這個函式只需要處理新的傳入訊息。如果圖的某個部分沒有被傳送訊息,那裡就不需要做任何工作。
|
||||||
|
|
||||||
這與Actor模型有些相似(請參閱“[分散式的Actor框架](ch4.md#分散式的Actor框架)”),除了頂點狀態和頂點之間的訊息具有容錯性和永續性,且通訊以固定的回合進行:在每次迭代中,框架遞送上次迭代中傳送的所有訊息。Actor通常沒有這樣的時序保證。
|
這與Actor模型有些相似(請參閱“[分散式的Actor框架](ch4.md#分散式的Actor框架)”),除了頂點狀態和頂點之間的訊息具有容錯性和永續性,且通訊以固定的回合進行:在每次迭代中,框架遞送上次迭代中傳送的所有訊息。Actor通常沒有這樣的時序保證。
|
||||||
|
|
||||||
|
@ -643,11 +643,11 @@ GROUP BY follows.follower_id
|
|||||||
|
|
||||||
Apache Flink則使用不同的方法,它會定期生成狀態的滾動存檔點並將其寫入持久儲存【92,93】。如果流運算元崩潰,它可以從最近的存檔點重啟,並丟棄從最近檢查點到崩潰之間的所有輸出。存檔點會由訊息流中的**壁障(barrier)** 觸發,類似於微批次之間的邊界,但不會強制一個特定的視窗大小。
|
Apache Flink則使用不同的方法,它會定期生成狀態的滾動存檔點並將其寫入持久儲存【92,93】。如果流運算元崩潰,它可以從最近的存檔點重啟,並丟棄從最近檢查點到崩潰之間的所有輸出。存檔點會由訊息流中的**壁障(barrier)** 觸發,類似於微批次之間的邊界,但不會強制一個特定的視窗大小。
|
||||||
|
|
||||||
在流處理框架的範圍內,微批次與存檔點方法提供了與批處理一樣的**恰好一次語義**。但是,只要輸出離開流處理器(例如,寫入資料庫,向外部訊息代理髮送訊息,或傳送電子郵件),框架就無法拋棄失敗批次的輸出了。在這種情況下,重啟失敗任務會導致外部副作用發生兩次,只有微批次或存檔點不足以阻止這一問題。
|
在流處理框架的範圍內,微批次與存檔點方法提供了與批處理一樣的**恰好一次語義**。但是,只要輸出離開流處理器(例如,寫入資料庫,向外部訊息代理傳送訊息,或傳送電子郵件),框架就無法拋棄失敗批次的輸出了。在這種情況下,重啟失敗任務會導致外部副作用發生兩次,只有微批次或存檔點不足以阻止這一問題。
|
||||||
|
|
||||||
#### 原子提交再現
|
#### 原子提交再現
|
||||||
|
|
||||||
為了在出現故障時表現出恰好處理一次的樣子,我們需要確保事件處理的所有輸出和副作用**當且僅當**處理成功時才會生效。這些影響包括髮送給下游運算元或外部訊息傳遞系統(包括電子郵件或推送通知)的任何訊息,任何資料庫寫入,對運算元狀態的任何變更,以及對輸入訊息的任何確認(包括在基於日誌的訊息代理中將消費者偏移量前移)。
|
為了在出現故障時表現出恰好處理一次的樣子,我們需要確保事件處理的所有輸出和副作用**當且僅當**處理成功時才會生效。這些影響包括傳送給下游運算元或外部訊息傳遞系統(包括電子郵件或推送通知)的任何訊息,任何資料庫寫入,對運算元狀態的任何變更,以及對輸入訊息的任何確認(包括在基於日誌的訊息代理中將消費者偏移量前移)。
|
||||||
|
|
||||||
這些事情要麼都原子地發生,要麼都不發生,但是它們不應當失去同步。如果這種方法聽起來很熟悉,那是因為我們在分散式事務和兩階段提交的上下文中討論過它(請參閱“[恰好一次的訊息處理](ch9.md#恰好一次的訊息處理)”)。
|
這些事情要麼都原子地發生,要麼都不發生,但是它們不應當失去同步。如果這種方法聽起來很熟悉,那是因為我們在分散式事務和兩階段提交的上下文中討論過它(請參閱“[恰好一次的訊息處理](ch9.md#恰好一次的訊息處理)”)。
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ COMMIT;
|
|||||||
但事實證明,使用分割槽日誌可以達到等價的正確性而無需原子提交:
|
但事實證明,使用分割槽日誌可以達到等價的正確性而無需原子提交:
|
||||||
|
|
||||||
1. 從賬戶A向賬戶B轉賬的請求由客戶端提供一個唯一的請求ID,並按請求ID追加寫入相應日誌分割槽。
|
1. 從賬戶A向賬戶B轉賬的請求由客戶端提供一個唯一的請求ID,並按請求ID追加寫入相應日誌分割槽。
|
||||||
2. 流處理器讀取請求日誌。對於每個請求訊息,它向輸出流發出兩條訊息:付款人賬戶A的借記指令(按A分割槽),收款人B的貸記指令(按B分割槽)。被髮出的訊息中會帶有原始的請求ID。
|
2. 流處理器讀取請求日誌。對於每個請求訊息,它向輸出流發出兩條訊息:付款人賬戶A的借記指令(按A分割槽),收款人B的貸記指令(按B分割槽)。被發出的訊息中會帶有原始的請求ID。
|
||||||
3. 後續處理器消費借記/貸記指令流,按照請求ID除重,並將變更應用至賬戶餘額。
|
3. 後續處理器消費借記/貸記指令流,按照請求ID除重,並將變更應用至賬戶餘額。
|
||||||
|
|
||||||
步驟1和步驟2是必要的,因為如果客戶直接傳送貸記與借記指令,則需要在這兩個分割槽之間進行原子提交,以確保兩者要麼都發生或都不發生。為了避免對分散式事務的需要,我們首先將請求持久化記錄為單條訊息,然後從這第一條訊息中衍生出貸記指令與借記指令。幾乎在所有資料系統中,單物件寫入都是原子性的(請參閱“[單物件寫入](ch7.md#單物件寫入)),因此請求要麼出現在日誌中,要麼就不出現,無需多分割槽原子提交。
|
步驟1和步驟2是必要的,因為如果客戶直接傳送貸記與借記指令,則需要在這兩個分割槽之間進行原子提交,以確保兩者要麼都發生或都不發生。為了避免對分散式事務的需要,我們首先將請求持久化記錄為單條訊息,然後從這第一條訊息中衍生出貸記指令與借記指令。幾乎在所有資料系統中,單物件寫入都是原子性的(請參閱“[單物件寫入](ch7.md#單物件寫入)),因此請求要麼出現在日誌中,要麼就不出現,無需多分割槽原子提交。
|
||||||
|
@ -265,7 +265,7 @@ PostgreSQL和Oracle等使用這種複製方法【16】。主要缺點是日誌
|
|||||||
> *Mr. Poons*
|
> *Mr. Poons*
|
||||||
> Mrs. Cake,你能看到多遠的未來?
|
> Mrs. Cake,你能看到多遠的未來?
|
||||||
|
|
||||||
對於觀察者來說,看起來好像Cake夫人在Poons先生髮問前就回答了這個問題。
|
對於觀察者來說,看起來好像Cake夫人在Poons先生提問前就回答了這個問題。
|
||||||
這種超能力讓人印象深刻,但也會把人搞糊塗。【25】。
|
這種超能力讓人印象深刻,但也會把人搞糊塗。【25】。
|
||||||
|
|
||||||
![](../img/fig5-5.png)
|
![](../img/fig5-5.png)
|
||||||
@ -538,7 +538,7 @@ PostgreSQL和Oracle等使用這種複製方法【16】。主要缺點是日誌
|
|||||||
|
|
||||||
通常,r和w被選為多數(超過 $n/2$ )節點,因為這確保了$w + r> n$,同時仍然容忍多達$n/2$個節點故障。但是,法定人數不一定必須是大多數,只是讀寫使用的節點交集至少需要包括一個節點。其他法定人數的配置是可能的,這使得分散式演算法的設計有一定的靈活性【45】。
|
通常,r和w被選為多數(超過 $n/2$ )節點,因為這確保了$w + r> n$,同時仍然容忍多達$n/2$個節點故障。但是,法定人數不一定必須是大多數,只是讀寫使用的節點交集至少需要包括一個節點。其他法定人數的配置是可能的,這使得分散式演算法的設計有一定的靈活性【45】。
|
||||||
|
|
||||||
你也可以將w和r設定為較小的數字,以使$w + r≤n$(即法定條件不滿足)。在這種情況下,讀取和寫入操作仍將被髮送到n個節點,但操作成功只需要少量的成功響應。
|
你也可以將w和r設定為較小的數字,以使$w + r≤n$(即法定條件不滿足)。在這種情況下,讀取和寫入操作仍將被傳送到n個節點,但操作成功只需要少量的成功響應。
|
||||||
|
|
||||||
較小的w和r更有可能會讀取過時的資料,因為你的讀取更有可能不包含具有最新值的節點。另一方面,這種配置允許更低的延遲和更高的可用性:如果存在網路中斷,並且許多副本變得無法訪問,則可以繼續處理讀取和寫入的機會更大。只有當可達副本的數量低於w或r時,資料庫才分別變得不可用於寫入或讀取。
|
較小的w和r更有可能會讀取過時的資料,因為你的讀取更有可能不包含具有最新值的節點。另一方面,這種配置允許更低的延遲和更高的可用性:如果存在網路中斷,並且許多副本變得無法訪問,則可以繼續處理讀取和寫入的機會更大。只有當可達副本的數量低於w或r時,資料庫才分別變得不可用於寫入或讀取。
|
||||||
|
|
||||||
@ -578,7 +578,7 @@ PostgreSQL和Oracle等使用這種複製方法【16】。主要缺點是日誌
|
|||||||
|
|
||||||
後者被認為是一個**寬鬆的法定人數(sloppy quorum)**【37】:寫和讀仍然需要w和r成功的響應,但這些響應可能來自不在指定的n個“主”節點中的其它節點。比方說,如果你把自己鎖在房子外面,你可能會敲開鄰居的門,問你是否可以暫時呆在沙發上。
|
後者被認為是一個**寬鬆的法定人數(sloppy quorum)**【37】:寫和讀仍然需要w和r成功的響應,但這些響應可能來自不在指定的n個“主”節點中的其它節點。比方說,如果你把自己鎖在房子外面,你可能會敲開鄰居的門,問你是否可以暫時呆在沙發上。
|
||||||
|
|
||||||
一旦網路中斷得到解決,代表另一個節點臨時接受的一個節點的任何寫入都被髮送到適當的“主”節點。這就是所謂的**提示移交(hinted handoff)**。 (一旦你再次找到你的房子的鑰匙,你的鄰居禮貌地要求你離開沙發回家。)
|
一旦網路中斷得到解決,代表另一個節點臨時接受的一個節點的任何寫入都被傳送到適當的“主”節點。這就是所謂的**提示移交(hinted handoff)**。 (一旦你再次找到你的房子的鑰匙,你的鄰居禮貌地要求你離開沙發回家。)
|
||||||
|
|
||||||
寬鬆的法定人數對寫入可用性的提高特別有用:只要有任何w節點可用,資料庫就可以接受寫入。然而,這意味著即使當$w + r> n$時,也不能確定讀取某個鍵的最新值,因為最新的值可能已經臨時寫入了n之外的某些節點【47】。
|
寬鬆的法定人數對寫入可用性的提高特別有用:只要有任何w節點可用,資料庫就可以接受寫入。然而,這意味著即使當$w + r> n$時,也不能確定讀取某個鍵的最新值,因為最新的值可能已經臨時寫入了n之外的某些節點【47】。
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ Cassandra和Ketama使用的第三種方法是使分割槽數與節點數成正
|
|||||||
|
|
||||||
**圖6-7 將請求路由到正確節點的三種不同方式。**
|
**圖6-7 將請求路由到正確節點的三種不同方式。**
|
||||||
|
|
||||||
這是一個具有挑戰性的問題,因為重要的是所有參與者都達成共識 - 否則請求將被髮送到錯誤的節點,得不到正確的處理。 在分散式系統中有達成共識的協議,但很難正確地實現(見[第九章](ch9.md))。
|
這是一個具有挑戰性的問題,因為重要的是所有參與者都達成共識 - 否則請求將被傳送到錯誤的節點,得不到正確的處理。 在分散式系統中有達成共識的協議,但很難正確地實現(見[第九章](ch9.md))。
|
||||||
|
|
||||||
許多分散式資料系統都依賴於一個獨立的協調服務,比如ZooKeeper來跟蹤叢集元資料,如[圖6-8](../img/fig6-8.png)所示。 每個節點在ZooKeeper中註冊自己,ZooKeeper維護分割槽到節點的可靠對映。 其他參與者(如路由層或分割槽感知客戶端)可以在ZooKeeper中訂閱此資訊。 只要分割槽分配發生了改變,或者叢集中新增或刪除了一個節點,ZooKeeper就會通知路由層使路由資訊保持最新狀態。
|
許多分散式資料系統都依賴於一個獨立的協調服務,比如ZooKeeper來跟蹤叢集元資料,如[圖6-8](../img/fig6-8.png)所示。 每個節點在ZooKeeper中註冊自己,ZooKeeper維護分割槽到節點的可靠對映。 其他參與者(如路由層或分割槽感知客戶端)可以在ZooKeeper中訂閱此資訊。 只要分割槽分配發生了改變,或者叢集中新增或刪除了一個節點,ZooKeeper就會通知路由層使路由資訊保持最新狀態。
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
|||||||
|
|
||||||
多物件事務需要某種方式來確定哪些讀寫操作屬於同一個事務。在關係型資料庫中,通常基於客戶端與資料庫伺服器的TCP連線:在任何特定連線上,`BEGIN TRANSACTION` 和 `COMMIT` 語句之間的所有內容,被認為是同一事務的一部分.[^iii]
|
多物件事務需要某種方式來確定哪些讀寫操作屬於同一個事務。在關係型資料庫中,通常基於客戶端與資料庫伺服器的TCP連線:在任何特定連線上,`BEGIN TRANSACTION` 和 `COMMIT` 語句之間的所有內容,被認為是同一事務的一部分.[^iii]
|
||||||
|
|
||||||
[^iii]: 這並不完美。如果TCP連線中斷,則事務必須中止。如果中斷髮生在客戶端請求提交之後,但在伺服器確認提交發生之前,客戶端並不知道事務是否已提交。為了解決這個問題,事務管理器可以透過一個唯一事務識別符號來對操作進行分組,這個識別符號並未繫結到特定TCP連線。後續再“[資料庫的端到端原則](ch12.md#資料庫的端到端原則)”一節將回到這個主題。
|
[^iii]: 這並不完美。如果TCP連線中斷,則事務必須中止。如果中斷發生在客戶端請求提交之後,但在伺服器確認提交發生之前,客戶端並不知道事務是否已提交。為了解決這個問題,事務管理器可以透過一個唯一事務識別符號來對操作進行分組,這個識別符號並未繫結到特定TCP連線。後續再“[資料庫的端到端原則](ch12.md#資料庫的端到端原則)”一節將回到這個主題。
|
||||||
|
|
||||||
另一方面,許多非關係資料庫並沒有將這些操作組合在一起的方法。即使存在多物件API(例如,某鍵值儲存可能具有在一個操作中更新幾個鍵的multi-put操作),但這並不一定意味著它具有事務語義:該命令可能在一些鍵上成功,在其他的鍵上失敗,使資料庫處於部分更新的狀態。
|
另一方面,許多非關係資料庫並沒有將這些操作組合在一起的方法。即使存在多物件API(例如,某鍵值儲存可能具有在一個操作中更新幾個鍵的multi-put操作),但這並不一定意味著它具有事務語義:該命令可能在一些鍵上成功,在其他的鍵上失敗,使資料庫處於部分更新的狀態。
|
||||||
|
|
||||||
|
10
zh-tw/ch8.md
10
zh-tw/ch8.md
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
最後,作為工程師,我們的任務是構建能夠完成工作的系統(即滿足使用者期望的保證),儘管一切都出錯了。 在[第九章](ch9.md)中,我們將看看一些可以在分散式系統中提供這種保證的演算法的例子。 但首先,在本章中,我們必須瞭解我們面臨的挑戰。
|
最後,作為工程師,我們的任務是構建能夠完成工作的系統(即滿足使用者期望的保證),儘管一切都出錯了。 在[第九章](ch9.md)中,我們將看看一些可以在分散式系統中提供這種保證的演算法的例子。 但首先,在本章中,我們必須瞭解我們面臨的挑戰。
|
||||||
|
|
||||||
本章對分散式系統中可能出現的問題進行徹底的悲觀和沮喪的總結。 我們將研究網路的問題(“[不可靠的網路](#不可靠的網路)”); 時鐘和時序問題(“[不可靠的時鐘](#不可靠的時鐘)”); 我們將討論他們可以避免的程度。 所有這些問題的後果都是困惑的,所以我們將探索如何思考一個分散式系統的狀態,以及如何推理髮生的事情(“[知識、真相與謊言](#知識、真相與謊言)”)。
|
本章對分散式系統中可能出現的問題進行徹底的悲觀和沮喪的總結。 我們將研究網路的問題(“[不可靠的網路](#不可靠的網路)”); 時鐘和時序問題(“[不可靠的時鐘](#不可靠的時鐘)”); 我們將討論他們可以避免的程度。 所有這些問題的後果都是困惑的,所以我們將探索如何思考一個分散式系統的狀態,以及如何推理發生的事情(“[知識、真相與謊言](#知識、真相與謊言)”)。
|
||||||
|
|
||||||
|
|
||||||
## 故障與部分失效
|
## 故障與部分失效
|
||||||
@ -82,7 +82,7 @@
|
|||||||
> 你可能想知道這是否有意義——直觀地看來,系統只能像其最不可靠的元件(最薄弱的環節)一樣可靠。事實並非如此:事實上,從不太可靠的潛在基礎構建更可靠的系統是計算機領域的一個古老思想【11】。例如:
|
> 你可能想知道這是否有意義——直觀地看來,系統只能像其最不可靠的元件(最薄弱的環節)一樣可靠。事實並非如此:事實上,從不太可靠的潛在基礎構建更可靠的系統是計算機領域的一個古老思想【11】。例如:
|
||||||
>
|
>
|
||||||
> * 糾錯碼允許數字資料在通訊通道上準確傳輸,偶爾會出現一些錯誤,例如由於無線網路上的無線電干擾【12】。
|
> * 糾錯碼允許數字資料在通訊通道上準確傳輸,偶爾會出現一些錯誤,例如由於無線網路上的無線電干擾【12】。
|
||||||
> * **網際網路協議(Internet Protocol, IP)** 不可靠:可能丟棄,延遲,重複或重排資料包。 傳輸控制協議(Transmission Control Protocol, TCP)在網際網路協議(IP)之上提供了更可靠的傳輸層:它確保丟失的資料包被重新傳輸,消除重複,並且資料包被重新組裝成它們被髮送的順序。
|
> * **網際網路協議(Internet Protocol, IP)** 不可靠:可能丟棄,延遲,重複或重排資料包。 傳輸控制協議(Transmission Control Protocol, TCP)在網際網路協議(IP)之上提供了更可靠的傳輸層:它確保丟失的資料包被重新傳輸,消除重複,並且資料包被重新組裝成它們被傳送的順序。
|
||||||
>
|
>
|
||||||
> 雖然這個系統可以比它的底層部分更可靠,但它的可靠性總是有限的。例如,糾錯碼可以處理少量的單位元錯誤,但是如果你的訊號被幹擾所淹沒,那麼透過通道可以得到多少資料,是有根本性的限制的【13】。 TCP可以隱藏資料包的丟失,重複和重新排序,但是它不能神奇地消除網路中的延遲。
|
> 雖然這個系統可以比它的底層部分更可靠,但它的可靠性總是有限的。例如,糾錯碼可以處理少量的單位元錯誤,但是如果你的訊號被幹擾所淹沒,那麼透過通道可以得到多少資料,是有根本性的限制的【13】。 TCP可以隱藏資料包的丟失,重複和重新排序,但是它不能神奇地消除網路中的延遲。
|
||||||
>
|
>
|
||||||
@ -108,7 +108,7 @@
|
|||||||
|
|
||||||
**圖8-1 如果傳送請求並沒有得到響應,則無法區分(a)請求是否丟失,(b)遠端節點是否關閉,或(c)響應是否丟失。**
|
**圖8-1 如果傳送請求並沒有得到響應,則無法區分(a)請求是否丟失,(b)遠端節點是否關閉,或(c)響應是否丟失。**
|
||||||
|
|
||||||
傳送者甚至不能分辨資料包是否被髮送:唯一的選擇是讓接收者傳送響應訊息,這可能會丟失或延遲。這些問題在非同步網路中難以區分:你所擁有的唯一資訊是,你尚未收到響應。如果你向另一個節點發送請求並且沒有收到響應,則不可能判斷是什麼原因。
|
傳送者甚至不能分辨資料包是否被傳送:唯一的選擇是讓接收者傳送響應訊息,這可能會丟失或延遲。這些問題在非同步網路中難以區分:你所擁有的唯一資訊是,你尚未收到響應。如果你向另一個節點發送請求並且沒有收到響應,則不可能判斷是什麼原因。
|
||||||
|
|
||||||
處理這個問題的通常方法是**超時(Timeout)**:在一段時間之後放棄等待,並且認為響應不會到達。但是,當發生超時時,你仍然不知道遠端節點是否收到了請求(如果請求仍然在某個地方排隊,那麼即使傳送者已經放棄了該請求,仍然可能會將其傳送給接收者)。
|
處理這個問題的通常方法是**超時(Timeout)**:在一段時間之後放棄等待,並且認為響應不會到達。但是,當發生超時時,你仍然不知道遠端節點是否收到了請求(如果請求仍然在某個地方排隊,那麼即使傳送者已經放棄了該請求,仍然可能會將其傳送給接收者)。
|
||||||
|
|
||||||
@ -458,7 +458,7 @@ while (true) {
|
|||||||
|
|
||||||
在一個稍微不那麼夢魘的場景中,半斷開的節點可能會注意到它傳送的訊息沒有被其他節點確認,因此意識到網路中必定存在故障。儘管如此,節點被其他節點錯誤地宣告為死亡,而半連線的節點對此無能為力。
|
在一個稍微不那麼夢魘的場景中,半斷開的節點可能會注意到它傳送的訊息沒有被其他節點確認,因此意識到網路中必定存在故障。儘管如此,節點被其他節點錯誤地宣告為死亡,而半連線的節點對此無能為力。
|
||||||
|
|
||||||
第三種情況,想象一個經歷了一個長時間**停止所有處理垃圾收集暫停(stop-the-world GC Pause)** 的節點。節點的所有執行緒被GC搶佔並暫停一分鐘,因此沒有請求被處理,也沒有響應被髮送。其他節點等待,重試,不耐煩,並最終宣佈節點死亡,並將其丟到靈車上。最後,GC完成,節點的執行緒繼續,好像什麼也沒有發生。其他節點感到驚訝,因為所謂的死亡節點突然從棺材中抬起頭來,身體健康,開始和旁觀者高興地聊天。GC後的節點最初甚至沒有意識到已經經過了整整一分鐘,而且自己已被宣告死亡。從它自己的角度來看,從最後一次與其他節點交談以來,幾乎沒有經過任何時間。
|
第三種情況,想象一個經歷了一個長時間**停止所有處理垃圾收集暫停(stop-the-world GC Pause)** 的節點。節點的所有執行緒被GC搶佔並暫停一分鐘,因此沒有請求被處理,也沒有響應被傳送。其他節點等待,重試,不耐煩,並最終宣佈節點死亡,並將其丟到靈車上。最後,GC完成,節點的執行緒繼續,好像什麼也沒有發生。其他節點感到驚訝,因為所謂的死亡節點突然從棺材中抬起頭來,身體健康,開始和旁觀者高興地聊天。GC後的節點最初甚至沒有意識到已經經過了整整一分鐘,而且自己已被宣告死亡。從它自己的角度來看,從最後一次與其他節點交談以來,幾乎沒有經過任何時間。
|
||||||
|
|
||||||
這些故事的寓意是,節點不一定能相信自己對於情況的判斷。分散式系統不能完全依賴單個節點,因為節點可能隨時失效,可能會使系統卡死,無法恢復。相反,許多分散式演算法都依賴於法定人數,即在節點之間進行投票(請參閱“[讀寫的法定人數](ch5.md#讀寫的法定人數)“):決策需要來自多個節點的最小投票數,以減少對於某個特定節點的依賴。
|
這些故事的寓意是,節點不一定能相信自己對於情況的判斷。分散式系統不能完全依賴單個節點,因為節點可能隨時失效,可能會使系統卡死,無法恢復。相反,許多分散式演算法都依賴於法定人數,即在節點之間進行投票(請參閱“[讀寫的法定人數](ch5.md#讀寫的法定人數)“):決策需要來自多個節點的最小投票數,以減少對於某個特定節點的依賴。
|
||||||
|
|
||||||
@ -523,7 +523,7 @@ while (true) {
|
|||||||
當一個系統在部分節點發生故障、不遵守協議、甚至惡意攻擊、擾亂網路時仍然能繼續正確工作,稱之為**拜占庭容錯(Byzantine fault-tolerant)** 的,在特定場景下,這種擔憂在是有意義的:
|
當一個系統在部分節點發生故障、不遵守協議、甚至惡意攻擊、擾亂網路時仍然能繼續正確工作,稱之為**拜占庭容錯(Byzantine fault-tolerant)** 的,在特定場景下,這種擔憂在是有意義的:
|
||||||
|
|
||||||
* 在航空航天環境中,計算機記憶體或CPU暫存器中的資料可能被輻射破壞,導致其以任意不可預知的方式響應其他節點。由於系統故障非常昂貴(例如,飛機撞毀和炸死船上所有人員,或火箭與國際空間站相撞),飛行控制系統必須容忍拜占庭故障【81,82】。
|
* 在航空航天環境中,計算機記憶體或CPU暫存器中的資料可能被輻射破壞,導致其以任意不可預知的方式響應其他節點。由於系統故障非常昂貴(例如,飛機撞毀和炸死船上所有人員,或火箭與國際空間站相撞),飛行控制系統必須容忍拜占庭故障【81,82】。
|
||||||
* 在多個參與組織的系統中,一些參與者可能會試圖欺騙或欺騙他人。在這種情況下,節點僅僅信任另一個節點的訊息是不安全的,因為它們可能是出於惡意的目的而被髮送的。例如,像比特幣和其他區塊鏈一樣的對等網路可以被認為是讓互不信任的各方同意交易是否發生的一種方式,而不依賴於中心機構(central authority)【83】。
|
* 在多個參與組織的系統中,一些參與者可能會試圖欺騙或欺騙他人。在這種情況下,節點僅僅信任另一個節點的訊息是不安全的,因為它們可能是出於惡意的目的而被傳送的。例如,像比特幣和其他區塊鏈一樣的對等網路可以被認為是讓互不信任的各方同意交易是否發生的一種方式,而不依賴於中心機構(central authority)【83】。
|
||||||
|
|
||||||
然而,在本書討論的那些系統中,我們通常可以安全地假設沒有拜占庭式的錯誤。在你的資料中心裡,所有的節點都是由你的組織控制的(所以他們可以信任),輻射水平足夠低,記憶體損壞不是一個大問題。製作拜占庭容錯系統的協議相當複雜【84】,而容錯嵌入式系統依賴於硬體層面的支援【81】。在大多數伺服器端資料系統中,部署拜占庭容錯解決方案的成本使其變得不切實際。
|
然而,在本書討論的那些系統中,我們通常可以安全地假設沒有拜占庭式的錯誤。在你的資料中心裡,所有的節點都是由你的組織控制的(所以他們可以信任),輻射水平足夠低,記憶體損壞不是一個大問題。製作拜占庭容錯系統的協議相當複雜【84】,而容錯嵌入式系統依賴於硬體層面的支援【81】。在大多數伺服器端資料系統中,部署拜占庭容錯解決方案的成本使其變得不切實際。
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user