update zh-tw content

This commit is contained in:
Gang Yin 2021-09-14 22:29:47 +08:00
parent 5c6f611c33
commit b99cb79a75
15 changed files with 58 additions and 53 deletions

View File

@ -152,11 +152,16 @@
| ISSUE & Pull Requests | USER | Title |
| ----------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [129](https://github.com/Vonng/ddia/pull/129) | [@anaer](https://github.com/anaer) | ch4: 修正兩處強調文字和四處程式碼變數名稱 |
| [128](https://github.com/Vonng/ddia/pull/128) | [@meilin96](https://github.com/meilin96) | ch5: 修正一處錯誤的引用 |
| [126](https://github.com/Vonng/ddia/pull/126) | [@cwr31](https://github.com/cwr31) | ch10: 修正一處錯誤的翻譯(功能 -> 函式) |
| [125](https://github.com/Vonng/ddia/pull/125) | [@dch1228](https://github.com/dch1228) | ch2: 最佳化 how best 的翻譯(如何以最佳方式) |
| [124](https://github.com/Vonng/ddia/pull/124) | [@yingang](https://github.com/yingang) | translation updates (chapter 10) |
| [123](https://github.com/Vonng/ddia/pull/123) | [@yingang](https://github.com/yingang) | translation updates (chapter 9, TOC in readme, glossary, etc.) |
| [121](https://github.com/Vonng/ddia/pull/121) | [@yingang](https://github.com/yingang) | translation updates (chapter 5 to chapter 8) |
| [120](https://github.com/Vonng/ddia/pull/120) | [@jiong-han](https://github.com/jiong-han) | Typo fix: 呲之以鼻 -> 嗤之以鼻 |
| [119](https://github.com/Vonng/ddia/pull/119) | [@cclauss](https://github.com/cclauss) | Streamline file operations in convert() |
| [118](https://github.com/Vonng/ddia/pull/118) | [@yingang](https://github.com/yingang) | translation updates (chapter 2 and 4) |
| [118](https://github.com/Vonng/ddia/pull/118) | [@yingang](https://github.com/yingang) | translation updates (chapter 2 to chapter 4) |
| [117](https://github.com/Vonng/ddia/pull/117) | [@feeeei](https://github.com/feeeei) | 統一每章的標題格式 |
| [115](https://github.com/Vonng/ddia/pull/115) | [@NageNalock](https://github.com/NageNalock) | 第七章病句修改: 重複詞語 |
| [114](https://github.com/Vonng/ddia/pull/114) | [@Sunt-ing](https://github.com/Sunt-ing) | Update README.md: correct the book name |

View File

@ -80,9 +80,9 @@
如果所有這些在一起意味著“正確工作”,那麼可以把可靠性粗略理解為“即使出現問題,也能繼續正確工作”。
造成錯誤的原因叫做**故障fault**,能預料並應對故障的系統特性可稱為**容錯fault-tolerant**或**韌性resilient**。“**容錯**”一詞可能會產生誤導,因為它暗示著系統可以容忍所有可能的錯誤,但在實際中這是不可能的。比方說,如果整個地球(及其上的所有伺服器)都被黑洞吞噬了,想要容忍這種錯誤,需要把網路託管到太空中——這種預算能不能批准就祝你好運了。所以在討論容錯時,只有談論特定型別的錯誤才有意義。
造成錯誤的原因叫做**故障fault**,能預料並應對故障的系統特性可稱為**容錯fault-tolerant** 或**韌性resilient**。“**容錯**”一詞可能會產生誤導,因為它暗示著系統可以容忍所有可能的錯誤,但在實際中這是不可能的。比方說,如果整個地球(及其上的所有伺服器)都被黑洞吞噬了,想要容忍這種錯誤,需要把網路託管到太空中——這種預算能不能批准就祝你好運了。所以在討論容錯時,只有談論特定型別的錯誤才有意義。
注意**故障fault**不同於**失效failure**【2】。**故障**通常定義為系統的一部分狀態偏離其標準,而**失效**則是系統作為一個整體停止向用戶提供服務。故障的概率不可能降到零,因此最好設計容錯機制以防因**故障**而導致**失效**。本書中我們將介紹幾種用不可靠的部件構建可靠系統的技術。
注意**故障fault** 不同於**失效failure**【2】。**故障**通常定義為系統的一部分狀態偏離其標準,而**失效**則是系統作為一個整體停止向用戶提供服務。故障的概率不可能降到零,因此最好設計容錯機制以防因**故障**而導致**失效**。本書中我們將介紹幾種用不可靠的部件構建可靠系統的技術。
反直覺的是,在這類容錯系統中,透過故意觸發來**提高**故障率是有意義的例如在沒有警告的情況下隨機地殺死單個程序。許多高危漏洞實際上是由糟糕的錯誤處理導致的【3】因此我們可以透過故意引發故障來確保容錯機制不斷執行並接受考驗從而提高故障自然發生時系統能正確處理的信心。Netflix公司的*Chaos Monkey*【4】就是這種方法的一個例子。
@ -90,7 +90,7 @@
### 硬體故障
當想到系統失效的原因時,**硬體故障hardware faults**總會第一個進入腦海。硬碟崩潰、記憶體出錯、機房斷電、有人拔錯網線……任何與大型資料中心打過交道的人都會告訴你:一旦你擁有很多機器,這些事情**總**會發生!
當想到系統失效的原因時,**硬體故障hardware faults** 總會第一個進入腦海。硬碟崩潰、記憶體出錯、機房斷電、有人拔錯網線……任何與大型資料中心打過交道的人都會告訴你:一旦你擁有很多機器,這些事情**總**會發生!
據報道稱,硬碟的 **平均無故障時間MTTF mean time to failure** 約為10到50年【5】【6】。因此從數學期望上講在擁有10000個磁碟的儲存叢集上平均每天會有1個磁碟出故障。
@ -98,7 +98,7 @@
直到最近,硬體冗餘對於大多數應用來說已經足夠了,它使單臺機器完全失效變得相當罕見。只要你能快速地把備份恢復到新機器上,故障停機時間對大多數應用而言都算不上災難性的。只有少量高可用性至關重要的應用才會要求有多套硬體冗餘。
但是隨著資料量和應用計算需求的增加,越來越多的應用開始大量使用機器,這會相應地增加硬體故障率。此外在一些雲平臺(**如亞馬遜網路服務AWS, Amazon Web Services**虛擬機器例項不可用卻沒有任何警告也是很常見的【7】因為雲平臺的設計就是優先考慮**靈活性flexibility**和**彈性elasticity**[^i],而不是單機可靠性。
但是隨著資料量和應用計算需求的增加,越來越多的應用開始大量使用機器,這會相應地增加硬體故障率。此外在一些雲平臺(**如亞馬遜網路服務AWS, Amazon Web Services**虛擬機器例項不可用卻沒有任何警告也是很常見的【7】因為雲平臺的設計就是優先考慮**靈活性flexibility** 和**彈性elasticity**[^i],而不是單機可靠性。
如果在硬體冗餘的基礎上進一步引入軟體容錯機制,那麼系統在容忍整個(單臺)機器故障的道路上就更進一步了。這樣的系統也有運維上的便利,例如:如果需要重啟機器(例如應用作業系統安全補丁),單伺服器系統就需要計劃停機。而允許機器失效的系統則可以一次修復一個節點,無需整個系統停機。
@ -234,7 +234,7 @@
另一方面最佳化第99.99百分位點(一萬個請求中最慢的一個)被認為太昂貴了,不能為亞馬遜的目標帶來足夠好處。減小高百分位點處的響應時間相當困難,因為它很容易受到隨機事件的影響,這超出了控制範圍,而且效益也很小。
百分位點通常用於**服務級別目標SLO, service level objectives**和**服務級別協議SLA, service level agreements**,即定義服務預期效能和可用性的合同。 SLA可能會宣告如果服務響應時間的中位數小於200毫秒且99.9百分位點低於1秒則認為服務工作正常如果響應時間更長就認為服務不達標。這些指標為客戶設定了期望值並允許客戶在SLA未達標的情況下要求退款。
百分位點通常用於**服務級別目標SLO, service level objectives** 和**服務級別協議SLA, service level agreements**,即定義服務預期效能和可用性的合同。 SLA可能會宣告如果服務響應時間的中位數小於200毫秒且99.9百分位點低於1秒則認為服務工作正常如果響應時間更長就認為服務不達標。這些指標為客戶設定了期望值並允許客戶在SLA未達標的情況下要求退款。
**排隊延遲queueing delay** 通常佔了高百分位點處響應時間的很大一部分。由於伺服器只能並行處理少量的事務如受其CPU核數的限制所以只要有少量緩慢的請求就能阻礙後續請求的處理這種效應有時被稱為 **頭部阻塞head-of-line blocking** 。即使後續請求在伺服器上處理的非常迅速,由於需要等待先前請求完成,客戶端最終看到的是緩慢的總體響應時間。因為存在這種效應,測量客戶端的響應時間非常重要。
@ -294,7 +294,7 @@
***可演化性evolability***
使工程師在未來能輕鬆地對系統進行更改,當需求變化時為新應用場景做適配。也稱為**可伸縮性extensibility****可修改性modifiability**或**可塑性plasticity**。
使工程師在未來能輕鬆地對系統進行更改,當需求變化時為新應用場景做適配。也稱為**可伸縮性extensibility****可修改性modifiability** 或**可塑性plasticity**。
和之前提到的可靠性、可伸縮性一樣,實現這些目標也沒有簡單的解決方案。不過我們會試著想象具有可操作性,簡單性和可演化性的系統會是什麼樣子。

View File

@ -165,7 +165,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
即使是具有**相同資料模型**的資料庫,將資料從一種資料庫匯出再匯入到另一種資料庫也並不容易。缺乏整合導致了資料的**巴爾幹化**[^譯註i]。
[^譯註i]: **巴爾幹化Balkanization**是一個常帶有貶義的地緣政治學術語,其定義為:一個國家或政區分裂成多個互相敵對的國家或政區的過程。
[^譯註i]: **巴爾幹化Balkanization** 是一個常帶有貶義的地緣政治學術語,其定義為:一個國家或政區分裂成多個互相敵對的國家或政區的過程。
@ -295,7 +295,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
#### 示例:使用者活動事件分析
[圖10-2](../img/fig10-2.png)給出了一個批處理作業中連線的典型例子。左側是事件日誌,描述登入使用者在網站上做的事情(稱為**活動事件activity events**或**點選流資料clickstream data**),右側是使用者資料庫。 你可以將此示例看作是星型模式的一部分(請參閱“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”):事件日誌是事實表,使用者資料庫是其中的一個維度。
[圖10-2](../img/fig10-2.png)給出了一個批處理作業中連線的典型例子。左側是事件日誌,描述登入使用者在網站上做的事情(稱為**活動事件activity events** 或**點選流資料clickstream data**),右側是使用者資料庫。 你可以將此示例看作是星型模式的一部分(請參閱“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”):事件日誌是事實表,使用者資料庫是其中的一個維度。
![](../img/fig10-2.png)
@ -351,7 +351,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
在單個Reducer中收集與某個名人相關的所有活動例如他們釋出內容的回覆可能導致嚴重的**偏斜**(也稱為**熱點hot spot**)—— 也就是說一個Reducer必須比其他Reducer處理更多的記錄請參閱“[負載偏斜與熱點消除](ch6.md#負載偏斜與熱點消除)“。由於MapReduce作業只有在所有Mapper和Reducer都完成時才完成所有後續作業必須等待最慢的Reducer才能啟動。
如果連線的輸入存在熱鍵可以使用一些演算法進行補償。例如Pig中的**偏斜連線skewed join**方法首先執行一個抽樣作業Sampling Job來確定哪些鍵是熱鍵【39】。連線實際執行時Mapper會將熱鍵的關聯記錄**隨機**相對於傳統MapReduce基於鍵雜湊的確定性方法傳送到幾個Reducer之一。對於另外一側的連線輸入與熱鍵相關的記錄需要被複制到**所有**處理該鍵的Reducer上【40】。
如果連線的輸入存在熱鍵可以使用一些演算法進行補償。例如Pig中的**偏斜連線skewed join** 方法首先執行一個抽樣作業Sampling Job來確定哪些鍵是熱鍵【39】。連線實際執行時Mapper會將熱鍵的關聯記錄**隨機**相對於傳統MapReduce基於鍵雜湊的確定性方法傳送到幾個Reducer之一。對於另外一側的連線輸入與熱鍵相關的記錄需要被複制到**所有**處理該鍵的Reducer上【40】。
這種技術將處理熱鍵的工作分散到多個Reducer上這樣可以使其更好地並行化代價是需要將連線另一側的輸入記錄複製到多個Reducer上。 Crunch中的**分片連線sharded join** 方法與之類似,但需要顯式指定熱鍵而不是使用抽樣作業。這種技術也非常類似於我們在“[負載偏斜與熱點消除](ch6.md#負載偏斜與熱點消除)”中討論的技術,使用隨機化來緩解分割槽資料庫中的熱點。
@ -623,7 +623,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
#### Pregel處理模型
針對圖批處理的最佳化 —— **批次同步並行BSPBulk Synchronous Parallel**計算模型【70】已經開始流行起來。其中Apache Giraph 【37】Spark的GraphX API和Flink的Gelly API 【71】實現了它。它也被稱為**Pregel**模型因為Google的Pregel論文推廣了這種處理圖的方法【72】。
針對圖批處理的最佳化 —— **批次同步並行BSPBulk Synchronous Parallel** 計算模型【70】已經開始流行起來。其中Apache Giraph 【37】Spark的GraphX API和Flink的Gelly API 【71】實現了它。它也被稱為**Pregel**模型因為Google的Pregel論文推廣了這種處理圖的方法【72】。
回想一下在MapReduce中Mapper在概念上向Reducer的特定呼叫“傳送訊息”因為框架將所有具有相同鍵的Mapper輸出集中在一起。 Pregel背後有一個類似的想法一個頂點可以向另一個頂點“傳送訊息”通常這些訊息是沿著圖的邊傳送的。

View File

@ -177,7 +177,7 @@
如果只追加寫入日誌,則磁碟空間終究會耗盡。為了回收磁碟空間,日誌實際上被分割成段,並不時地將舊段刪除或移動到歸檔儲存。 (我們將在後面討論一種更為複雜的磁碟空間釋放方式)
這就意味著如果一個慢消費者跟不上訊息產生的速率而落後得太多,它的消費偏移量指向了刪除的段,那麼它就會錯過一些訊息。實際上,日誌實現了一個有限大小的緩衝區,當緩衝區填滿時會丟棄舊訊息,它也被稱為**迴圈緩衝區circular buffer**或**環形緩衝區ring buffer**。不過由於緩衝區在磁碟上,因此緩衝區可能相當的大。
這就意味著如果一個慢消費者跟不上訊息產生的速率而落後得太多,它的消費偏移量指向了刪除的段,那麼它就會錯過一些訊息。實際上,日誌實現了一個有限大小的緩衝區,當緩衝區填滿時會丟棄舊訊息,它也被稱為**迴圈緩衝區circular buffer** 或**環形緩衝區ring buffer**。不過由於緩衝區在磁碟上,因此緩衝區可能相當的大。
讓我們做個簡單計算。在撰寫本文時典型的大型硬碟容量為6TB順序寫入吞吐量為150MB/s。如果以最快的速度寫訊息則需要大約11個小時才能填滿磁碟。因而磁碟可以緩衝11個小時的訊息之後它將開始覆蓋舊的訊息。即使使用多個磁碟和機器這個比率也是一樣的。實踐中的部署很少能用滿磁碟的寫入頻寬所以通常可以儲存一個幾天甚至幾周的日誌緩衝區。
@ -325,7 +325,7 @@
#### 命令和事件
事件溯源的哲學是仔細區分**事件event**和**命令command**【48】。當來自使用者的請求剛到達時它一開始是一個命令在這個時間點上它仍然可能可能失敗比如因為違反了一些完整性條件。應用必須首先驗證它是否可以執行該命令。如果驗證成功並且命令被接受則它變為一個持久化且不可變的事件。
事件溯源的哲學是仔細區分**事件event** 和**命令command**【48】。當來自使用者的請求剛到達時它一開始是一個命令在這個時間點上它仍然可能可能失敗比如因為違反了一些完整性條件。應用必須首先驗證它是否可以執行該命令。如果驗證成功並且命令被接受則它變為一個持久化且不可變的事件。
例如,如果使用者試圖註冊特定使用者名稱,或預定飛機或劇院的座位,則應用需要檢查使用者名稱或座位是否已被佔用。(先前在“[容錯共識](ch8.md#容錯共識)”中討論過這個例子當檢查成功時應用可以生成一個事件指示特定的使用者名稱是由特定的使用者ID註冊的或者座位已經預留給特定的顧客。
@ -416,7 +416,7 @@ $$
2. 你能以某種方式將事件推送給使用者,例如傳送報警郵件或推送通知,或將事件流式傳輸到可實時顯示的儀表板上。在這種情況下,人是流的最終消費者。
3. 你可以處理一個或多個輸入流併產生一個或多個輸出流。流可能會經過由幾個這樣的處理階段組成的流水線最後再輸出選項1或2
在本章的剩餘部分中我們將討論選項3處理流以產生其他衍生流。處理這樣的流的程式碼片段被稱為**運算元operator**或**作業job**。它與我們在[第十章](ch10.md)中討論過的Unix程序和MapReduce作業密切相關資料流的模式是相似的一個流處理器以只讀的方式使用輸入流並將其輸出以僅追加的方式寫入一個不同的位置。
在本章的剩餘部分中我們將討論選項3處理流以產生其他衍生流。處理這樣的流的程式碼片段被稱為**運算元operator** 或**作業job**。它與我們在[第十章](ch10.md)中討論過的Unix程序和MapReduce作業密切相關資料流的模式是相似的一個流處理器以只讀的方式使用輸入流並將其輸出以僅追加的方式寫入一個不同的位置。
流處理中的分割槽和並行化模式也非常類似於[第十章](ch10.md)中介紹的MapReduce和資料流引擎因此我們不再重複這些主題。基本的Map操作如轉換和過濾記錄也是一樣的。

View File

@ -119,7 +119,7 @@
>
> 大規模的“模式遷移”也發生在非計算機系統中。例如在19世紀英國鐵路建設初期軌距兩軌之間的距離就有了各種各樣的競爭標準。為一種軌距而建的列車不能在另一種軌距的軌道上執行這限制了火車網路中可能的相互連線【9】。
>
> 在1846年最終確定了一個標準軌距之後其他軌距的軌道必須轉換 —— 但是如何在不停運火車線路的情況下進行數月甚至數年的遷移?解決的辦法是首先透過新增第三條軌道將軌道轉換為**雙軌距dual guage**或**混合軌距**。這種轉換可以逐漸完成,當完成時,兩種軌距的列車都可以線上路上跑,使用三條軌道中的兩條。事實上,一旦所有的列車都轉換成標準軌距,那麼可以移除提供非標準軌距的軌道。
> 在1846年最終確定了一個標準軌距之後其他軌距的軌道必須轉換 —— 但是如何在不停運火車線路的情況下進行數月甚至數年的遷移?解決的辦法是首先透過新增第三條軌道將軌道轉換為**雙軌距dual guage** 或**混合軌距**。這種轉換可以逐漸完成,當完成時,兩種軌距的列車都可以線上路上跑,使用三條軌道中的兩條。事實上,一旦所有的列車都轉換成標準軌距,那麼可以移除提供非標準軌距的軌道。
>
> 以這種方式“再加工”現有的軌道讓新舊版本並存可以在幾年的時間內逐漸改變軌距。然而這是一項昂貴的事業這就是今天非標準軌距仍然存在的原因。例如舊金山灣區的BART系統使用了與美國大部分地區不同的軌距。

View File

@ -143,7 +143,7 @@ JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區
使用ID的好處是ID對人類沒有任何意義因而永遠不需要改變ID可以保持不變即使它標識的資訊發生變化。任何對人類有意義的東西都可能需要在將來某個時候改變——如果這些資訊被複制所有的冗餘副本都需要更新。這會導致寫入開銷也存在不一致的風險一些副本被更新了還有些副本沒有被更新。去除此類重複是資料庫 **規範化normalization** 的關鍵思想。[^ii]
[^ii]: 關於關係模型的文獻區分了幾種不同的規範形式,但這些區別幾乎沒有實際意義。一個經驗法則是,如果重複儲存了可以儲存在一個地方的值,則模式就不是**規範化normalized**的。
[^ii]: 關於關係模型的文獻區分了幾種不同的規範形式,但這些區別幾乎沒有實際意義。一個經驗法則是,如果重複儲存了可以儲存在一個地方的值,則模式就不是**規範化normalized** 的。
> 資料庫管理員和開發人員喜歡爭論規範化和非規範化,讓我們暫時保留判斷吧。在本書的[第三部分](part-iii.md),我們將回到這個話題,探討系統的方法用以處理快取,非規範化和衍生資料。
@ -515,7 +515,7 @@ db.observations.aggregate([
但是,要是多對多關係在你的資料中很常見呢?關係模型可以處理多對多關係的簡單情況,但是隨著資料之間的連線變得更加複雜,將資料建模為圖形顯得更加自然。
一個圖由兩種物件組成:**頂點vertices**(也稱為**節點nodes** 或**實體entities**),和**邊edges** 也稱為**關係relationships**或**弧 arcs** )。多種資料可以被建模為一個圖形。典型的例子包括:
一個圖由兩種物件組成:**頂點vertices**(也稱為**節點nodes** 或**實體entities**),和**邊edges** 也稱為**關係relationships** 或**弧 arcs** )。多種資料可以被建模為一個圖形。典型的例子包括:
***社交圖譜***

View File

@ -36,7 +36,7 @@ db_get () {
}
```
這兩個函式實現了鍵值儲存的功能。執行 `db_set key value` ,會將 **鍵key**和**值value** 儲存在資料庫中。鍵和值幾乎可以是你喜歡的任何東西例如值可以是JSON文件。然後呼叫 `db_get key` ,查詢與該鍵關聯的最新值並將其返回。
這兩個函式實現了鍵值儲存的功能。執行 `db_set key value` ,會將 **鍵key** 和**值value** 儲存在資料庫中。鍵和值幾乎可以是你喜歡的任何東西例如值可以是JSON文件。然後呼叫 `db_get key` ,查詢與該鍵關聯的最新值並將其返回。
麻雀雖小,五臟俱全:
@ -81,7 +81,7 @@ $ cat database
讓我們從 **鍵值資料key-value Data** 的索引開始。這不是您可以索引的唯一資料型別,但鍵值資料是很常見的。對於更復雜的索引來說,這是一個有用的構建模組。
鍵值儲存與在大多數程式語言中可以找到的**字典dictionary**型別非常相似,通常字典都是用**雜湊對映hash map**(或**雜湊表hash table**實現的。雜湊對映在許多演算法教科書中都有描述【1,2】所以這裡我們不會討論它的工作細節。既然我們已經有**記憶體中**資料結構 —— 雜湊對映,為什麼不使用它來索引在**磁碟上**的資料呢?
鍵值儲存與在大多數程式語言中可以找到的**字典dictionary** 型別非常相似,通常字典都是用**雜湊對映hash map**(或**雜湊表hash table**實現的。雜湊對映在許多演算法教科書中都有描述【1,2】所以這裡我們不會討論它的工作細節。既然我們已經有**記憶體中**資料結構 —— 雜湊對映,為什麼不使用它來索引在**磁碟上**的資料呢?
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣。那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到一個數據檔案中的位元組偏移量,指明瞭可以找到對應值的位置,如[圖3-1](../img/fig3-1.png)所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找seek** 該位置並讀取該值。
@ -310,7 +310,7 @@ B樹在資料庫體系結構中是非常根深蒂固的為許多工作負載
在某些情況下從索引到堆檔案的額外跳躍對讀取來說效能損失太大因此可能希望將索引行直接儲存在索引中。這被稱為聚集索引。例如在MySQL的InnoDB儲存引擎中表的主鍵總是一個聚集索引二級索引則引用主鍵而不是堆檔案中的位置【31】。在SQL Server中可以為每個表指定一個聚集索引【32】。
**聚集索引clustered index** (在索引中儲存所有行資料)和 **非聚集索引nonclustered index** (僅在索引中儲存對資料的引用)之間的折衷被稱為 **包含列的索引index with included columns**或**覆蓋索引covering index**其儲存表的一部分在索引內【33】。這允許透過單獨使用索引來回答一些查詢這種情況叫做索引 **覆蓋cover** 了查詢【32】。
**聚集索引clustered index** (在索引中儲存所有行資料)和 **非聚集索引nonclustered index** (僅在索引中儲存對資料的引用)之間的折衷被稱為 **包含列的索引index with included columns** 或**覆蓋索引covering index**其儲存表的一部分在索引內【33】。這允許透過單獨使用索引來回答一些查詢這種情況叫做索引 **覆蓋cover** 了查詢【32】。
與任何型別的資料重複一樣,聚集和覆蓋索引可以加快讀取速度,但是它們需要額外的儲存空間,並且會增加寫入開銷。資料庫還需要額外的努力來執行事務保證,因為應用程式不應看到任何因為重複而導致的不一致。

View File

@ -36,7 +36,7 @@
向前相容性可能會更棘手,因為舊版的程式需要忽略新版資料格式中新增的部分。
本章中將介紹幾種編碼資料的格式,包括 JSONXMLProtocol BuffersThrift和Avro。尤其將關注這些格式如何應對模式變化以及它們如何對新舊程式碼資料需要共存的系統提供支援。然後將討論如何使用這些格式進行資料儲存和通訊在Web服務中**表述性狀態傳遞REST**和**遠端過程呼叫RPC**,以及**訊息傳遞系統**如Actor和訊息佇列
本章中將介紹幾種編碼資料的格式,包括 JSONXMLProtocol BuffersThrift和Avro。尤其將關注這些格式如何應對模式變化以及它們如何對新舊程式碼資料需要共存的系統提供支援。然後將討論如何使用這些格式進行資料儲存和通訊在Web服務中**表述性狀態傳遞REST** 和**遠端過程呼叫RPC**,以及**訊息傳遞系統**如Actor和訊息佇列
## 編碼資料的格式
@ -47,7 +47,7 @@
[^i]: 除一些特殊情況外,例如某些記憶體對映檔案或直接在壓縮資料上操作(如“[列壓縮](ch4.md#列壓縮)”中所述)。
所以,需要在兩種表示之間進行某種型別的翻譯。 從記憶體中表示到位元組序列的轉換稱為 **編碼Encoding** (也稱為**序列化serialization**或**編組marshalling**),反過來稱為**解碼Decoding**[^ii]**解析Parsing****反序列化deserialization****反編組( unmarshalling**[^譯i]。
所以,需要在兩種表示之間進行某種型別的翻譯。 從記憶體中表示到位元組序列的轉換稱為 **編碼Encoding** (也稱為**序列化serialization** 或**編組marshalling**),反過來稱為**解碼Decoding**[^ii]**解析Parsing****反序列化deserialization****反編組( unmarshalling**[^譯i]。
[^ii]: 請注意,**編碼encode** 與 **加密encryption** 無關。 本書不討論加密。
[^譯i]: Marshal與Serialization的區別Marshal不僅傳輸物件的狀態而且會一起傳輸物件的方法相關程式碼

View File

@ -18,7 +18,7 @@
本章將假設你的資料集非常小,每臺機器都可以儲存整個資料集的副本。在[第六章](ch6.md)中將放寬這個假設,討論對單個機器來說太大的資料集的分割(分片)。在後面的章節中,我們將討論複製資料系統中可能發生的各種故障,以及如何處理這些故障。
如果複製中的資料不會隨時間而改變,那複製就很簡單:將資料複製到每個節點一次就萬事大吉。複製的困難之處在於處理複製資料的**變更change**,這就是本章所要講的。我們將討論三種流行的變更復制演算法:**單領導者single leader****多領導者multi leader**和**無領導者leaderless**。幾乎所有分散式資料庫都使用這三種方法之一。
如果複製中的資料不會隨時間而改變,那複製就很簡單:將資料複製到每個節點一次就萬事大吉。複製的困難之處在於處理複製資料的**變更change**,這就是本章所要講的。我們將討論三種流行的變更復制演算法:**單領導者single leader****多領導者multi leader** 和**無領導者leaderless**。幾乎所有分散式資料庫都使用這三種方法之一。
在複製時需要進行許多權衡:例如,使用同步複製還是非同步複製?如何處理失敗的副本?這些通常是資料庫中的配置選項,細節因資料庫而異,但原理在許多不同的實現中都類似。本章會討論這些決策的後果。
@ -31,7 +31,7 @@
每一次向資料庫的寫入操作都需要傳播到所有副本上,否則副本就會包含不一樣的資料。最常見的解決方案被稱為 **基於領導者的複製leader-based replication** (也稱 **主動/被動active/passive****主/從master/slave** 複製),如[圖5-1](#fig5-1.png)所示。它的工作原理如下:
1. 副本之一被指定為 **領導者leader**,也稱為 **主庫master|primary** 。當客戶端要向資料庫寫入時,它必須將請求傳送給**領導者**,領導者會將新資料寫入其本地儲存。
2. 其他副本被稱為**追隨者followers**,亦稱為**只讀副本read replicas****從庫slaves****備庫( secondaries****熱備hot-standby**[^i]。每當領導者將新資料寫入本地儲存時,它也會將資料變更傳送給所有的追隨者,稱之為**複製日誌replication log**記錄或**變更流change stream**。每個跟隨者從領導者拉取日誌,並相應更新其本地資料庫副本,方法是按照領導者處理的相同順序應用所有寫入。
2. 其他副本被稱為**追隨者followers**,亦稱為**只讀副本read replicas****從庫slaves****備庫( secondaries****熱備hot-standby**[^i]。每當領導者將新資料寫入本地儲存時,它也會將資料變更傳送給所有的追隨者,稱之為**複製日誌replication log** 記錄或**變更流change stream**。每個跟隨者從領導者拉取日誌,並相應更新其本地資料庫副本,方法是按照領導者處理的相同順序應用所有寫入。
3. 當客戶想要從資料庫中讀取資料時,它可以向領導者或追隨者查詢。 但只有領導者才能接受寫操作(從客戶端的角度來看從庫都是隻讀的)。
[^i]: 不同的人對 **熱hot****溫warm****冷cold** 備份伺服器有不同的定義。 例如在PostgreSQL中**熱備hot standby** 指的是能接受客戶端讀請求的副本。而 **溫備warm standby** 只是追隨領導者,但不處理客戶端的任何查詢。 就本書而言,這些差異並不重要。
@ -103,7 +103,7 @@
故障切換可以手動進行(通知管理員主庫掛了,並採取必要的步驟來建立新的主庫)或自動進行。自動故障切換過程通常由以下步驟組成:
1. 確認主庫失效。有很多事情可能會出錯:崩潰,停電,網路問題等等。沒有萬無一失的方法來檢測出現了什麼問題,所以大多數系統只是簡單使用 **超時Timeout** 節點頻繁地相互來回傳遞訊息並且如果一個節點在一段時間內例如30秒沒有響應就認為它掛了因為計劃內維護而故意關閉主庫不算
2. 選擇一個新的主庫。這可以透過選舉過程(主庫由剩餘副本以多數選舉產生)來完成,或者可以由之前選定的**控制器節點controller node**來指定新的主庫。主庫的最佳人選通常是擁有舊主庫最新資料副本的從庫(最小化資料損失)。讓所有的節點同意一個新的領導者,是一個**共識**問題,將在[第九章](ch9.md)詳細討論。
2. 選擇一個新的主庫。這可以透過選舉過程(主庫由剩餘副本以多數選舉產生)來完成,或者可以由之前選定的**控制器節點controller node** 來指定新的主庫。主庫的最佳人選通常是擁有舊主庫最新資料副本的從庫(最小化資料損失)。讓所有的節點同意一個新的領導者,是一個**共識**問題,將在[第九章](ch9.md)詳細討論。
3. 重新配置系統以啟用新的主庫。客戶端現在需要將它們的寫請求傳送給新主庫(將在“[請求路由](ch6.md#請求路由)”中討論這個問題)。如果老領導回來,可能仍然認為自己是主庫,沒有意識到其他副本已經讓它下臺了。系統需要確保老領導認可新領導,成為一個從庫。
故障切換會出現很多大麻煩:

View File

@ -121,7 +121,7 @@
次級索引是關係型資料庫的基礎並且在文件資料庫中也很普遍。許多鍵值儲存如HBase和Volde-mort為了減少實現的複雜度而放棄了次級索引但是一些如Riak已經開始新增它們因為它們對於資料模型實在是太有用了。並且次級索引也是Solr和Elasticsearch等搜尋伺服器的基石。
次級索引的問題是它們不能整齊地對映到分割槽。有兩種用二級索引對資料庫進行分割槽的方法:**基於文件的分割槽document-based**和**基於關鍵詞term-based的分割槽**。
次級索引的問題是它們不能整齊地對映到分割槽。有兩種用二級索引對資料庫進行分割槽的方法:**基於文件的分割槽document-based** 和**基於關鍵詞term-based的分割槽**。
### 基於文件的二級索引進行分割槽

View File

@ -47,11 +47,11 @@
### ACID的含義
事務所提供的安全保證通常由眾所周知的首字母縮略詞ACID來描述ACID代表**原子性Atomicity****一致性Consistency****隔離性Isolation**和**永續性Durability**。它由Theo Härder和Andreas Reuter於1983年提出旨在為資料庫中的容錯機制建立精確的術語。
事務所提供的安全保證通常由眾所周知的首字母縮略詞ACID來描述ACID代表**原子性Atomicity****一致性Consistency****隔離性Isolation** 和**永續性Durability**。它由Theo Härder和Andreas Reuter於1983年提出旨在為資料庫中的容錯機制建立精確的術語。
但實際上不同資料庫的ACID實現並不相同。例如我們將會看到關於**隔離性**的含義就有許多含糊不清【8】。高層次上的想法很美好但魔鬼隱藏在細節裡。今天當一個系統聲稱自己“符合ACID”時實際上能期待的是什麼保證並不清楚。不幸的是ACID現在幾乎已經變成了一個營銷術語。
不符合ACID標準的系統有時被稱為BASE它代表**基本可用性Basically Available****軟狀態Soft State**和**最終一致性Eventual consistency**【9】這比ACID的定義更加模糊似乎BASE的唯一合理的定義是“不是ACID”即它幾乎可以代表任何你想要的東西。
不符合ACID標準的系統有時被稱為BASE它代表**基本可用性Basically Available****軟狀態Soft State** 和**最終一致性Eventual consistency**【9】這比ACID的定義更加模糊似乎BASE的唯一合理的定義是“不是ACID”即它幾乎可以代表任何你想要的東西。
讓我們深入瞭解原子性,一致性,隔離性和永續性的定義,這可以讓我們提煉出事務的思想。
@ -296,7 +296,7 @@ SELECT COUNT*FROM emails WHERE recipient_id = 2 AND unread_flag = true
愛麗絲在銀行有1000美元的儲蓄分為兩個賬戶每個500美元。現在一筆事務從她的一個賬戶轉移了100美元到另一個賬戶。如果她在事務處理的同時檢視其賬戶餘額列表她可能會碰巧在收款到達前看到收款賬戶的餘額仍然是500美元而在付款產生後看到付款賬戶的餘額已經是400美元。對愛麗絲來說現在她的賬戶似乎總共只有900美元——看起來有100美元已經憑空消失了。
這種異常被稱為**不可重複讀nonrepeatable read**或**讀取偏差read skew**如果Alice在事務結束時再次讀取賬戶1的餘額她將看到與她之前的查詢中看到的不同的值600美元。在讀已提交的隔離條件下**不可重複讀**被認為是可接受的Alice看到的帳戶餘額時確實在閱讀時已經提交了。
這種異常被稱為**不可重複讀nonrepeatable read** 或**讀取偏差read skew**如果Alice在事務結束時再次讀取賬戶1的餘額她將看到與她之前的查詢中看到的不同的值600美元。在讀已提交的隔離條件下**不可重複讀**被認為是可接受的Alice看到的帳戶餘額時確實在閱讀時已經提交了。
> 不幸的是,術語**偏差skew** 這個詞是過載的:以前使用它是因為熱點的不平衡工作量(請參閱“[負載偏斜與熱點消除](ch6.md#負載偏斜與熱點消除)”),而這裡偏差意味著異常的時序。
@ -368,7 +368,7 @@ SELECT COUNT*FROM emails WHERE recipient_id = 2 AND unread_flag = true
#### 可重複讀與命名混淆
快照隔離是一個有用的隔離級別特別對於只讀事務而言。但是許多資料庫實現了它卻用不同的名字來稱呼。在Oracle中稱為**可序列化Serializable**的在PostgreSQL和MySQL中稱為**可重複讀repeatable read**【23】。
快照隔離是一個有用的隔離級別特別對於只讀事務而言。但是許多資料庫實現了它卻用不同的名字來稱呼。在Oracle中稱為**可序列化Serializable** 在PostgreSQL和MySQL中稱為**可重複讀repeatable read**【23】。
這種命名混淆的原因是SQL標準沒有**快照隔離**的概念因為標準是基於System R 1975年定義的隔離級別【2】那時候**快照隔離**尚未發明。相反,它定義了**可重複讀**,表面上看起來與快照隔離很相似。 PostgreSQL和MySQL稱其**快照隔離**級別為**可重複讀repeatable read**,因為這樣符合標準要求,所以它們可以聲稱自己“標準相容”。
@ -588,7 +588,7 @@ COMMIT;
這不是一個新問題從20世紀70年代以來就一直是這樣了當時首先引入了較弱的隔離級別【2】。一直以來研究人員的答案都很簡單使用**可序列化serializable** 的隔離級別!
**可序列化Serializability**隔離通常被認為是最強的隔離級別。它保證即使事務可以並行執行,最終的結果也是一樣的,就好像它們沒有任何併發性,連續挨個執行一樣。因此資料庫保證,如果事務在單獨執行時正常執行,則它們在併發執行時繼續保持正確 —— 換句話說,資料庫可以防止**所有**可能的競爭條件。
**可序列化Serializability** 隔離通常被認為是最強的隔離級別。它保證即使事務可以並行執行,最終的結果也是一樣的,就好像它們沒有任何併發性,連續挨個執行一樣。因此資料庫保證,如果事務在單獨執行時正常執行,則它們在併發執行時繼續保持正確 —— 換句話說,資料庫可以防止**所有**可能的競爭條件。
但如果可序列化隔離級別比弱隔離級別的爛攤子要好得多,那為什麼沒有人見人愛?為了回答這個問題,我們需要看看實現可序列化的選項,以及它們如何執行。目前大多數提供可序列化的資料庫都使用了三種技術之一,本章的剩餘部分將會介紹這些技術:
@ -687,7 +687,7 @@ VoltDB還使用儲存過程進行復制但不是將事務的寫入結果從
2PL用於MySQLInnoDB和SQL Server中的可序列化隔離級別以及DB2中的可重複讀隔離級別【23,36】。
讀與寫的阻塞是透過為資料庫中每個物件新增鎖來實現的。鎖可以處於**共享模式shared mode**或**獨佔模式exclusive mode**。鎖使用如下:
讀與寫的阻塞是透過為資料庫中每個物件新增鎖來實現的。鎖可以處於**共享模式shared mode** 或**獨佔模式exclusive mode**。鎖使用如下:
- 若事務要讀取物件,則須先以共享模式獲取鎖。允許多個事務同時持有共享鎖。但如果另一個事務已經在物件上持有排它鎖,則這些事務必須等待。
- 若事務要寫入一個物件,它必須首先以獨佔模式獲取該鎖。沒有其他事務可以同時持有鎖(無論是共享模式還是獨佔模式),所以如果物件上存在任何鎖,該事務必須等待。
@ -710,7 +710,7 @@ VoltDB還使用儲存過程進行復制但不是將事務的寫入結果從
#### 謂詞鎖
在前面關於鎖的描述中,我們掩蓋了一個微妙而重要的細節。在“[導致寫入偏差的幻讀](#導致寫入偏差的幻讀)”中,我們討論了**幻讀phantoms**的問題。即一個事務改變另一個事務的搜尋查詢的結果。具有可序列化隔離級別的資料庫必須防止**幻讀**。
在前面關於鎖的描述中,我們掩蓋了一個微妙而重要的細節。在“[導致寫入偏差的幻讀](#導致寫入偏差的幻讀)”中,我們討論了**幻讀phantoms** 的問題。即一個事務改變另一個事務的搜尋查詢的結果。具有可序列化隔離級別的資料庫必須防止**幻讀**。
在會議室預訂的例子中,這意味著如果一個事務在某個時間視窗內搜尋了一個房間的現有預訂(見[例7-2]()),則另一個事務不能同時插入或更新同一時間視窗與同一房間的另一個預訂 (可以同時插入其他房間的預訂,或在不影響另一個預定的條件下預定同一房間的其他時間段)。

View File

@ -123,7 +123,7 @@
> #### 網路分割槽
>
> 當網路的一部分由於網路故障而被切斷時,有時稱為**網路分割槽network partition**或**網路斷裂netsplit**。在本書中,我們通常會堅持使用更一般的術語**網路故障network fault**,以避免與[第六章](ch6.md)討論的儲存系統的分割槽(分片)相混淆。
> 當網路的一部分由於網路故障而被切斷時,有時稱為**網路分割槽network partition** 或**網路斷裂netsplit**。在本書中,我們通常會堅持使用更一般的術語**網路故障network fault**,以避免與[第六章](ch6.md)討論的儲存系統的分割槽(分片)相混淆。
即使網路故障在你的環境中非常罕見,故障可能發生的事實,意味著你的軟體需要能夠處理它們。無論何時透過網路進行通訊,都可能會失敗,這是無法避免的。
@ -170,7 +170,7 @@
* 如果多個不同的節點同時嘗試將資料包傳送到同一目的地,則網路交換機必須將它們排隊並將它們逐個送入目標網路鏈路(如[圖8-2](../img/fig8-2.png)所示)。在繁忙的網路鏈路上,資料包可能需要等待一段時間才能獲得一個插槽(這稱為網路擁塞)。如果傳入的資料太多,交換機佇列填滿,資料包將被丟棄,因此需要重新發送資料包 - 即使網路執行良好。
* 當資料包到達目標機器時如果所有CPU核心當前都處於繁忙狀態則來自網路的傳入請求將被作業系統排隊直到應用程式準備好處理它為止。根據機器上的負載這可能需要一段任意的時間。
* 在虛擬化環境中正在執行的作業系統經常暫停幾十毫秒因為另一個虛擬機器正在使用CPU核心。在這段時間內虛擬機器不能從網路中消耗任何資料所以傳入的資料被虛擬機器監視器 【26】排隊緩衝進一步增加了網路延遲的可變性。
* TCP執行**流量控制flow control**(也稱為**擁塞避免congestion avoidance**或**背壓backpressure**其中節點會限制自己的傳送速率以避免網路鏈路或接收節點過載【27】。這意味著甚至在資料進入網路之前在傳送者處就需要進行額外的排隊。
* TCP執行**流量控制flow control**(也稱為**擁塞避免congestion avoidance** 或**背壓backpressure**其中節點會限制自己的傳送速率以避免網路鏈路或接收節點過載【27】。這意味著甚至在資料進入網路之前在傳送者處就需要進行額外的排隊。
![](../img/fig8-2.png)
@ -221,7 +221,7 @@
但是,目前在多租戶資料中心和公共雲或透過網際網路[^iv]進行通訊時,此類服務質量尚未啟用。當前部署的技術不允許我們對網路的延遲或可靠性作出任何保證:我們必須假設網路擁塞,排隊和無限的延遲總是會發生。因此,超時時間沒有“正確”的值——它需要透過實驗來確定。
[^iv]: 網際網路服務提供商之間的對等協議和透過**BGP閘道器協議BGP**建立的路由與IP協議相比更接近於電路交換。在這個級別上可以購買專用頻寬。但是網際網路路由在網路級別執行而不是主機之間的單獨連線而且執行時間要長得多。
[^iv]: 網際網路服務提供商之間的對等協議和透過**BGP閘道器協議BGP** 建立的路由與IP協議相比更接近於電路交換。在這個級別上可以購買專用頻寬。但是網際網路路由在網路級別執行而不是主機之間的單獨連線而且執行時間要長得多。
> ### 延遲和資源利用
>
@ -638,7 +638,7 @@ while (true) {
* 節點的時鐘可能會與其他節點顯著不同步儘管您盡最大努力設定NTP它可能會突然跳轉或跳回依靠它是很危險的因為您很可能沒有好的方法來測量你的時鐘的錯誤間隔。
* 一個程序可能會在其執行的任何時候暫停一段相當長的時間(可能是因為停止所有處理的垃圾收集器),被其他節點宣告死亡,然後再次復活,卻沒有意識到它被暫停了。
這類**部分失效partial failure**可能發生的事實是分散式系統的決定性特徵。每當軟體試圖做任何涉及其他節點的事情時,偶爾就有可能會失敗,或者隨機變慢,或者根本沒有響應(最終超時)。在分散式系統中,我們試圖在軟體中建立**部分失效**的容錯機制,這樣整個系統在即使某些組成部分被破壞的情況下,也可以繼續執行。
這類**部分失效partial failure** 可能發生的事實是分散式系統的決定性特徵。每當軟體試圖做任何涉及其他節點的事情時,偶爾就有可能會失敗,或者隨機變慢,或者根本沒有響應(最終超時)。在分散式系統中,我們試圖在軟體中建立**部分失效**的容錯機制,這樣整個系統在即使某些組成部分被破壞的情況下,也可以繼續執行。
為了容忍錯誤,第一步是**檢測**它們,但即使這樣也很難。大多數系統沒有檢測節點是否發生故障的準確機制,所以大多數分散式演算法依靠**超時**來確定遠端節點是否仍然可用。但是超時無法區分網路失效和節點失效並且可變的網路延遲有時會導致節點被錯誤地懷疑發生故障。此外有時一個節點可能處於降級狀態例如由於驅動程式錯誤千兆網絡卡可能突然下降到1 Kb/s的吞吐量【94】。這樣一個“跛行”而不是死掉的節點可能比一個乾淨的失效節點更難處理。

View File

@ -55,7 +55,7 @@
在**最終一致**的資料庫,如果你在同一時刻問兩個不同副本相同的問題,可能會得到兩個不同的答案。這很讓人困惑。如果資料庫可以提供只有一個副本的假象(即,只有一個數據副本),那麼事情就簡單太多了。那麼每個客戶端都會有相同的資料檢視,且不必擔心複製滯後了。
這就是**線性一致性linearizability**背後的想法【6】也稱為**原子一致性atomic consistency**【7】**強一致性strong consistency****立即一致性immediate consistency**或**外部一致性external consistency **【8】。線性一致性的精確定義相當微妙我們將在本節的剩餘部分探討它。但是基本的想法是讓一個系統看起來好像只有一個數據副本而且所有的操作都是原子性的。有了這個保證即使實際中可能有多個副本應用也不需要擔心它們。
這就是**線性一致性linearizability** 背後的想法【6】也稱為**原子一致性atomic consistency**【7】**強一致性strong consistency****立即一致性immediate consistency** 或**外部一致性external consistency **【8】。線性一致性的精確定義相當微妙我們將在本節的剩餘部分探討它。但是基本的想法是讓一個系統看起來好像只有一個數據副本而且所有的操作都是原子性的。有了這個保證即使實際中可能有多個副本應用也不需要擔心它們。
在一個線性一致的系統中,只要一個客戶端成功完成寫操作,所有客戶端從資料庫中讀取資料必須能夠看到剛剛寫入的值。要維護資料的單個副本的假象,系統應保障讀到的值是最近的、最新的,而不是來自陳舊的快取或副本。換句話說,線性一致性是一個**新鮮度保證recency guarantee**。為了闡明這個想法,我們來看看一個非線性一致系統的例子。
@ -125,7 +125,7 @@
* 在客戶端A從資料庫收到響應之前客戶端B的讀取返回 `1` ,表示寫入值 `1` 已成功。這也是可以的這並不意味著在寫之前讀到了值這只是意味著從資料庫到客戶端A的正確響應在網路中略有延遲。
* 此模型不假設有任何事務隔離另一個客戶端可能隨時更改值。例如C首先讀取 `1` ,然後讀取 `2` 因為兩次讀取之間的值由B更改。可以使用原子**比較並設定cas**操作來檢查該值是否未被另一客戶端同時更改B和C的**cas**請求成功但是D的**cas**請求失敗(在資料庫處理它時,`x` 的值不再是 `0` )。
* 此模型不假設有任何事務隔離另一個客戶端可能隨時更改值。例如C首先讀取 `1` ,然後讀取 `2` 因為兩次讀取之間的值由B更改。可以使用原子**比較並設定cas** 操作來檢查該值是否未被另一客戶端同時更改B和C的**cas**請求成功但是D的**cas**請求失敗(在資料庫處理它時,`x` 的值不再是 `0` )。
* 客戶B的最後一次讀取陰影條柱中不是線性一致性的。 該操作與C的**cas**寫操作併發(它將 `x``2` 更新為 `4` 。在沒有其他請求的情況下B的讀取返回 `2` 是可以的。然而在B的讀取開始之前客戶端A已經讀取了新的值 `4` 因此不允許B讀取比A更舊的值。再次與[圖9-1](../img/fig9-1.png)中的Alice和Bob的情況相同。
@ -139,11 +139,11 @@
>
> ***可序列化***
>
> **可序列化Serializability**是事務的隔離屬性,每個事務可以讀寫多個物件(行,文件,記錄)——請參閱“[單物件和多物件操作](ch7.md#單物件和多物件操作)”。它確保事務的行為,與它們按照**某種**順序依次執行的結果相同每個事務在下一個事務開始之前執行完成。這種執行順序可以與事務實際執行的順序不同。【12】。
> **可序列化Serializability** 是事務的隔離屬性,每個事務可以讀寫多個物件(行,文件,記錄)——請參閱“[單物件和多物件操作](ch7.md#單物件和多物件操作)”。它確保事務的行為,與它們按照**某種**順序依次執行的結果相同每個事務在下一個事務開始之前執行完成。這種執行順序可以與事務實際執行的順序不同。【12】。
>
> ***線性一致性***
>
> **線性一致性Linearizability**是讀取和寫入暫存器(單個物件)的**新鮮度保證**。它不會將操作組合為事務,因此它也不會阻止寫入偏差等問題(請參閱“[寫入偏差和幻讀](ch7.md#寫入偏斜與幻讀)”),除非採取其他措施(例如[物化衝突](ch7.md#物化衝突))。
> **線性一致性Linearizability** 是讀取和寫入暫存器(單個物件)的**新鮮度保證**。它不會將操作組合為事務,因此它也不會阻止寫入偏差等問題(請參閱“[寫入偏差和幻讀](ch7.md#寫入偏斜與幻讀)”),除非採取其他措施(例如[物化衝突](ch7.md#物化衝突))。
>
> 一個數據庫可以提供可序列化和線性一致性,這種組合被稱為嚴格的可序列化或**強的單副本可序列化strong-1SR**【4,13】。基於兩階段鎖定的可序列化實現請參閱“[兩階段鎖定](ch7.md#兩階段鎖定)”一節)或**真的序列執行**(請參閱第“[真的序列執行](ch7.md#真的序列執行)”)通常是線性一致性的。
>
@ -291,7 +291,7 @@
#### 線性一致性和網路延遲
雖然線性一致是一個很有用的保證但實際上線性一致的系統驚人的少。例如現代多核CPU上的記憶體甚至都不是線性一致的【43】如果一個CPU核上執行的執行緒寫入某個記憶體地址而另一個CPU核上執行的執行緒不久之後讀取相同的地址並沒有保證一定能一定讀到第一個執行緒寫入的值除非使用了**記憶體屏障memory barrier**或**圍欄fence**【44】
雖然線性一致是一個很有用的保證但實際上線性一致的系統驚人的少。例如現代多核CPU上的記憶體甚至都不是線性一致的【43】如果一個CPU核上執行的執行緒寫入某個記憶體地址而另一個CPU核上執行的執行緒不久之後讀取相同的地址並沒有保證一定能一定讀到第一個執行緒寫入的值除非使用了**記憶體屏障memory barrier** 或**圍欄fence**【44】
這種行為的原因是每個CPU核都有自己的記憶體快取和儲存緩衝區。預設情況下記憶體訪問首先走快取任何變更會非同步寫入主存。因為快取訪問比主存要快得多【45】所以這個特性對於現代CPU的良好效能表現至關重要。但是現在就有幾個資料副本一個在主存中也許還有幾個在不同快取中的其他副本而且這些副本是非同步更新的所以就失去了線性一致性。
@ -307,7 +307,7 @@
之前說過,線性一致暫存器的行為就好像只有單個數據副本一樣,且每個操作似乎都是在某個時間點以原子性的方式生效的。這個定義意味著操作是按照某種良好定義的順序執行的。我們將操作以看上去被執行的順序連線起來,以此說明了[圖9-4](../img/fig9-4.png)中的順序。
**順序ordering**這一主題在本書中反覆出現,這表明它可能是一個重要的基礎性概念。讓我們簡要回顧一下其它曾經出現過**順序**的上下文:
**順序ordering** 這一主題在本書中反覆出現,這表明它可能是一個重要的基礎性概念。讓我們簡要回顧一下其它曾經出現過**順序**的上下文:
* 在[第五章](ch5.md)中我們看到,領導者在單主複製中的主要目的就是,在複製日誌中確定**寫入順序order of write**——也就是從庫應用這些寫入的順序。如果不存在一個領導者,則併發操作可能導致衝突(請參閱“[處理寫入衝突](ch5.md#處理寫入衝突)”)。
* 在[第七章](ch7.md)中討論的**可序列化**,是關於事務表現的像按**某種先後順序some sequential order** 執行的保證。它可以字面意義上地以**序列順序serial order** 執行事務來實現,或者允許並行執行,但同時防止序列化衝突來實現(透過鎖或中止事務)。
@ -319,9 +319,9 @@
**順序**反覆出現有幾個原因,其中一個原因是,它有助於保持**因果關係causality**。在本書中我們已經看到了幾個例子,其中因果關係是很重要的:
* 在“[一致字首讀](ch5.md#一致字首讀)”([圖5-5](../img/fig5-5.png))中,我們看到一個例子:一個對話的觀察者首先看到問題的答案,然後才看到被回答的問題。這是令人困惑的,因為它違背了我們對**因cause**與**果effect**的直覺:如果一個問題被回答,顯然問題本身得先在那裡,因為給出答案的人必須先看到這個問題(假如他們並沒有預見未來的超能力)。我們認為在問題和答案之間存在**因果依賴causal dependency**。
* 在“[一致字首讀](ch5.md#一致字首讀)”([圖5-5](../img/fig5-5.png))中,我們看到一個例子:一個對話的觀察者首先看到問題的答案,然後才看到被回答的問題。這是令人困惑的,因為它違背了我們對**因cause** 與**果effect** 的直覺:如果一個問題被回答,顯然問題本身得先在那裡,因為給出答案的人必須先看到這個問題(假如他們並沒有預見未來的超能力)。我們認為在問題和答案之間存在**因果依賴causal dependency**。
* [圖5-9](../img/fig5-9.png)中出現了類似的模式,我們看到三位領導者之間的複製,並注意到由於網路延遲,一些寫入可能會“壓倒”其他寫入。從其中一個副本的角度來看,好像有一個對尚不存在的記錄的更新操作。這裡的因果意味著,一條記錄必須先被建立,然後才能被更新。
* 在“[檢測併發寫入](ch5.md#檢測併發寫入)”中我們觀察到如果有兩個操作A和B則存在三種可能性A發生在B之前或B發生在A之前或者A和B**併發**。這種**此前發生happened before**關係是因果關係的另一種表述如果A在B前發生那麼意味著B可能已經知道了A或者建立在A的基礎上或者依賴於A。如果A和B是**併發**的那麼它們之間並沒有因果聯絡換句話說我們確信A和B不知道彼此。
* 在“[檢測併發寫入](ch5.md#檢測併發寫入)”中我們觀察到如果有兩個操作A和B則存在三種可能性A發生在B之前或B發生在A之前或者A和B**併發**。這種**此前發生happened before** 關係是因果關係的另一種表述如果A在B前發生那麼意味著B可能已經知道了A或者建立在A的基礎上或者依賴於A。如果A和B是**併發**的那麼它們之間並沒有因果聯絡換句話說我們確信A和B不知道彼此。
* 在事務快照隔離的上下文中(“[快照隔離和可重複讀](ch7.md#快照隔離和可重複讀)”),我們說事務是從一致性快照中讀取的。但此語境中“一致”到底又是什麼意思?這意味著**與因果關係保持一致consistent with causality**如果快照包含答案它也必須包含被回答的問題【48】。在某個時間點觀察整個資料庫與因果關係保持一致意味著因果上在該時間點之前發生的所有操作其影響都是可見的但因果上在該時間點之後發生的操作其影響對觀察者不可見。**讀偏差read skew** 意味著讀取的資料處於違反因果關係的狀態(不可重複讀,如[圖7-6](../img/fig7-6.png)所示)。
* 事務之間**寫偏差write skew** 的例子(請參閱“[寫入偏斜與幻讀](ch7.md#寫入偏斜與幻讀)”)也說明了因果依賴:在[圖7-8](../img/fig7-8.png)中,愛麗絲被允許離班,因為事務認為鮑勃仍在值班,反之亦然。在這種情況下,離班的動作因果依賴於對當前值班情況的觀察。[可序列化快照隔離](ch7.md#可序列化快照隔離)透過跟蹤事務之間的因果依賴來檢測寫偏差。
* 在愛麗絲和鮑勃看球的例子中([圖9-1](../img/fig9-1.png)),在聽到愛麗絲驚呼比賽結果後,鮑勃從伺服器得到陳舊結果的事實違背了因果關係:愛麗絲的驚呼因果依賴於得分宣告,所以鮑勃應該也能在聽到愛麗斯驚呼後查詢到比分。相同的模式在“[跨通道的時序依賴](#跨通道的時序依賴)”一節中,以“影象大小調整服務”的偽裝再次出現。
@ -386,7 +386,7 @@
雖然因果是一個重要的理論概念,但實際上跟蹤所有的因果關係是不切實際的。在許多應用中,客戶端在寫入內容之前會先讀取大量資料,我們無法弄清寫入因果依賴於先前全部的讀取內容,還是僅包括其中一部分。顯式跟蹤所有已讀資料意味著巨大的額外開銷。
但還有一個更好的方法:我們可以使用**序列號sequence nunber**或**時間戳timestamp**來排序事件。時間戳不一定來自日曆時鐘(或物理時鐘,它們存在許多問題,如 “[不可靠的時鐘](ch8.md#不可靠的時鐘)” 中所述)。它可以來自一個**邏輯時鐘logical clock**,這是一個用來生成標識操作的數字序列的演算法,典型實現是使用一個每次操作自增的計數器。
但還有一個更好的方法:我們可以使用**序列號sequence nunber** 或**時間戳timestamp** 來排序事件。時間戳不一定來自日曆時鐘(或物理時鐘,它們存在許多問題,如 “[不可靠的時鐘](ch8.md#不可靠的時鐘)” 中所述)。它可以來自一個**邏輯時鐘logical clock**,這是一個用來生成標識操作的數字序列的演算法,典型實現是使用一個每次操作自增的計數器。
這樣的序列號或時間戳是緊湊的(只有幾個位元組大小),它提供了一個全序關係:也就是說每個操作都有一個唯一的序列號,而且總是可以比較兩個序列號,確定哪一個更大(即哪些操作後發生)。
@ -461,7 +461,7 @@
如果你的程式只執行在單個CPU核上那麼定義一個操作全序是很容易的可以簡單地就是CPU執行這些操作的順序。但是在分散式系統中讓所有節點對同一個全域性操作順序達成一致可能相當棘手。在上一節中我們討論了按時間戳或序列號進行排序但發現它還不如單主複製給力如果你使用時間戳排序來實現唯一性約束就不能容忍任何錯誤因為你必須要從每個節點都獲取到最新的序列號
如前所述單主複製透過選擇一個節點作為主庫來確定操作的全序並在主庫的單個CPU核上對所有操作進行排序。接下來的挑戰是如果吞吐量超出單個主庫的處理能力這種情況下如何擴充套件系統以及如果主庫失效“[處理節點宕機](ch5.md#處理節點宕機)”),如何處理故障切換。在分散式系統文獻中,這個問題被稱為**全序廣播total order broadcast**或**原子廣播atomic broadcast**[^ix]【25,57,58】。
如前所述單主複製透過選擇一個節點作為主庫來確定操作的全序並在主庫的單個CPU核上對所有操作進行排序。接下來的挑戰是如果吞吐量超出單個主庫的處理能力這種情況下如何擴充套件系統以及如果主庫失效“[處理節點宕機](ch5.md#處理節點宕機)”),如何處理故障切換。在分散式系統文獻中,這個問題被稱為**全序廣播total order broadcast** 或**原子廣播atomic broadcast**[^ix]【25,57,58】。
[^ix]: “原子廣播”是一個傳統的術語非常混亂而且與“原子”一詞的其他用法不一致它與ACID事務中的原子性沒有任何關係只是與原子操作在多執行緒程式設計的意義上 或原子暫存器線性一致儲存有間接的聯絡。全序組播total order multicast是另一個同義詞。
@ -602,7 +602,7 @@
#### 兩階段提交簡介
**兩階段提交two-phase commit**是一種用於實現跨多個節點的原子事務提交的演算法,即確保所有節點提交或所有節點中止。 它是分散式資料庫中的經典演算法【13,35,75】。 2PC在某些資料庫內部使用也以**XA事務**的形式對應用可用【76,77】例如Java Transaction API支援或以SOAP Web服務的`WS-AtomicTransaction` 形式提供給應用【78,79】。
**兩階段提交two-phase commit** 是一種用於實現跨多個節點的原子事務提交的演算法,即確保所有節點提交或所有節點中止。 它是分散式資料庫中的經典演算法【13,35,75】。 2PC在某些資料庫內部使用也以**XA事務**的形式對應用可用【76,77】例如Java Transaction API支援或以SOAP Web服務的`WS-AtomicTransaction` 形式提供給應用【78,79】。
[圖9-9](../img/fig9-9.png)說明了2PC的基本流程。2PC中的提交/中止過程分為兩個階段(因此而得名),而不是單節點事務中的單個提交請求。
@ -697,7 +697,7 @@
*X/Open XA***擴充套件架構eXtended Architecture** 的縮寫是跨異構技術實現兩階段提交的標準【76,77】。它於1991年推出並得到了廣泛的實現許多傳統關係資料庫包括PostgreSQLMySQLDB2SQL Server和Oracle和訊息代理包括ActiveMQHornetQMSMQ和IBM MQ 都支援XA。
XA不是一個網路協議——它只是一個用來與事務協調者連線的C API。其他語言也有這種API的繫結例如在Java EE應用的世界中XA事務是使用**Java事務APIJTA, Java Transaction API**實現的,而許多使用**Java資料庫連線JDBC, Java Database Connectivity**的資料庫驅動,以及許多使用**Java訊息服務JMS**API的訊息代理都支援**Java事務APIJTA**。
XA不是一個網路協議——它只是一個用來與事務協調者連線的C API。其他語言也有這種API的繫結例如在Java EE應用的世界中XA事務是使用**Java事務APIJTA, Java Transaction API** 實現的,而許多使用**Java資料庫連線JDBC, Java Database Connectivity** 的資料庫驅動,以及許多使用**Java訊息服務JMS** API的訊息代理都支援**Java事務APIJTA**。
XA假定你的應用使用網路驅動或客戶端庫來與**參與者**資料庫或訊息服務進行通訊。如果驅動支援XA則意味著它會呼叫XA API 以查明操作是否為分散式事務的一部分 —— 如果是,則將必要的資訊發往資料庫伺服器。驅動還會向協調者暴露回撥介面,協調者可以透過回撥來要求參與者準備、提交或中止。
@ -742,7 +742,7 @@
非正式地,共識意味著讓幾個節點就某事達成一致。例如,如果有幾個人**同時concurrently** 嘗試預訂飛機上的最後一個座位,或劇院中的同一個座位,或者嘗試使用相同的使用者名稱註冊一個帳戶。共識演算法可以用來確定這些**互不相容mutually incompatible** 的操作中,哪一個才是贏家。
共識問題通常形式化如下:一個或多個節點可以**提議propose**某些值,而共識演算法**決定decides**採用其中的某個值。在座位預訂的例子中,當幾個顧客同時試圖訂購最後一個座位時,處理顧客請求的每個節點可以**提議**將要服務的顧客的ID而**決定**指明瞭哪個顧客獲得了座位。
共識問題通常形式化如下:一個或多個節點可以**提議propose** 某些值,而共識演算法**決定decides** 採用其中的某個值。在座位預訂的例子中,當幾個顧客同時試圖訂購最後一個座位時,處理顧客請求的每個節點可以**提議**將要服務的顧客的ID而**決定**指明瞭哪個顧客獲得了座位。
在這種形式下共識演算法必須滿足以下性質【25】[^xiii]

View File

@ -25,9 +25,9 @@
## 伸縮至更高的載荷
如果你需要的只是伸縮至更高的**載荷load**,最簡單的方法就是購買更強大的機器(有時稱為**垂直伸縮vertical scaling**或**向上伸縮scale up**)。許多處理器,記憶體和磁碟可以在同一個作業系統下相互連線,快速的相互連線允許任意處理器訪問記憶體或磁碟的任意部分。在這種 **共享記憶體架構shared-memory architecture** 中,所有的元件都可以看作一臺單獨的機器[^i]。
如果你需要的只是伸縮至更高的**載荷load**,最簡單的方法就是購買更強大的機器(有時稱為**垂直伸縮vertical scaling** 或**向上伸縮scale up**)。許多處理器,記憶體和磁碟可以在同一個作業系統下相互連線,快速的相互連線允許任意處理器訪問記憶體或磁碟的任意部分。在這種 **共享記憶體架構shared-memory architecture** 中,所有的元件都可以看作一臺單獨的機器[^i]。
[^i]: 在大型機中,儘管任意處理器都可以訪問記憶體的任意部分,但總有一些記憶體區域與一些處理器更接近(稱為**非均勻記憶體訪問nonuniform memory access, NUMA**【1】。 為了有效利用這種架構特性,需要對處理進行細分,以便每個處理器主要訪問臨近的記憶體,這意味著即使表面上看起來只有一臺機器在執行,**分割槽partitioning**仍然是必要的。
[^i]: 在大型機中,儘管任意處理器都可以訪問記憶體的任意部分,但總有一些記憶體區域與一些處理器更接近(稱為**非均勻記憶體訪問nonuniform memory access, NUMA**【1】。 為了有效利用這種架構特性,需要對處理進行細分,以便每個處理器主要訪問臨近的記憶體,這意味著即使表面上看起來只有一臺機器在執行,**分割槽partitioning** 仍然是必要的。
共享記憶體方法的問題在於,成本增長速度快於線性增長:一臺有著雙倍處理器數量,雙倍記憶體大小,雙倍磁碟容量的機器,通常成本會遠遠超過原來的兩倍。而且可能因為存在瓶頸,並不足以處理雙倍的載荷。

View File

@ -11,7 +11,7 @@
* 即使您在一個小團隊中工作現在也可以構建分佈在多臺計算機甚至多個地理區域的系統這要歸功於譬如亞馬遜網路服務AWS等基礎設施即服務IaaS概念的踐行者。
* 許多服務都要求高可用,因停電或維護導致的服務不可用,變得越來越難以接受。
**資料密集型應用data-intensive applications**正在透過使用這些技術進步來推動可能性的邊界。一個應用被稱為**資料密集型**的,如果**資料是其主要挑戰**(資料量,資料複雜度或資料變化速度)—— 與之相對的是**計算密集型**,即處理器速度是其瓶頸。
**資料密集型應用data-intensive applications** 正在透過使用這些技術進步來推動可能性的邊界。一個應用被稱為**資料密集型**的,如果**資料是其主要挑戰**(資料量,資料複雜度或資料變化速度)—— 與之相對的是**計算密集型**,即處理器速度是其瓶頸。
幫助資料密集型應用儲存和處理資料的工具與技術正迅速地適應這些變化。新型資料庫系統“NoSQL”已經備受關注而訊息佇列快取搜尋索引批處理和流處理框架以及相關技術也非常重要。很多應用組合使用這些工具與技術。