mirror of
https://github.com/Vonng/ddia.git
synced 2026-06-21 00:47:05 +08:00
add tw translation
This commit is contained in:
parent
003fb7a32e
commit
7c17fcfaa1
30 changed files with 8573 additions and 11031 deletions
|
|
@ -2191,7 +2191,7 @@ several widely used isolation levels, in particular *read committed*, *snapshot
|
|||
(sometimes called *repeatable read*), and *serializable*. We characterized those isolation levels by
|
||||
discussing various examples of race conditions, summarized in [Table 8-1](/en/ch8#ch_transactions_isolation_levels):
|
||||
|
||||
Table 8-1. Summary of anomalies that can occur at various isolation levels
|
||||
{{< figure id="ch_transactions_isolation_levels" title="Table 8-1. Summary of anomalies that can occur at various isolation levels" class="w-full my-4" >}}
|
||||
|
||||
| Isolation level | Dirty reads | Read skew | Phantom reads | Lost updates | Write skew |
|
||||
|--------------------|-------------|-------------|---------------|--------------|-------------|
|
||||
|
|
@ -2348,4 +2348,4 @@ The examples in this chapter used a relational data model. However, as discussed
|
|||
[^82]: Clemens Vasters. [Transactions in Windows Azure (with Service Bus) – An Email Discussion](https://learn.microsoft.com/en-gb/archive/blogs/clemensv/transactions-in-windows-azure-with-service-bus-an-email-discussion). *learn.microsoft.com*, July 2012. Archived at [perma.cc/4EZ9-5SKW](https://perma.cc/4EZ9-5SKW)
|
||||
[^83]: Ajmer Dhariwal. [Orphaned MSDTC Transactions (-2 spids)](https://www.eraofdata.com/posts/2008/orphaned-msdtc-transactions-2-spids/). *eraofdata.com*, December 2008. Archived at [perma.cc/YG6F-U34C](https://perma.cc/YG6F-U34C)
|
||||
[^84]: Paul Randal. [Real World Story of DBCC PAGE Saving the Day](https://www.sqlskills.com/blogs/paul/real-world-story-of-dbcc-page-saving-the-day/). *sqlskills.com*, June 2013. Archived at [perma.cc/2MJN-A5QH](https://perma.cc/2MJN-A5QH)
|
||||
[^85]: Guozhang Wang, Lei Chen, Ayusman Dikshit, Jason Gustafson, Boyang Chen, Matthias J. Sax, John Roesler, Sophie Blee-Goldman, Bruno Cadonna, Apurva Mehta, Varun Madan, and Jun Rao. [Consistency and Completeness: Rethinking Distributed Stream Processing in Apache Kafka](https://dl.acm.org/doi/pdf/10.1145/3448016.3457556). At *ACM International Conference on Management of Data* (SIGMOD), June 2021. [doi:10.1145/3448016.3457556](https://doi.org/10.1145/3448016.3457556)
|
||||
[^85]: Guozhang Wang, Lei Chen, Ayusman Dikshit, Jason Gustafson, Boyang Chen, Matthias J. Sax, John Roesler, Sophie Blee-Goldman, Bruno Cadonna, Apurva Mehta, Varun Madan, and Jun Rao. [Consistency and Completeness: Rethinking Distributed Stream Processing in Apache Kafka](https://dl.acm.org/doi/pdf/10.1145/3448016.3457556). At *ACM International Conference on Management of Data* (SIGMOD), June 2021. [doi:10.1145/3448016.3457556](https://doi.org/10.1145/3448016.3457556)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: 設計資料密集型應用
|
||||
title: 設計資料密集型應用(第二版)
|
||||
linkTitle: DDIA
|
||||
cascade:
|
||||
type: docs
|
||||
|
|
@ -19,7 +19,7 @@ PostgreSQL 專家,資料庫老司機,雲計算泥石流。
|
|||
**校訂**: [@yingang](https://github.com/yingang) | [繁體中文](/tw) **版本維護** by [@afunTW](https://github.com/afunTW) | [完整貢獻者列表](/contrib)
|
||||
|
||||
> [!NOTE]
|
||||
> DDIA [**第二版**](/v2) 正在翻譯中 ([`content/v2`](https://github.com/Vonng/ddia/tree/main) 目錄),歡迎加入並提出您的寶貴意見!
|
||||
> DDIA [**第二版**](/v2) 正在翻譯中 ([`v2/v2`](https://github.com/Vonng/ddia/tree/main) 目錄),歡迎加入並提出您的寶貴意見!
|
||||
|
||||
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ PostgreSQL 專家,資料庫老司機,雲計算泥石流。
|
|||
* [第八章:分散式系統的麻煩](/tw/ch8)
|
||||
* [第九章:一致性與共識](/tw/ch9)
|
||||
|
||||
### [第三部分:衍生資料](/tw/part-iii)
|
||||
### [第三部分:派生資料](/tw/part-iii)
|
||||
|
||||
* [第十章:批處理](/tw/ch10)
|
||||
* [第十一章:流處理](/tw/ch11)
|
||||
|
|
|
|||
|
|
@ -1,409 +1,500 @@
|
|||
---
|
||||
title: "第一章:可靠性、可伸縮性和可維護性"
|
||||
linkTitle: "1. 可靠性、可伸縮性和可維護性"
|
||||
title: "1. 資料系統架構中的權衡"
|
||||
weight: 101
|
||||
breadcrumbs: false
|
||||
---
|
||||
|
||||
|
||||

|
||||
|
||||
> 網際網路做得太棒了,以至於大多數人將它看作像太平洋這樣的自然資源,而不是什麼人工產物。上一次出現這種大規模且無差錯的技術,你還記得是什麼時候嗎?
|
||||
> *沒有解決方案,只有利弊權衡。[…] 但你要努力獲得最好的權衡,這就是你所能期望的全部。*
|
||||
>
|
||||
> —— [艾倫・凱](http://www.drdobbs.com/architecture-and-design/interview-with-alan-kay/240003442) 在接受 Dobb 博士雜誌採訪時說(2012 年)
|
||||
> [Thomas Sowell](https://www.youtube.com/watch?v=2YUtKr8-_Fg),接受 Fred Barnes 採訪(2005)
|
||||
|
||||
現今很多應用程式都是 **資料密集型(data-intensive)** 的,而非 **計算密集型(compute-intensive)** 的。因此 CPU 很少成為這類應用的瓶頸,更大的問題通常來自資料量、資料複雜性、以及資料的變更速度。
|
||||
|
||||
資料密集型應用通常由標準組件構建而成,標準組件提供了很多通用的功能;例如,許多應用程式都需要:
|
||||
|
||||
- 儲存資料,以便自己或其他應用程式之後能再次找到 (*資料庫,即 databases*)
|
||||
- 記住開銷昂貴操作的結果,加快讀取速度(*快取,即 caches*)
|
||||
- 允許使用者按關鍵字搜尋資料,或以各種方式對資料進行過濾(*搜尋索引,即 search indexes*)
|
||||
- 向其他程序傳送訊息,進行非同步處理(*流處理,即 stream processing*)
|
||||
- 定期處理累積的大批次資料(*批處理,即 batch processing*)
|
||||
|
||||
如果這些功能聽上去平淡無奇,那是因為這些 **資料系統(data system)** 是非常成功的抽象:我們一直不假思索地使用它們並習以為常。絕大多數工程師不會幻想從零開始編寫儲存引擎,因為在開發應用時,資料庫已經是足夠完美的工具了。
|
||||
|
||||
但現實沒有這麼簡單。不同的應用有著不同的需求,因而資料庫系統也是百花齊放,有著各式各樣的特性。實現快取有很多種手段,建立搜尋索引也有好幾種方法,諸如此類。因此在開發應用前,我們依然有必要先弄清楚最適合手頭工作的工具和方法。而且當單個工具解決不了你的問題時,組合使用這些工具可能還是有些難度的。
|
||||
|
||||
本書將是一趟關於資料系統原理、實踐與應用的旅程,並講述了設計資料密集型應用的方法。我們將探索不同工具之間的共性與特性,以及各自的實現原理。
|
||||
|
||||
本章將從我們所要實現的基礎目標開始:可靠、可伸縮、可維護的資料系統。我們將澄清這些詞語的含義,概述考量這些目標的方法。並回顧一些後續章節所需的基礎知識。在接下來的章節中我們將抽絲剝繭,研究設計資料密集型應用時可能遇到的設計決策。
|
||||
|
||||
|
||||
## 關於資料系統的思考
|
||||
|
||||
我們通常認為,資料庫、訊息佇列、快取等工具分屬於幾個差異顯著的類別。雖然資料庫和訊息隊列表面上有一些相似性 —— 它們都會儲存一段時間的資料 —— 但它們有迥然不同的訪問模式,這意味著迥異的效能特徵和實現手段。
|
||||
|
||||
那我們為什麼要把這些東西放在 **資料系統(data system)** 的總稱之下混為一談呢?
|
||||
|
||||
近些年來,出現了許多新的資料儲存工具與資料處理工具。它們針對不同應用場景進行最佳化,因此不再適合生硬地歸入傳統類別【1】。類別之間的界限變得越來越模糊,例如:資料儲存可以被當成訊息佇列用(Redis),訊息佇列則帶有類似資料庫的持久保證(Apache Kafka)。
|
||||
|
||||
其次,越來越多的應用程式有著各種嚴格而廣泛的要求,單個工具不足以滿足所有的資料處理和儲存需求。取而代之的是,總體工作被拆分成一系列能被單個工具高效完成的任務,並透過應用程式碼將它們縫合起來。
|
||||
|
||||
例如,如果將快取(應用管理的快取層,Memcached 或同類產品)和全文搜尋(全文搜尋伺服器,例如 Elasticsearch 或 Solr)功能從主資料庫剝離出來,那麼使快取 / 索引與主資料庫保持同步通常是應用程式碼的責任。[圖 1-1](/img/fig1-1.png) 給出了這種架構可能的樣子(細節將在後面的章節中詳細介紹)。
|
||||
|
||||

|
||||
|
||||
**圖 1-1 一個可能的組合使用多個元件的資料系統架構**
|
||||
|
||||
當你將多個工具組合在一起提供服務時,服務的介面或 **應用程式程式設計介面(API, Application Programming Interface)** 通常向客戶端隱藏這些實現細節。現在,你基本上已經使用較小的通用元件建立了一個全新的、專用的資料系統。這個新的複合資料系統可能會提供特定的保證,例如:快取在寫入時會作廢或更新,以便外部客戶端獲取一致的結果。現在你不僅是應用程式開發人員,還是資料系統設計人員了。
|
||||
|
||||
設計資料系統或服務時可能會遇到很多棘手的問題,例如:當系統出問題時,如何確保資料的正確性和完整性?當部分系統退化降級時,如何為客戶提供始終如一的良好效能?當負載增加時,如何擴容應對?什麼樣的 API 才是好的 API?
|
||||
|
||||
影響資料系統設計的因素很多,包括參與人員的技能和經驗、歷史遺留問題、系統路徑依賴、交付時限、公司的風險容忍度、監管約束等,這些因素都需要具體問題具體分析。
|
||||
|
||||
本書著重討論三個在大多數軟體系統中都很重要的問題:
|
||||
|
||||
* 可靠性(Reliability)
|
||||
|
||||
系統在 **困境**(adversity,比如硬體故障、軟體故障、人為錯誤)中仍可正常工作(正確完成功能,並能達到期望的效能水準)。請參閱 “[可靠性](#可靠性)”。
|
||||
|
||||
* 可伸縮性(Scalability)
|
||||
|
||||
有合理的辦法應對系統的增長(資料量、流量、複雜性)。請參閱 “[可伸縮性](#可伸縮性)”。
|
||||
|
||||
* 可維護性(Maintainability)
|
||||
|
||||
許多不同的人(工程師、運維)在不同的生命週期,都能高效地在系統上工作(使系統保持現有行為,並適應新的應用場景)。請參閱 “[可維護性](#可維護性)”。
|
||||
|
||||
人們經常追求這些詞彙,卻沒有清楚理解它們到底意味著什麼。為了工程的嚴謹性,本章的剩餘部分將探討可靠性、可伸縮性和可維護性的含義。為實現這些目標而使用的各種技術,架構和演算法將在後續的章節中研究。
|
||||
|
||||
|
||||
## 可靠性
|
||||
|
||||
人們對於一個東西是否可靠,都有一個直觀的想法。人們對可靠軟體的典型期望包括:
|
||||
|
||||
* 應用程式表現出使用者所期望的功能。
|
||||
* 允許使用者犯錯,允許使用者以出乎意料的方式使用軟體。
|
||||
* 在預期的負載和資料量下,效能滿足要求。
|
||||
* 系統能防止未經授權的訪問和濫用。
|
||||
|
||||
如果所有這些在一起意味著 “正確工作”,那麼可以把可靠性粗略理解為 “即使出現問題,也能繼續正確工作”。
|
||||
|
||||
造成錯誤的原因叫做 **故障(fault)**,能預料並應對故障的系統特性可稱為 **容錯(fault-tolerant)** 或 **回彈性(resilient)**。“**容錯**” 一詞可能會產生誤導,因為它暗示著系統可以容忍所有可能的錯誤,但在實際中這是不可能的。比方說,如果整個地球(及其上的所有伺服器)都被黑洞吞噬了,想要容忍這種錯誤,需要把網路託管到太空中 —— 這種預算能不能批准就祝你好運了。所以在討論容錯時,只有談論特定型別的錯誤才有意義。
|
||||
|
||||
注意 **故障(fault)** 不同於 **失效(failure)**【2】。**故障** 通常定義為系統的一部分狀態偏離其標準,而 **失效** 則是系統作為一個整體停止向用戶提供服務。故障的機率不可能降到零,因此最好設計容錯機制以防因 **故障** 而導致 **失效**。本書中我們將介紹幾種用不可靠的部件構建可靠系統的技術。
|
||||
|
||||
反直覺的是,在這類容錯系統中,透過故意觸發來 **提高** 故障率是有意義的,例如:在沒有警告的情況下隨機地殺死單個程序。許多高危漏洞實際上是由糟糕的錯誤處理導致的【3】,因此我們可以透過故意引發故障來確保容錯機制不斷執行並接受考驗,從而提高故障自然發生時系統能正確處理的信心。Netflix 公司的 *Chaos Monkey*【4】就是這種方法的一個例子。
|
||||
|
||||
儘管比起 **阻止錯誤(prevent error)**,我們通常更傾向於 **容忍錯誤**。但也有 **預防勝於治療** 的情況(比如不存在治療方法時)。安全問題就屬於這種情況。例如,如果攻擊者破壞了系統,並獲取了敏感資料,這種事是撤銷不了的。但本書主要討論的是可以恢復的故障種類,正如下面幾節所述。
|
||||
|
||||
### 硬體故障
|
||||
|
||||
當想到系統失效的原因時,**硬體故障(hardware faults)** 總會第一個進入腦海。硬碟崩潰、記憶體出錯、機房斷電、有人拔錯網線…… 任何與大型資料中心打過交道的人都會告訴你:一旦你擁有很多機器,這些事情 **總** 會發生!
|
||||
|
||||
據報道稱,硬碟的 **平均無故障時間(MTTF, mean time to failure)** 約為 10 到 50 年【5】【6】。因此從數學期望上講,在擁有 10000 個磁碟的儲存叢集上,平均每天會有 1 個磁碟出故障。
|
||||
|
||||
為了減少系統的故障率,第一反應通常都是增加單個硬體的冗餘度,例如:磁碟可以組建 RAID,伺服器可能有雙路電源和熱插拔 CPU,資料中心可能有電池和柴油發電機作為後備電源,某個元件掛掉時冗餘元件可以立刻接管。這種方法雖然不能完全防止由硬體問題導致的系統失效,但它簡單易懂,通常也足以讓機器不間斷執行很多年。
|
||||
|
||||
直到最近,硬體冗餘對於大多數應用來說已經足夠了,它使單臺機器完全失效變得相當罕見。只要你能快速地把備份恢復到新機器上,故障停機時間對大多數應用而言都算不上災難性的。只有少量高可用性至關重要的應用才會要求有多套硬體冗餘。
|
||||
|
||||
但是隨著資料量和應用計算需求的增加,越來越多的應用開始大量使用機器,這會相應地增加硬體故障率。此外,在類似亞馬遜 AWS(Amazon Web Services)的一些雲服務平臺上,虛擬機器例項不可用卻沒有任何警告也是很常見的【7】,因為雲平臺的設計就是優先考慮 **靈活性(flexibility)** 和 **彈性(elasticity)**[^i],而不是單機可靠性。
|
||||
|
||||
如果在硬體冗餘的基礎上進一步引入軟體容錯機制,那麼系統在容忍整個(單臺)機器故障的道路上就更進一步了。這樣的系統也有運維上的便利,例如:如果需要重啟機器(例如應用作業系統安全補丁),單伺服器系統就需要計劃停機。而允許機器失效的系統則可以一次修復一個節點,無需整個系統停機。
|
||||
|
||||
[^i]: 在 [應對負載的方法](#應對負載的方法) 一節定義
|
||||
|
||||
### 軟體錯誤
|
||||
|
||||
我們通常認為硬體故障是隨機的、相互獨立的:一臺機器的磁碟失效並不意味著另一臺機器的磁碟也會失效。雖然大量硬體元件之間可能存在微弱的相關性(例如伺服器機架的溫度等共同的原因),但同時發生故障也是極為罕見的。
|
||||
|
||||
另一類錯誤是內部的 **系統性錯誤(systematic error)**【8】。這類錯誤難以預料,而且因為是跨節點相關的,所以比起不相關的硬體故障往往可能造成更多的 **系統失效**【5】。例子包括:
|
||||
|
||||
* 接受特定的錯誤輸入,便導致所有應用伺服器例項崩潰的 BUG。例如 2012 年 6 月 30 日的閏秒,由於 Linux 核心中的一個錯誤【9】,許多應用同時掛掉了。
|
||||
* 失控程序會用盡一些共享資源,包括 CPU 時間、記憶體、磁碟空間或網路頻寬。
|
||||
* 系統依賴的服務變慢,沒有響應,或者開始返回錯誤的響應。
|
||||
* 級聯故障,一個元件中的小故障觸發另一個元件中的故障,進而觸發更多的故障【10】。
|
||||
|
||||
導致這類軟體故障的 BUG 通常會潛伏很長時間,直到被異常情況觸發為止。這種情況意味著軟體對其環境做出了某種假設 —— 雖然這種假設通常來說是正確的,但由於某種原因最後不再成立了【11】。
|
||||
|
||||
雖然軟體中的系統性故障沒有速效藥,但我們還是有很多小辦法,例如:仔細考慮系統中的假設和互動;徹底的測試;程序隔離;允許程序崩潰並重啟;測量、監控並分析生產環境中的系統行為。如果系統能夠提供一些保證(例如在一個訊息佇列中,進入與發出的訊息數量相等),那麼系統就可以在執行時不斷自檢,並在出現 **差異(discrepancy)** 時報警【12】。
|
||||
|
||||
### 人為錯誤
|
||||
|
||||
設計並構建了軟體系統的工程師是人類,維持系統執行的運維也是人類。即使他們懷有最大的善意,人類也是不可靠的。舉個例子,一項關於大型網際網路服務的研究發現,運維配置錯誤是導致服務中斷的首要原因,而硬體故障(伺服器或網路)僅導致了 10-25% 的服務中斷【13】。
|
||||
|
||||
儘管人類不可靠,但怎麼做才能讓系統變得可靠?最好的系統會組合使用以下幾種辦法:
|
||||
|
||||
* 以最小化犯錯機會的方式設計系統。例如,精心設計的抽象、API 和管理後臺使做對事情更容易,搞砸事情更困難。但如果介面限制太多,人們就會忽略它們的好處而想辦法繞開。很難正確把握這種微妙的平衡。
|
||||
* 將人們最容易犯錯的地方與可能導致失效的地方 **解耦(decouple)**。特別是提供一個功能齊全的非生產環境 **沙箱(sandbox)**,使人們可以在不影響真實使用者的情況下,使用真實資料安全地探索和實驗。
|
||||
* 在各個層次進行徹底的測試【3】,從單元測試、全系統整合測試到手動測試。自動化測試易於理解,已經被廣泛使用,特別適合用來覆蓋正常情況中少見的 **邊緣場景(corner case)**。
|
||||
* 允許從人為錯誤中簡單快速地恢復,以最大限度地減少失效情況帶來的影響。例如,快速回滾配置變更,分批發布新程式碼(以便任何意外錯誤隻影響一小部分使用者),並提供資料重算工具(以備舊的計算出錯)。
|
||||
* 配置詳細和明確的監控,比如效能指標和錯誤率。在其他工程學科中這指的是 **遙測(telemetry)**(一旦火箭離開了地面,遙測技術對於跟蹤發生的事情和理解失敗是至關重要的)。監控可以向我們發出預警訊號,並允許我們檢查是否有任何地方違反了假設和約束。當出現問題時,指標資料對於問題診斷是非常寶貴的。
|
||||
* 良好的管理實踐與充分的培訓 —— 一個複雜而重要的方面,但超出了本書的範圍。
|
||||
|
||||
### 可靠性有多重要?
|
||||
|
||||
可靠性不僅僅是針對核電站和空中交通管制軟體而言,我們也期望更多平凡的應用能可靠地執行。商務應用中的錯誤會導致生產力損失(也許資料報告不完整還會有法律風險),而電商網站的中斷則可能會導致收入和聲譽的巨大損失。
|
||||
|
||||
即使在 “非關鍵” 應用中,我們也對使用者負有責任。試想一位家長把所有的照片和孩子的影片儲存在你的照片應用裡【15】。如果資料庫突然損壞,他們會感覺如何?他們可能會知道如何從備份恢復嗎?
|
||||
|
||||
在某些情況下,我們可能會選擇犧牲可靠性來降低開發成本(例如為未經證實的市場開發產品原型)或運營成本(例如利潤率極低的服務),但我們偷工減料時,應該清楚意識到自己在做什麼。
|
||||
|
||||
|
||||
## 可伸縮性
|
||||
|
||||
系統今天能可靠執行,並不意味未來也能可靠執行。服務 **降級(degradation)** 的一個常見原因是負載增加,例如:系統負載已經從一萬個併發使用者增長到十萬個併發使用者,或者從一百萬增長到一千萬。也許現在處理的資料量級要比過去大得多。
|
||||
|
||||
**可伸縮性(Scalability)** 是用來描述系統應對負載增長能力的術語。但是請注意,這不是貼在系統上的一維標籤:說 “X 可伸縮” 或 “Y 不可伸縮” 是沒有任何意義的。相反,討論可伸縮性意味著考慮諸如 “如果系統以特定方式增長,有什麼選項可以應對增長?” 和 “如何增加計算資源來處理額外的負載?” 等問題。
|
||||
|
||||
### 描述負載
|
||||
|
||||
在討論增長問題(如果負載加倍會發生什麼?)前,首先要能簡要描述系統的當前負載。負載可以用一些稱為 **負載引數(load parameters)** 的數字來描述。引數的最佳選擇取決於系統架構,它可能是每秒向 Web 伺服器發出的請求、資料庫中的讀寫比率、聊天室中同時活躍的使用者數量、快取命中率或其他東西。除此之外,也許平均情況對你很重要,也許你的瓶頸是少數極端場景。
|
||||
|
||||
為了使這個概念更加具體,我們以推特在 2012 年 11 月釋出的資料【16】為例。推特的兩個主要業務是:
|
||||
|
||||
* 釋出推文
|
||||
|
||||
使用者可以向其粉絲釋出新訊息(平均 4.6k 請求 / 秒,峰值超過 12k 請求 / 秒)。
|
||||
|
||||
* 主頁時間線
|
||||
|
||||
使用者可以查閱他們關注的人釋出的推文(300k 請求 / 秒)。
|
||||
|
||||
處理每秒 12,000 次寫入(發推文的速率峰值)還是很簡單的。然而推特的伸縮性挑戰並不是主要來自推特量,而是來自 **扇出(fan-out)**[^ii]—— 每個使用者關注了很多人,也被很多人關注。
|
||||
|
||||
[^ii]: 扇出:從電子工程學中借用的術語,它描述了輸入連線到另一個門輸出的邏輯閘數量。輸出需要提供足夠的電流來驅動所有連線的輸入。在事務處理系統中,我們使用它來描述為了服務一個傳入請求而需要執行其他服務的請求數量。
|
||||
|
||||
大體上講,這一對操作有兩種實現方式。
|
||||
|
||||
1. 釋出推文時,只需將新推文插入全域性推文集合即可。當一個使用者請求自己的主頁時間線時,首先查詢他關注的所有人,查詢這些被關注使用者釋出的推文並按時間順序合併。在如 [圖 1-2](/img/fig1-2.png) 所示的關係型資料庫中,可以編寫這樣的查詢:
|
||||
|
||||
```sql
|
||||
SELECT tweets.*, users.*
|
||||
FROM tweets
|
||||
JOIN users ON tweets.sender_id = users.id
|
||||
JOIN follows ON follows.followee_id = users.id
|
||||
WHERE follows.follower_id = current_user
|
||||
```
|
||||
|
||||

|
||||
|
||||
**圖 1-2 推特主頁時間線的關係型模式簡單實現**
|
||||
|
||||
2. 為每個使用者的主頁時間線維護一個快取,就像每個使用者的推文收件箱([圖 1-3](/img/fig1-3.png))。當一個使用者釋出推文時,查詢所有關注該使用者的人,並將新的推文插入到每個主頁時間線快取中。因此讀取主頁時間線的請求開銷很小,因為結果已經提前計算好了。
|
||||
|
||||

|
||||
|
||||
**圖 1-3 用於分發推特至關注者的資料流水線,2012 年 11 月的負載引數【16】**
|
||||
|
||||
推特的第一個版本使用了方法 1,但系統很難跟上主頁時間線查詢的負載。所以公司轉向了方法 2,方法 2 的效果更好,因為發推頻率比查詢主頁時間線的頻率幾乎低了兩個數量級,所以在這種情況下,最好在寫入時做更多的工作,而在讀取時做更少的工作。
|
||||
|
||||
然而方法 2 的缺點是,發推現在需要大量的額外工作。平均來說,一條推文會發往約 75 個關注者,所以每秒 4.6k 的發推寫入,變成了對主頁時間線快取每秒 345k 的寫入。但這個平均值隱藏了使用者粉絲數差異巨大這一現實,一些使用者有超過 3000 萬的粉絲,這意味著一條推文就可能會導致主頁時間線快取的 3000 萬次寫入!及時完成這種操作是一個巨大的挑戰 —— 推特嘗試在 5 秒內向粉絲傳送推文。
|
||||
|
||||
在推特的例子中,每個使用者粉絲數的分佈(可能按這些使用者的發推頻率來加權)是探討可伸縮性的一個關鍵負載引數,因為它決定了扇出負載。你的應用程式可能具有非常不同的特徵,但可以採用相似的原則來考慮它的負載。
|
||||
|
||||
推特軼事的最終轉折:現在已經穩健地實現了方法 2,推特逐步轉向了兩種方法的混合。大多數使用者發的推文會被扇出寫入其粉絲主頁時間線快取中。但是少數擁有海量粉絲的使用者(即名流)會被排除在外。當用戶讀取主頁時間線時,分別地獲取出該使用者所關注的每位名流的推文,再與使用者的主頁時間線快取合併,如方法 1 所示。這種混合方法能始終如一地提供良好效能。在 [第十二章](/tw/ch12) 中我們將重新討論這個例子,這在覆蓋更多技術層面之後。
|
||||
|
||||
### 描述效能
|
||||
|
||||
一旦系統的負載被描述好,就可以研究當負載增加會發生什麼。我們可以從兩種角度來看:
|
||||
|
||||
* 增加負載引數並保持系統資源(CPU、記憶體、網路頻寬等)不變時,系統性能將受到什麼影響?
|
||||
* 增加負載引數並希望保持效能不變時,需要增加多少系統資源?
|
||||
|
||||
這兩個問題都需要效能資料,所以讓我們簡單地看一下如何描述系統性能。
|
||||
|
||||
對於 Hadoop 這樣的批處理系統,通常關心的是 **吞吐量(throughput)**,即每秒可以處理的記錄數量,或者在特定規模資料集上執行作業的總時間 [^iii]。對於線上系統,通常更重要的是服務的 **響應時間(response time)**,即客戶端傳送請求到接收響應之間的時間。
|
||||
|
||||
[^iii]: 理想情況下,批次作業的執行時間是資料集的大小除以吞吐量。在實踐中由於資料傾斜(資料不是均勻分佈在每個工作程序中),需要等待最慢的任務完成,所以執行時間往往更長。
|
||||
|
||||
> #### 延遲和響應時間
|
||||
> [!TIP] 早期版本讀者須知
|
||||
> 透過早期版本的電子書,你可以在作者寫作時就獲得最原始、未經編輯的內容——這樣你就能在這些技術正式釋出之前很久就利用它們。
|
||||
>
|
||||
> **延遲(latency)** 和 **響應時間(response time)** 經常用作同義詞,但實際上它們並不一樣。響應時間是客戶所看到的,除了實際處理請求的時間( **服務時間(service time)** )之外,還包括網路延遲和排隊延遲。延遲是某個請求等待處理的 **持續時長**,在此期間它處於 **休眠(latent)** 狀態,並等待服務【17】。
|
||||
> 這將是最終書籍的第一章。本書的 GitHub 倉庫是 https://github.com/ept/ddia2-feedback。
|
||||
> 如果你想積極參與審閱和評論這份草稿,請在 GitHub 上聯絡。
|
||||
|
||||
即使不斷重複傳送同樣的請求,每次得到的響應時間也都會略有不同。現實世界的系統會處理各式各樣的請求,響應時間可能會有很大差異。因此我們需要將響應時間視為一個可以測量的數值 **分佈(distribution)**,而不是單個數值。
|
||||
資料是當今許多應用程式開發的核心。隨著 Web 和移動應用、軟體即服務(SaaS)以及雲服務的出現,將來自許多不同使用者的資料儲存在共享的基於伺服器的資料基礎設施中已經變得很正常。來自使用者活動、業務交易、裝置和感測器的資料需要被儲存並可供分析使用。當用戶與應用程式互動時,他們既讀取儲存的資料,也生成更多的資料。
|
||||
|
||||
在 [圖 1-4](/img/fig1-4.png) 中,每個灰條代表一次對服務的請求,其高度表示請求花費了多長時間。大多數請求是相當快的,但偶爾會出現需要更長的時間的異常值。這也許是因為緩慢的請求實質上開銷更大,例如它們可能會處理更多的資料。但即使(你認為)所有請求都花費相同時間的情況下,隨機的附加延遲也會導致結果變化,例如:上下文切換到後臺程序,網路資料包丟失與 TCP 重傳,垃圾收集暫停,強制從磁碟讀取的頁面錯誤,伺服器機架中的震動【18】,還有很多其他原因。
|
||||
少量資料可以在單臺機器上儲存和處理,通常相當容易處理。然而,隨著資料量或查詢速率的增長,需要將其分佈在多臺機器上,這帶來了許多挑戰。隨著應用程式需求變得更加複雜,僅將所有內容儲存在一個系統中已經不夠,可能需要組合多個提供不同功能的儲存或處理系統。
|
||||
|
||||

|
||||
如果資料管理是開發應用程式的主要挑戰之一,我們稱該應用程式為 **資料密集型**(data-intensive)[^1]。
|
||||
雖然在 **計算密集型**(compute-intensive)系統中的挑戰是並行化某些非常大的計算,但在資料密集型應用中,我們通常更擔心諸如儲存和處理大資料量、管理資料變更、確保在故障和併發情況下的一致性以及確保服務高可用等問題。
|
||||
|
||||
**圖 1-4 展示了一個服務 100 次請求響應時間的均值與百分位數**
|
||||
這些應用程式通常由提供常用功能的標準構建塊構建而成。例如,許多應用程式需要:
|
||||
|
||||
通常報表都會展示服務的平均響應時間。(嚴格來講 “平均” 一詞並不指代任何特定公式,但實際上它通常被理解為 **算術平均值(arithmetic mean)**:給定 n 個值,加起來除以 n )。然而如果你想知道 “**典型(typical)**” 響應時間,那麼平均值並不是一個非常好的指標,因為它不能告訴你有多少使用者實際上經歷了這個延遲。
|
||||
* 儲存資料,以便它們或另一個應用程式以後可以再次找到它(**資料庫**)
|
||||
* 記住昂貴操作的結果,以加快讀取速度(**快取**)
|
||||
* 允許使用者按關鍵字搜尋資料或以各種方式過濾資料(**搜尋索引**)
|
||||
* 處理事件和資料變更(**流處理**)
|
||||
* 定期處理大量累積的資料(**批處理**)
|
||||
|
||||
通常使用 **百分位點(percentiles)** 會更好。如果將響應時間列表按最快到最慢排序,那麼 **中位數(median)** 就在正中間:舉個例子,如果你的響應時間中位數是 200 毫秒,這意味著一半請求的返回時間少於 200 毫秒,另一半比這個要長。
|
||||
在構建應用程式時,我們通常會採用幾個軟體系統或服務,例如資料庫或 API,並用一些應用程式程式碼將它們粘合在一起。如果你正在做這些資料系統設計的目標,那麼這個過程可能會很容易。
|
||||
|
||||
如果想知道典型場景下使用者需要等待多長時間,那麼中位數是一個好的度量標準:一半使用者請求的響應時間少於響應時間的中位數,另一半服務時間比中位數長。中位數也被稱為第 50 百分位點,有時縮寫為 p50。注意中位數是關於單個請求的;如果使用者同時發出幾個請求(在一個會話過程中,或者由於一個頁面中包含了多個資源),則至少一個請求比中位數慢的機率遠大於 50%。
|
||||
然而,隨著你的應用程式變得更加雄心勃勃,挑戰就會出現。有許多具有不同特性的資料庫系統,適合不同的目的——你如何選擇使用哪一個?有各種快取方法、構建搜尋索引的幾種方法等等——你如何權衡它們的利弊?你需要找出哪些工具和哪些方法最適合手頭的任務,當需要做單個工具無法單獨完成的事情時,組合工具可能會很困難。
|
||||
|
||||
為了弄清異常值有多糟糕,可以看看更高的百分位點,例如第 95、99 和 99.9 百分位點(縮寫為 p95,p99 和 p999)。它們意味著 95%、99% 或 99.9% 的請求響應時間要比該閾值快,例如:如果第 95 百分位點響應時間是 1.5 秒,則意味著 100 個請求中的 95 個響應時間快於 1.5 秒,而 100 個請求中的 5 個響應時間超過 1.5 秒。如 [圖 1-4](/img/fig1-4.png) 所示。
|
||||
本書是一本指南,幫助你決定使用哪些技術以及如何組合它們。正如你將看到的,沒有一種方法從根本上優於其他方法;每種方法都有利弊。透過本書,你將學會提出正確的問題來評估和比較資料系統,這樣你就能找出哪種方法最能滿足你特定應用程式的需求。
|
||||
|
||||
響應時間的高百分位點(也稱為 **尾部延遲**,即 **tail latencies**)非常重要,因為它們直接影響使用者的服務體驗。例如亞馬遜在描述內部服務的響應時間要求時是以 99.9 百分位點為準,即使它隻影響一千個請求中的一個。這是因為請求響應最慢的客戶往往也是資料最多的客戶,也可以說是最有價值的客戶 —— 因為他們掏錢了【19】。保證網站響應迅速對於保持客戶的滿意度非常重要,亞馬遜觀察到:響應時間增加 100 毫秒,銷售量就減少 1%【20】;而另一些報告說:慢 1 秒鐘會讓客戶滿意度指標減少 16%【21,22】。
|
||||
我們將透過檢視當今組織中資料的一些典型使用方式來開始我們的旅程。這裡的許多想法起源於 **企業軟體**(即大型組織(如大公司和政府)的軟體需求和工程實踐),因為歷史上只有大型組織擁有需要複雜技術解決方案的大資料量。如果你的資料量足夠小,你可以簡單地將其儲存在電子表格中!然而,最近小公司和初創企業管理大資料量並構建資料密集型系統也變得很常見。
|
||||
|
||||
另一方面,最佳化第 99.99 百分位點(一萬個請求中最慢的一個)被認為太昂貴了,不能為亞馬遜的目標帶來足夠好處。減小高百分位點處的響應時間相當困難,因為它很容易受到隨機事件的影響,這超出了控制範圍,而且效益也很小。
|
||||
資料系統的關鍵挑戰之一是不同的人需要用資料做非常不同的事情。如果你在一家公司工作,你和你的團隊會有一套優先事項,而另一個團隊可能有完全不同的目標,即使你們可能使用相同的資料集!此外,這些目標可能沒有明確表達,這可能導致關於正確方法的誤解和分歧。
|
||||
|
||||
百分位點通常用於 **服務級別目標(SLO, service level objectives)** 和 **服務級別協議(SLA, service level agreements)**,即定義服務預期效能和可用性的合同。SLA 可能會宣告,如果服務響應時間的中位數小於 200 毫秒,且 99.9 百分位點低於 1 秒,則認為服務工作正常(如果響應時間更長,就認為服務不達標)。這些指標為客戶設定了期望值,並允許客戶在 SLA 未達標的情況下要求退款。
|
||||
為了幫助你理解可以做出哪些選擇,本章比較了幾個對比的概念,並探討了它們的權衡:
|
||||
|
||||
**排隊延遲(queueing delay)** 通常佔了高百分位點處響應時間的很大一部分。由於伺服器只能並行處理少量的事務(如受其 CPU 核數的限制),所以只要有少量緩慢的請求就能阻礙後續請求的處理,這種效應有時被稱為 **頭部阻塞(head-of-line blocking)** 。即使後續請求在伺服器上處理的非常迅速,由於需要等待先前請求完成,客戶端最終看到的是緩慢的總體響應時間。因為存在這種效應,測量客戶端的響應時間非常重要。
|
||||
* 事務型系統和分析型系統之間的區別(["分析型與事務型系統"](/tw/ch1#sec_introduction_analytics));
|
||||
* 雲服務和自託管系統的利弊(["雲服務與自託管"](/tw/ch1#sec_introduction_cloud));
|
||||
* 何時從單節點系統轉向分散式系統(["分散式與單節點系統"](/tw/ch1#sec_introduction_distributed));以及
|
||||
* 平衡業務需求和使用者權利(["資料系統、法律與社會"](/tw/ch1#sec_introduction_compliance))。
|
||||
|
||||
為測試系統的可伸縮性而人為產生負載時,產生負載的客戶端要獨立於響應時間不斷傳送請求。如果客戶端在傳送下一個請求之前等待先前的請求完成,這種行為會產生人為排隊的效果,使得測試時的佇列比現實情況更短,使測量結果產生偏差【23】。
|
||||
此外,本章將為你提供本書其餘部分所需的術語。
|
||||
|
||||
> #### 實踐中的百分位點
|
||||
>
|
||||
> 在多重呼叫的後端服務裡,高百分位數變得特別重要。即使並行呼叫,終端使用者請求仍然需要等待最慢的並行呼叫完成。如 [圖 1-5](/img/fig1-5.png) 所示,只需要一個緩慢的呼叫就可以使整個終端使用者請求變慢。即使只有一小部分後端呼叫速度較慢,如果終端使用者請求需要多個後端呼叫,則獲得較慢呼叫的機會也會增加,因此較高比例的終端使用者請求速度會變慢(該效果稱為尾部延遲放大,即 tail latency amplification【24】)。
|
||||
>
|
||||
> 如果你想將響應時間百分點新增到你的服務的監視儀表板,則需要持續有效地計算它們。例如,你可以使用滑動視窗來跟蹤連續10分鐘內的請求響應時間。每一分鐘,你都會計算出該視窗中的響應時間中值和各種百分數,並將這些度量值繪製在圖上。
|
||||
>
|
||||
> 簡單的實現是在時間視窗內儲存所有請求的響應時間列表,並且每分鐘對列表進行排序。如果對你來說效率太低,那麼有一些演算法能夠以最小的 CPU 和記憶體成本(如前向衰減【25】、t-digest【26】或 HdrHistogram 【27】)來計算百分位數的近似值。請注意,平均百分比(例如,減少時間解析度或合併來自多臺機器的資料)在數學上沒有意義 - 聚合響應時間資料的正確方法是新增直方圖【28】。
|
||||
> [!TIP] 術語:前端和後端
|
||||
|
||||

|
||||
本書中我們將討論的大部分內容都與 **後端開發** 有關。為了解釋這個術語:對於 Web 應用程式,在 Web 瀏覽器中執行的客戶端程式碼稱為 **前端**,而處理使用者請求的伺服器端程式碼稱為 **後端**。移動應用類似於前端,因為它們提供使用者介面,通常透過網際網路與伺服器端後端通訊。前端有時會在使用者裝置上本地管理資料 [^2],但最大的資料基礎設施挑戰通常在於後端:前端只需要處理一個使用者的資料,而後端代表 **所有** 使用者管理資料。
|
||||
|
||||
**圖 1-5 當一個請求需要多個後端請求時,單個後端慢請求就會拖慢整個終端使用者的請求**
|
||||
後端服務通常可透過 HTTP(有時是 WebSocket)訪問;它通常由一些應用程式程式碼組成,這些程式碼在一個或多個數據庫中讀寫資料,有時還與其他資料系統(如快取或訊息佇列)介面(我們可能統稱為 **資料基礎設施**)。應用程式程式碼通常是 **無狀態的**(即,當它完成處理一個 HTTP 請求時,它會忘記關於該請求的所有內容),任何需要從一個請求持續到另一個請求的資訊都需要儲存在客戶端或伺服器端資料基礎設施中。
|
||||
|
||||
### 應對負載的方法
|
||||
|
||||
現在我們已經討論了用於描述負載的引數和用於衡量效能的指標。可以開始認真討論可伸縮性了:當負載引數增加時,如何保持良好的效能?
|
||||
## 分析型與事務型系統 {#sec_introduction_analytics}
|
||||
|
||||
適應某個級別負載的架構不太可能應付 10 倍於此的負載。如果你正在開發一個快速增長的服務,那麼每次負載發生數量級的增長時,你可能都需要重新考慮架構 —— 或者更頻繁。
|
||||
如果你在企業中從事資料系統工作,你可能會遇到幾種不同型別的資料工作人員。第一類是構建處理讀取和更新資料請求的服務的 **後端工程師**;這些服務通常直接或透過其他服務間接地為外部使用者提供服務(參見["微服務和無伺服器"](/tw/ch1#sec_introduction_microservices))。有時服務是供組織其他部分內部使用的。
|
||||
|
||||
人們經常討論 **縱向伸縮**(scaling up,也稱為垂直伸縮,即 vertical scaling,轉向更強大的機器)和 **橫向伸縮**(scaling out,也稱為水平伸縮,即 horizontal scaling,將負載分佈到多臺小機器上)之間的對立。跨多臺機器分配負載也稱為 “**無共享(shared-nothing)**” 架構。可以在單臺機器上執行的系統通常更簡單,但高階機器可能非常貴,所以非常密集的負載通常無法避免地需要橫向伸縮。現實世界中的優秀架構需要將這兩種方法務實地結合,因為使用幾臺足夠強大的機器可能比使用大量的小型虛擬機器更簡單也更便宜。
|
||||
除了管理後端服務的團隊外,通常還有另外兩組人需要訪問組織的資料:**業務分析師**,他們生成有關組織活動的報告,以幫助管理層做出更好的決策(**商業智慧** 或 **BI**),以及 **資料科學家**,他們在資料中尋找新穎的見解或建立由資料分析和機器學習/AI 支援的面向使用者的產品功能(例如,電子商務網站上的"購買了 X 的人也購買了 Y"推薦、預測分析如風險評分或垃圾郵件過濾,以及搜尋結果排名)。
|
||||
|
||||
有些系統是 **彈性(elastic)** 的,這意味著可以在檢測到負載增加時自動增加計算資源,而其他系統則是手動伸縮(人工分析容量並決定向系統新增更多的機器)。如果負載 **極難預測(highly unpredictable)**,則彈性系統可能很有用,但手動伸縮系統更簡單,並且意外操作可能會更少(請參閱 “[分割槽再平衡](/tw/ch6#分割槽再平衡)”)。
|
||||
雖然業務分析師和資料科學家傾向於使用不同的工具並以不同的方式操作,但他們有一些共同點:兩者都執行 **分析**,這意味著他們檢視使用者和後端服務生成的資料,但他們通常不修改這些資料(除了可能修復錯誤)。他們可能會建立派生資料集,其中原始資料已經以某種方式處理過。這導致了兩種系統型別之間的分離——這種區別將貫穿本書:
|
||||
|
||||
跨多臺機器部署 **無狀態服務(stateless services)** 非常簡單,但將帶狀態的資料系統從單節點變為分散式配置則可能引入許多額外複雜度。出於這個原因,常識告訴我們應該將資料庫放在單個節點上(縱向伸縮),直到伸縮成本或可用性需求迫使其改為分散式。
|
||||
* **事務型系統** 由後端服務和資料基礎設施組成,在這裡建立資料,例如透過為外部使用者提供服務。在這裡,應用程式程式碼基於使用者執行的操作讀取和修改其資料庫中的資料。
|
||||
* **分析型系統** 服務於業務分析師和資料科學家的需求。它們包含來自事務型系統的只讀資料副本,並針對分析所需的資料處理型別進行了最佳化。
|
||||
|
||||
隨著分散式系統的工具和抽象越來越好,至少對於某些型別的應用而言,這種常識可能會改變。可以預見分散式資料系統將成為未來的預設設定,即使對不處理大量資料或流量的場景也如此。本書的其餘部分將介紹多種分散式資料系統,不僅討論它們在可伸縮性方面的表現,還包括易用性和可維護性。
|
||||
正如我們將在下一節看到的,事務型和分析型系統通常由於充分的理由而保持分離。隨著這些系統的成熟,出現了兩個新的專門角色:**資料工程師** 和 **分析工程師**。資料工程師是知道如何整合事務型和分析型系統的人,並且更廣泛地負責組織的資料基礎設施 [^3]。
|
||||
分析工程師對資料進行建模和轉換,使其對組織中的業務分析師和資料科學家更有用 [^4]。
|
||||
|
||||
大規模的系統架構通常是應用特定的 —— 沒有一招鮮吃遍天的通用可伸縮架構(不正式的叫法:**萬金油(magic scaling sauce)** )。應用的問題可能是讀取量、寫入量、要儲存的資料量、資料的複雜度、響應時間要求、訪問模式或者所有問題的大雜燴。
|
||||
許多工程師專門從事事務型或分析型方面。然而,本書涵蓋了事務型和分析型資料系統,因為兩者在組織內資料的生命週期中都發揮著重要作用。我們將深入探討用於向內部和外部使用者提供服務的資料基礎設施,以便你能夠更好地與分界線另一邊的同事合作。
|
||||
|
||||
舉個例子,用於處理每秒十萬個請求(每個大小為 1 kB)的系統與用於處理每分鐘 3 個請求(每個大小為 2GB)的系統看上去會非常不一樣,儘管兩個系統有同樣的資料吞吐量。
|
||||
### 事務處理與分析的特徵 {#sec_introduction_oltp}
|
||||
|
||||
一個良好適配應用的可伸縮架構,是圍繞著 **假設(assumption)** 建立的:哪些操作是常見的?哪些操作是罕見的?這就是所謂負載引數。如果假設最終是錯誤的,那麼為伸縮所做的工程投入就白費了,最糟糕的是適得其反。在早期創業公司或非正式產品中,通常支援產品快速迭代的能力,要比可伸縮至未來的假想負載要重要的多。
|
||||
在商業資料處理的早期,對資料庫的寫入通常對應於正在發生的 **商業交易**:進行銷售、向供應商下訂單、支付員工工資等。隨著資料庫擴充套件到不涉及資金交換的領域,術語 **事務** 仍然保留了下來,指的是形成邏輯單元的一組讀寫操作。
|
||||
|
||||
儘管這些架構是應用程式特定的,但可伸縮的架構通常也是從通用的積木塊搭建而成的,並以常見的模式排列。在本書中,我們將討論這些構件和模式。
|
||||
> [!NOTE]
|
||||
> [第 8 章](/tw/ch8#ch_transactions) 詳細探討了我們所說的事務的含義。本章寬泛地使用該術語來指代低延遲的讀寫操作。
|
||||
|
||||
儘管資料庫開始用於許多不同型別的資料——社交媒體上的帖子、遊戲中的動作、地址簿中的聯絡人等等——基本訪問模式仍然類似於處理商業交易。事務型系統通常透過某個鍵查詢少量記錄(這稱為 **點查詢**)。基於使用者的輸入插入、更新或刪除記錄。因為這些應用程式是互動式的,這種訪問模式被稱為 **聯機事務處理**(OLTP)。
|
||||
|
||||
## 可維護性
|
||||
然而,資料庫也越來越多地用於分析,與 OLTP 相比具有非常不同的訪問模式。通常,分析查詢會掃描大量記錄,並計算聚合統計資訊(如計數、總和或平均值),而不是將單個記錄返回給使用者。例如,超市連鎖店的業務分析師可能想要回答以下分析查詢:
|
||||
|
||||
眾所周知,軟體的大部分開銷並不在最初的開發階段,而是在持續的維護階段,包括修復漏洞、保持系統正常執行、調查失效、適配新的平臺、為新的場景進行修改、償還技術債和新增新的功能。
|
||||
* 我們每家商店在一月份的總收入是多少?
|
||||
* 在我們最近的促銷期間,我們賣出的香蕉比平時多了多少?
|
||||
* 哪個品牌的嬰兒食品最常與品牌 X 紙尿褲一起購買?
|
||||
|
||||
不幸的是,許多從事軟體系統行業的人不喜歡維護所謂的 **遺留(legacy)** 系統,—— 也許因為涉及修復其他人的錯誤、和過時的平臺打交道,或者系統被迫使用於一些份外工作。每一個遺留系統都以自己的方式讓人不爽,所以很難給出一個通用的建議來和它們打交道。
|
||||
這些查詢產生的報告對商業智慧很重要,幫助管理層決定下一步做什麼。為了將這種使用資料庫的模式與事務處理區分開來,它被稱為 **聯機分析處理**(OLAP)[^5]。
|
||||
OLTP 和分析之間的區別並不總是明確的,但一些典型特徵列在[表 1-1](/tw/ch1#tab_oltp_vs_olap) 中。
|
||||
|
||||
但是我們可以,也應該以這樣一種方式來設計軟體:在設計之初就儘量考慮儘可能減少維護期間的痛苦,從而避免自己的軟體系統變成遺留系統。為此,我們將特別關注軟體系統的三個設計原則:
|
||||
{{< figure id="tab_oltp_vs_olap" title="表 1-1. 比較事務型和分析型系統的特徵" class="w-full my-4" >}}
|
||||
|
||||
* 可操作性(Operability)
|
||||
| 屬性 | 事務型系統(OLTP) | 分析型系統(OLAP) |
|
||||
|--------|-----------------|---------------|
|
||||
| 主要讀取模式 | 點查詢(按鍵獲取單個記錄) | 對大量記錄進行聚合 |
|
||||
| 主要寫入模式 | 建立、更新和刪除單個記錄 | 批次匯入(ETL)或事件流 |
|
||||
| 人類使用者示例 | Web/移動應用程式的終端使用者 | 內部分析師,用於決策支援 |
|
||||
| 機器使用示例 | 檢查操作是否被授權 | 檢測欺詐/濫用模式 |
|
||||
| 查詢型別 | 固定的查詢集,由應用程式預定義 | 分析師可以進行任意查詢 |
|
||||
| 資料代表 | 資料的最新狀態(當前時間點) | 隨時間發生的事件歷史 |
|
||||
| 資料集大小 | GB 到 TB | TB 到 PB |
|
||||
|
||||
便於運維團隊保持系統平穩執行。
|
||||
> [!NOTE]
|
||||
> **OLAP** 中 **聯機** 的含義不清楚;它可能指的是查詢不僅用於預定義的報告,而且分析師使用 OLAP 系統互動式地進行探索性查詢。
|
||||
|
||||
* 簡單性(Simplicity)
|
||||
對於事務型系統,通常不允許使用者構建自定義 SQL 查詢並在資料庫上執行它們,因為這可能會允許他們讀取或修改他們無權訪問的資料。此外,他們可能會編寫執行成本昂貴的查詢,從而影響其他使用者的資料庫效能。出於這些原因,OLTP 系統主要執行嵌入到應用程式程式碼中的固定查詢集,僅偶爾使用一次性自定義查詢進行維護或故障排除。另一方面,分析資料庫通常賦予使用者手動編寫任意 SQL 查詢的自由,或使用資料視覺化或儀表板工具(如 Tableau、Looker 或 Microsoft Power BI)自動生成查詢。
|
||||
|
||||
從系統中消除儘可能多的 **複雜度(complexity)**,使新工程師也能輕鬆理解系統(注意這和使用者介面的簡單性不一樣)。
|
||||
還有一種專為分析工作負載(聚合多條記錄的查詢)設計但嵌入到面向使用者產品中的系統型別。這一類別被稱為 **產品分析** 或 **即時分析**,專為此類用途設計的系統包括 Pinot、Druid 和 ClickHouse [^6]。
|
||||
|
||||
* 可演化性(evolvability)
|
||||
### 資料倉庫 {#sec_introduction_dwh}
|
||||
|
||||
使工程師在未來能輕鬆地對系統進行更改,當需求變化時為新應用場景做適配。也稱為 **可擴充套件性(extensibility)**、**可修改性(modifiability)** 或 **可塑性(plasticity)**。
|
||||
起初,相同的資料庫用於事務處理和分析查詢。SQL 在這方面相當靈活:它對兩種型別的查詢都很有效。然而,在 20 世紀 80 年代末和 90 年代初,公司停止將其 OLTP 系統用於分析目的,轉而在單獨的資料庫系統上執行分析的趨勢出現了。這個單獨的資料庫被稱為 **資料倉庫**。
|
||||
|
||||
和之前提到的可靠性、可伸縮性一樣,實現這些目標也沒有簡單的解決方案。不過我們會試著想象具有可操作性,簡單性和可演化性的系統會是什麼樣子。
|
||||
一家大型企業可能有幾十個甚至幾百個聯機事務處理系統:為面向客戶的網站提供支援的系統、控制實體店中的銷售點(收銀臺)系統、跟蹤倉庫庫存、規劃車輛路線、管理供應商、管理員工以及執行許多其他任務的系統。這些系統中的每一個都很複雜,需要一個團隊來維護它,因此這些系統最終大多獨立於彼此執行。
|
||||
|
||||
### 可操作性:人生苦短,關愛運維
|
||||
由於幾個原因,業務分析師和資料科學家直接查詢這些 OLTP 系統通常是不可取的:
|
||||
|
||||
有人認為,“良好的運維經常可以繞開垃圾(或不完整)軟體的侷限性,而再好的軟體攤上垃圾運維也沒法可靠執行”。儘管運維的某些方面可以,而且應該是自動化的,但在最初建立正確運作的自動化機制仍然取決於人。
|
||||
* 感興趣的資料可能分散在多個事務型系統中,使得在單個查詢中組合這些資料集變得困難(這個問題被稱為 **資料孤島**);
|
||||
* 適合 OLTP 的模式和資料佈局型別不太適合分析(參見["用於分析的星型和雪花型模式"](/tw/ch3#sec_datamodels_analytics));
|
||||
* 分析查詢可能相當昂貴,在 OLTP 資料庫上執行它們會影響其他使用者的效能;以及
|
||||
* OLTP 系統可能位於使用者出於安全或合規原因不允許直接訪問的單獨網路中。
|
||||
|
||||
運維團隊對於保持軟體系統順利執行至關重要。一個優秀運維團隊的典型職責如下(或者更多)【29】:
|
||||
相比之下,**資料倉庫** 是一個單獨的資料庫,分析師可以隨心所欲地查詢,而不會影響 OLTP 操作 [^7]。
|
||||
正如我們將在[第 4 章](/tw/ch4#ch_storage)中看到的,資料倉庫通常以與 OLTP 資料庫非常不同的方式儲存資料,以最佳化分析中常見的查詢型別。
|
||||
|
||||
* 監控系統的執行狀況,並在服務狀態不佳時快速恢復服務。
|
||||
* 跟蹤問題的原因,例如系統故障或效能下降。
|
||||
* 及時更新軟體和平臺,比如安全補丁。
|
||||
* 瞭解系統間的相互作用,以便在異常變更造成損失前進行規避。
|
||||
* 預測未來的問題,並在問題出現之前加以解決(例如,容量規劃)。
|
||||
* 建立部署、配置、管理方面的良好實踐,編寫相應工具。
|
||||
* 執行複雜的維護任務,例如將應用程式從一個平臺遷移到另一個平臺。
|
||||
* 當配置變更時,維持系統的安全性。
|
||||
* 定義工作流程,使運維操作可預測,並保持生產環境穩定。
|
||||
* 鐵打的營盤流水的兵,維持組織對系統的瞭解。
|
||||
資料倉庫包含公司中所有各種 OLTP 系統中資料的只讀副本。資料從 OLTP 資料庫中提取(使用定期資料轉儲或連續更新流),轉換為分析友好的模式,清理,然後載入到資料倉庫中。將資料匯入資料倉庫的過程稱為 **提取-轉換-載入**(ETL),如[圖 1-1](/tw/ch1#fig_dwh_etl) 所示。有時 **轉換** 和 **載入** 步驟的順序會交換(即,轉換在資料倉庫中完成,在載入之後),導致 **ELT**。
|
||||
|
||||
良好的可操作性意味著更輕鬆的日常工作,進而運維團隊能專注於高價值的事情。資料系統可以透過各種方式使日常任務更輕鬆:
|
||||
{{< figure src="/fig/ddia_0101.png" id="fig_dwh_etl" caption="圖 1-1. ETL 到資料倉庫的簡化概述。" class="w-full my-4" >}}
|
||||
|
||||
* 透過良好的監控,提供對系統內部狀態和執行時行為的 **可見性(visibility)**。
|
||||
* 為自動化提供良好支援,將系統與標準化工具相整合。
|
||||
* 避免依賴單臺機器(在整個系統繼續不間斷執行的情況下允許機器停機維護)。
|
||||
* 提供良好的文件和易於理解的操作模型(“如果做 X,會發生 Y”)。
|
||||
* 提供良好的預設行為,但需要時也允許管理員自由覆蓋預設值。
|
||||
* 有條件時進行自我修復,但需要時也允許管理員手動控制系統狀態。
|
||||
* 行為可預測,最大限度減少意外。
|
||||
在某些情況下,ETL 過程的資料來源是外部 SaaS 產品,如客戶關係管理(CRM)、電子郵件營銷或信用卡處理系統。在這些情況下,你無法直接訪問原始資料庫,因為它只能透過軟體供應商的 API 訪問。將這些外部系統的資料帶入你自己的資料倉庫可以實現透過 SaaS API 無法進行的分析。用於 SaaS API 的 ETL 通常由專門的資料聯結器服務(如 Fivetran、Singer 或 AirByte)實現。
|
||||
|
||||
一些資料庫系統提供 **混合事務/分析處理**(HTAP),旨在在單個系統中啟用 OLTP 和分析,而無需從一個系統到另一個系統的 ETL [^8] [^9]。
|
||||
然而,許多 HTAP 系統內部由一個 OLTP 系統和一個單獨的分析系統組成,隱藏在一個公共介面後面——因此兩者之間的區別對於理解這些系統的工作原理仍然很重要。
|
||||
|
||||
### 簡單性:管理複雜度
|
||||
此外,即使 HTAP 存在,由於不同的目標和要求,事務型和分析型系統之間的分離也很常見。特別是,每個事務型系統都有自己的資料庫被認為是良好的實踐(參見["微服務和無伺服器"](/tw/ch1#sec_introduction_microservices)),導致數百個獨立的事務型資料庫;另一方面,企業通常只有一個數據倉庫,以便業務分析師可以在單個查詢中組合來自多個事務型系統的資料。
|
||||
|
||||
小型軟體專案可以使用簡單討喜的、富表現力的程式碼,但隨著專案越來越大,程式碼往往變得非常複雜,難以理解。這種複雜度拖慢了所有系統相關人員,進一步增加了維護成本。一個陷入複雜泥潭的軟體專案有時被描述為 **爛泥潭(a big ball of mud)** 【30】。
|
||||
因此,HTAP 並不能取代資料倉庫。相反,它在同一個應用程式既需要執行掃描大量行的分析查詢,又需要以低延遲讀取和更新單個記錄的場景中很有用。例如,欺詐檢測可能涉及此類工作負載 [^10]。
|
||||
|
||||
**複雜度(complexity)** 有各種可能的症狀,例如:狀態空間激增、模組間緊密耦合、糾結的依賴關係、不一致的命名和術語、解決效能問題的 Hack、需要繞開的特例等等,現在已經有很多關於這個話題的討論【31,32,33】。
|
||||
事務型和分析型系統之間的分離是更廣泛趨勢的一部分:隨著工作負載變得更加苛刻,系統變得更加專業化並針對特定工作負載進行最佳化。通用系統可以輕鬆處理小資料量,但規模越大,系統往往變得越專業化 [^11]。
|
||||
|
||||
因為複雜度導致維護困難時,預算和時間安排通常會超支。在複雜的軟體中進行變更,引入錯誤的風險也更大:當開發人員難以理解系統時,隱藏的假設、無意的後果和意外的互動就更容易被忽略。相反,降低複雜度能極大地提高軟體的可維護性,因此簡單性應該是構建系統的一個關鍵目標。
|
||||
#### 從資料倉庫到資料湖 {#from-data-warehouse-to-data-lake}
|
||||
|
||||
簡化系統並不一定意味著減少功能;它也可以意味著消除 **額外的(accidental)** 的複雜度。Moseley 和 Marks【32】把 **額外複雜度** 定義為:由具體實現中湧現,而非(從使用者視角看,系統所解決的)問題本身固有的複雜度。
|
||||
資料倉庫通常使用透過 SQL 查詢的 **關係** 資料模型(參見[第 3 章](/tw/ch3#ch_datamodels)),可能使用專門的商業智慧軟體。這個模型非常適合業務分析師需要進行的查詢型別,但不太適合資料科學家的需求,他們可能需要執行以下任務:
|
||||
|
||||
用於消除 **額外複雜度** 的最好工具之一是 **抽象(abstraction)**。一個好的抽象可以將大量實現細節隱藏在一個乾淨,簡單易懂的外觀下面。一個好的抽象也可以廣泛用於各類不同應用。比起重複造很多輪子,重用抽象不僅更有效率,而且有助於開發高質量的軟體。抽象元件的質量改進將使所有使用它的應用受益。
|
||||
* 將資料轉換為適合訓練機器學習模型的形式;通常這需要將資料庫表的行和列轉換為稱為 **特徵** 的數值向量或矩陣。以最大化訓練模型效能的方式執行此轉換的過程稱為 **特徵工程**,它通常需要難以使用 SQL 表達的自定義程式碼。
|
||||
* 獲取文字資料(例如,產品評論)並使用自然語言處理技術嘗試從中提取結構化資訊(例如,作者的情感或他們提到的主題)。同樣,他們可能需要使用計算機視覺技術從照片中提取結構化資訊。
|
||||
|
||||
例如,高階程式語言是一種抽象,隱藏了機器碼、CPU 暫存器和系統呼叫。SQL 也是一種抽象,隱藏了複雜的磁碟 / 記憶體資料結構、來自其他客戶端的併發請求、崩潰後的不一致性。當然在用高階語言程式設計時,我們仍然用到了機器碼;只不過沒有 **直接(directly)** 使用罷了,正是因為程式語言的抽象,我們才不必去考慮這些實現細節。
|
||||
雖然已經努力將機器學習運算子新增到 SQL 資料模型 [^12] 並在關係基礎上構建高效的機器學習系統 [^13],但許多資料科學家更喜歡不在關係資料庫(如資料倉庫)中工作。相反,許多人更喜歡使用 Python 資料分析庫(如 pandas 和 scikit-learn)、統計分析語言(如 R)以及分散式分析框架(如 Spark)[^14]。
|
||||
我們將在["資料框、矩陣和陣列"](/tw/ch3#sec_datamodels_dataframes)中進一步討論這些。
|
||||
|
||||
抽象可以幫助我們將系統的複雜度控制在可管理的水平,不過,找到好的抽象是非常困難的。在分散式系統領域雖然有許多好的演算法,但我們並不清楚它們應該打包成什麼樣抽象。
|
||||
因此,組織面臨著以適合資料科學家使用的形式提供資料的需求。答案是 **資料湖**:一箇中央資料儲存庫,儲存可能對分析有用的任何資料的副本,透過 ETL 過程從事務型系統獲得。與資料倉庫的區別在於,資料湖只包含檔案,而不強加任何特定的檔案格式或資料模型。資料湖中的檔案可能是資料庫記錄的集合,使用檔案格式(如 Avro 或 Parquet)編碼(參見[第 5 章](/tw/ch5#ch_encoding)),但它們同樣可以包含文字、影像、影片、感測器讀數、稀疏矩陣、特徵向量、基因組序列或任何其他型別的資料 [^15]。
|
||||
除了更加靈活之外,這通常也比關係資料儲存更便宜,因為資料湖可以使用商品化的檔案儲存,如物件儲存(參見["雲原生系統架構"](/tw/ch1#sec_introduction_cloud_native))。
|
||||
|
||||
本書將緊盯那些允許我們將大型系統的部分提取為定義明確的、可重用的元件的優秀抽象。
|
||||
ETL 過程已經被推廣為 **資料管道**,在某些情況下,資料湖已成為從事務型系統到資料倉庫路徑上的中間站。資料湖包含由事務型系統產生的"原始"形式的資料,沒有轉換為關係資料倉庫模式。這種方法的優點是資料的每個消費者都可以將原始資料轉換為最適合其需求的形式。它被稱為 **壽司原則**:"原始資料更好"[^16]。
|
||||
|
||||
### 可演化性:擁抱變化
|
||||
除了將資料從資料湖載入到單獨的資料倉庫之外,還可以直接在資料湖中的檔案上執行典型的資料倉庫工作負載(SQL 查詢和業務分析),以及資料科學/機器學習工作負載。這種架構被稱為 **資料湖倉**,它需要一個查詢執行引擎和一個元資料(例如,模式管理)層來擴充套件資料湖的檔案儲存 [^17]。
|
||||
|
||||
系統的需求永遠不變,基本是不可能的。更可能的情況是,它們處於常態的變化中,例如:你瞭解了新的事實、出現意想不到的應用場景、業務優先順序發生變化、使用者要求新功能、新平臺取代舊平臺、法律或監管要求發生變化、系統增長迫使架構變化等。
|
||||
Apache Hive、Spark SQL、Presto 和 Trino 是這種方法的示例。
|
||||
|
||||
在組織流程方面,**敏捷(agile)** 工作模式為適應變化提供了一個框架。敏捷社群還開發了對在頻繁變化的環境中開發軟體很有幫助的技術工具和模式,如 **測試驅動開發(TDD, test-driven development)** 和 **重構(refactoring)** 。
|
||||
#### 超越資料湖 {#beyond-the-data-lake}
|
||||
|
||||
這些敏捷技術的大部分討論都集中在相當小的規模(同一個應用中的幾個程式碼檔案)。本書將探索在更大資料系統層面上提高敏捷性的方法,可能由幾個不同的應用或服務組成。例如,為了將裝配主頁時間線的方法從方法 1 變為方法 2,你會如何 “重構” 推特的架構 ?
|
||||
隨著分析實踐的成熟,組織越來越關注分析系統和資料管道的管理和操作,例如 DataOps 宣言中所捕獲的 [^18]。
|
||||
其中一部分是治理、隱私以及符合 GDPR 和 CCPA 等法規的問題,我們將在["資料系統、法律與社會"](/tw/ch1#sec_introduction_compliance)和[待補充連結]中討論。
|
||||
|
||||
修改資料系統並使其適應不斷變化需求的容易程度,是與 **簡單性** 和 **抽象性** 密切相關的:簡單易懂的系統通常比複雜系統更容易修改。但由於這是一個非常重要的概念,我們將用一個不同的詞來指代資料系統層面的敏捷性: **可演化性(evolvability)** 【34】。
|
||||
此外,分析資料越來越多地不僅作為檔案和關係表提供,還作為事件流提供(參見[待補充連結])。使用基於檔案的資料分析,你可以定期(例如,每天)重新執行分析以響應資料的變化,但流處理允許分析系統以秒級的速度響應事件。根據應用程式及其時間敏感性,流處理方法可能很有價值,例如識別和阻止潛在的欺詐或濫用活動。
|
||||
|
||||
在某些情況下,分析系統的輸出被提供給事務型系統(這個過程有時被稱為 **反向 ETL** [^19])。例如,在分析系統上訓練的機器學習模型可能會部署到生產環境中,以便它可以為終端使用者生成推薦,例如"購買了 X 的人也購買了 Y"。這種分析系統的部署輸出也被稱為 **資料產品** [^20]。
|
||||
機器學習模型可以使用專門的工具(如 TFX、Kubeflow 或 MLflow)部署到事務型系統。
|
||||
|
||||
## 本章小結
|
||||
### 權威資料來源與派生資料 {#sec_introduction_derived}
|
||||
|
||||
本章探討了一些關於資料密集型應用的基本思考方式。這些原則將指導我們閱讀本書的其餘部分,那裡將會深入技術細節。
|
||||
與事務型和分析型系統的區別相關,本書還區分了 **權威記錄系統** 和 **派生資料系統**。這些術語很有用,因為它們可以幫助你理清資料在系統中的流動:
|
||||
|
||||
一個應用必須滿足各種需求才稱得上有用。有一些 **功能需求**(functional requirements,即它應該做什麼,比如允許以各種方式儲存,檢索,搜尋和處理資料)以及一些 **非功能性需求**(nonfunctional,即通用屬性,例如安全性、可靠性、合規性、可伸縮性、相容性和可維護性)。在本章詳細討論了可靠性,可伸縮性和可維護性。
|
||||
權威記錄系統
|
||||
: 權威記錄系統(SoR, System of Record),也稱為 **權威資料來源**,儲存某些資料的權威或 **規範** 版本。當新資料進入時,例如作為使用者輸入,它首先被寫入這裡。每個事實只表示一次(表示通常是 **規範化的**;參見["規範化、反規範化和連線"](/tw/ch3#sec_datamodels_normalization))。如果另一個系統和權威記錄系統之間存在任何差異,那麼權威記錄系統中的值(按定義)是正確的。
|
||||
|
||||
派生資料系統
|
||||
: 派生系統中的資料是透過獲取另一個系統中的一些現有資料並以某種方式轉換或處理它的結果。如果你丟失了派生資料,你可以從原始源重新建立它。一個經典的例子是快取:如果存在,可以從快取中提供資料,但如果快取不包含你需要的內容,你可以回退到底層資料庫。反規範化的值、索引、物化檢視、轉換的資料表示以及在資料集上訓練的模型也屬於這一類別。
|
||||
|
||||
**可靠性(Reliability)** 意味著即使發生故障,系統也能正常工作。故障可能發生在硬體(通常是隨機的和不相關的)、軟體(通常是系統性的 Bug,很難處理)和人類(不可避免地時不時出錯)。**容錯技術** 可以對終端使用者隱藏某些型別的故障。
|
||||
從技術上講,派生資料是 **冗餘的**,因為它複製了現有資訊。然而,它對於在讀查詢上獲得良好效能通常是必不可少的。你可以從單個源派生幾個不同的資料集,使你能夠從不同的"視角"檢視資料。
|
||||
|
||||
**可伸縮性(Scalability)** 意味著即使在負載增加的情況下也有保持效能的策略。為了討論可伸縮性,我們首先需要定量描述負載和效能的方法。我們簡要了解了推特主頁時間線的例子,介紹描述負載的方法,並將響應時間百分位點作為衡量效能的一種方式。在可伸縮的系統中可以新增 **處理容量(processing capacity)** 以在高負載下保持可靠。
|
||||
分析系統通常是派生資料系統,因為它們是在其他地方建立的資料的消費者。事務型服務可能包含權威記錄系統和派生資料系統的混合。權威記錄系統是資料首次寫入的主要資料庫,而派生資料系統是加速常見讀取操作的索引和快取,特別是對於權威記錄系統無法有效回答的查詢。
|
||||
|
||||
**可維護性(Maintainability)** 有許多方面,但實質上是關於工程師和運維團隊的生活質量的。良好的抽象可以幫助降低複雜度,並使系統易於修改和適應新的應用場景。良好的可操作性意味著對系統的健康狀態具有良好的可見性,並擁有有效的管理手段。
|
||||
大多數資料庫、儲存引擎和查詢語言本質上不是權威記錄系統或派生系統。資料庫只是一個工具:如何使用它取決於你。權威記錄系統和派生資料系統之間的區別不取決於工具,而是取決於你在應用程式中如何使用它。透過明確哪些資料是從哪些其他資料派生的,你可以為原本令人困惑的系統架構帶來清晰度。
|
||||
|
||||
不幸的是,使應用可靠、可伸縮或可維護並不容易。但是某些模式和技術會不斷重新出現在不同的應用中。在接下來的幾章中,我們將看到一些資料系統的例子,並分析它們如何實現這些目標。
|
||||
當一個系統中的資料是從另一個系統中的資料派生的時,你需要一個過程來在權威記錄系統中的原始資料發生變化時更新派生資料。不幸的是,許多資料庫的設計基於你的應用程式只需要使用那一個數據庫的假設,它們不容易整合多個系統以傳播此類更新。在[待補充連結]中,我們將討論 **資料整合** 的方法,這允許我們組合多個數據系統來實現單個系統無法完成的事情。
|
||||
|
||||
在本書後面的 [第三部分](/tw/part-iii) 中,我們將看到一種模式:幾個元件協同工作以構成一個完整的系統(如 [圖 1-1](/img/fig1-1.png) 中的例子)
|
||||
這就是我們對分析和事務處理比較的結束。在下一節中,我們將研究另一個你可能已經看到多次辯論的權衡。
|
||||
|
||||
|
||||
## 參考文獻
|
||||
|
||||
1. Michael Stonebraker and Uğur Çetintemel: “['One Size Fits All': An Idea Whose Time Has Come and Gone](https://cs.brown.edu/~ugur/fits_all.pdf),” at *21st International Conference on Data Engineering* (ICDE), April 2005.
|
||||
1. Walter L. Heimerdinger and Charles B. Weinstock: “[A Conceptual Framework for System Fault Tolerance](https://resources.sei.cmu.edu/asset_files/TechnicalReport/1992_005_001_16112.pdf),” Technical Report CMU/SEI-92-TR-033, Software Engineering Institute, Carnegie Mellon University, October 1992.
|
||||
1. Ding Yuan, Yu Luo, Xin Zhuang, et al.: “[Simple Testing Can Prevent Most Critical Failures: An Analysis of Production Failures in Distributed Data-Intensive Systems](https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf),” at *11th USENIX Symposium on Operating Systems Design and Implementation* (OSDI), October 2014.
|
||||
1. Yury Izrailevsky and Ariel Tseitlin: “[The Netflix Simian Army](https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116),” *netflixtechblog.com*, July 19, 2011.
|
||||
1. Daniel Ford, François Labelle, Florentina I. Popovici, et al.: “[Availability in Globally Distributed Storage Systems](http://research.google.com/pubs/archive/36737.pdf),” at *9th USENIX Symposium on Operating Systems Design and Implementation* (OSDI), October 2010.
|
||||
1. Brian Beach: “[Hard Drive Reliability Update – Sep 2014](https://www.backblaze.com/blog/hard-drive-reliability-update-september-2014/),” *backblaze.com*, September 23, 2014.
|
||||
1. Laurie Voss: “[AWS: The Good, the Bad and the Ugly](https://web.archive.org/web/20160429075023/http://blog.awe.sm/2012/12/18/aws-the-good-the-bad-and-the-ugly/),” *blog.awe.sm*, December 18, 2012.
|
||||
1. Haryadi S. Gunawi, Mingzhe Hao, Tanakorn Leesatapornwongsa, et al.: “[What Bugs Live in the Cloud?](http://ucare.cs.uchicago.edu/pdf/socc14-cbs.pdf),” at *5th ACM Symposium on Cloud Computing* (SoCC), November 2014. [doi:10.1145/2670979.2670986](http://dx.doi.org/10.1145/2670979.2670986)
|
||||
1. Nelson Minar: “[Leap Second Crashes Half the Internet](http://www.somebits.com/weblog/tech/bad/leap-second-2012.html),” *somebits.com*, July 3, 2012.
|
||||
1. Amazon Web Services: “[Summary of the Amazon EC2 and Amazon RDS Service Disruption in the US East Region](http://aws.amazon.com/message/65648/),” *aws.amazon.com*, April 29, 2011.
|
||||
1. Richard I. Cook: “[How Complex Systems Fail](https://www.adaptivecapacitylabs.com/HowComplexSystemsFail.pdf),” Cognitive Technologies Laboratory, April 2000.
|
||||
1. Jay Kreps: “[Getting Real About Distributed System Reliability](http://blog.empathybox.com/post/19574936361/getting-real-about-distributed-system-reliability),” *blog.empathybox.com*, March 19, 2012.
|
||||
1. David Oppenheimer, Archana Ganapathi, and David A. Patterson: “[Why Do Internet Services Fail, and What Can Be Done About It?](http://static.usenix.org/legacy/events/usits03/tech/full_papers/oppenheimer/oppenheimer.pdf),” at *4th USENIX Symposium on Internet Technologies and Systems* (USITS), March 2003.
|
||||
1. Nathan Marz: “[Principles of Software Engineering, Part 1](http://nathanmarz.com/blog/principles-of-software-engineering-part-1.html),” *nathanmarz.com*, April 2, 2013.
|
||||
1. Michael Jurewitz: “[The Human Impact of Bugs](http://jury.me/blog/2013/3/14/the-human-impact-of-bugs),” *jury.me*, March 15, 2013.
|
||||
1. Raffi Krikorian: “[Timelines at Scale](http://www.infoq.com/presentations/Twitter-Timeline-Scalability),” at *QCon San Francisco*, November 2012.
|
||||
1. Martin Fowler: *Patterns of Enterprise Application Architecture*. Addison Wesley, 2002. ISBN: 978-0-321-12742-6
|
||||
1. Kelly Sommers: “[After all that run around, what caused 500ms disk latency even when we replaced physical server?](https://twitter.com/kellabyte/status/532930540777635840)” *twitter.com*, November 13, 2014.
|
||||
1. Giuseppe DeCandia, Deniz Hastorun, Madan Jampani, et al.: “[Dynamo: Amazon's Highly Available Key-Value Store](http://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf),” at *21st ACM Symposium on Operating Systems Principles* (SOSP), October 2007.
|
||||
1. Greg Linden: “[Make Data Useful](http://glinden.blogspot.co.uk/2006/12/slides-from-my-talk-at-stanford.html),” slides from presentation at Stanford University Data Mining class (CS345), December 2006.
|
||||
1. Tammy Everts: “[The Real Cost of Slow Time vs Downtime](https://www.slideshare.net/Radware/radware-cmg2014-tammyevertsslowtimevsdowntime),” *slideshare.net*, November 5, 2014.
|
||||
1. Jake Brutlag: “[Speed Matters](https://ai.googleblog.com/2009/06/speed-matters.html),” *ai.googleblog.com*, June 23, 2009.
|
||||
1. Tyler Treat: “[Everything You Know About Latency Is Wrong](http://bravenewgeek.com/everything-you-know-about-latency-is-wrong/),” *bravenewgeek.com*, December 12, 2015.
|
||||
1. Jeffrey Dean and Luiz André Barroso: “[The Tail at Scale](http://cacm.acm.org/magazines/2013/2/160173-the-tail-at-scale/fulltext),” *Communications of the ACM*, volume 56, number 2, pages 74–80, February 2013. [doi:10.1145/2408776.2408794](http://dx.doi.org/10.1145/2408776.2408794)
|
||||
1. Graham Cormode, Vladislav Shkapenyuk, Divesh Srivastava, and Bojian Xu: “[Forward Decay: A Practical Time Decay Model for Streaming Systems](http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf),” at *25th IEEE International Conference on Data Engineering* (ICDE), March 2009.
|
||||
1. Ted Dunning and Otmar Ertl: “[Computing Extremely Accurate Quantiles Using t-Digests](https://github.com/tdunning/t-digest),” *github.com*, March 2014.
|
||||
1. Gil Tene: “[HdrHistogram](http://www.hdrhistogram.org/),” *hdrhistogram.org*.
|
||||
1. Baron Schwartz: “[Why Percentiles Don’t Work the Way You Think](https://orangematter.solarwinds.com/2016/11/18/why-percentiles-dont-work-the-way-you-think/),” *solarwinds.com*, November 18, 2016.
|
||||
1. James Hamilton: “[On Designing and Deploying Internet-Scale Services](https://www.usenix.org/legacy/events/lisa07/tech/full_papers/hamilton/hamilton.pdf),” at *21st Large Installation System Administration Conference* (LISA), November 2007.
|
||||
1. Brian Foote and Joseph Yoder: “[Big Ball of Mud](http://www.laputan.org/pub/foote/mud.pdf),” at *4th Conference on Pattern Languages of Programs* (PLoP), September 1997.
|
||||
1. Frederick P Brooks: “No Silver Bullet – Essence and Accident in Software Engineering,” in *The Mythical Man-Month*, Anniversary edition, Addison-Wesley, 1995. ISBN: 978-0-201-83595-3
|
||||
1. Ben Moseley and Peter Marks: “[Out of the Tar Pit](https://curtclifton.net/papers/MoseleyMarks06a.pdf),” at *BCS Software Practice Advancement* (SPA), 2006.
|
||||
1. Rich Hickey: “[Simple Made Easy](http://www.infoq.com/presentations/Simple-Made-Easy),” at *Strange Loop*, September 2011.
|
||||
1. Hongyu Pei Breivold, Ivica Crnkovic, and Peter J. Eriksson: “[Analyzing Software Evolvability](http://www.es.mdh.se/pdf_publications/1251.pdf),” at *32nd Annual IEEE International Computer Software and Applications Conference* (COMPSAC), July 2008. [doi:10.1109/COMPSAC.2008.50](http://dx.doi.org/10.1109/COMPSAC.2008.50)
|
||||
## 雲服務與自託管 {#sec_introduction_cloud}
|
||||
|
||||
對於組織需要做的任何事情,首要問題之一是:應該在內部完成,還是應該外包?應該自建還是購買?
|
||||
|
||||
最終,這是一個關於業務優先順序的問題。管理智慧認為,作為組織核心競爭力或競爭優勢的事情應該在內部完成,而非核心、常規或普通的事情應該留給供應商 [^21]。
|
||||
舉一個極端的例子,大多數公司不自己發電(除非他們是能源公司,撇開緊急備用電源不談),因為從電網購買電力更便宜。
|
||||
|
||||
對於軟體,需要做出兩個重要決定:誰構建軟體以及誰部署它。外包每個決定的程度有多種可能性,如[圖 1-2](/tw/ch1#fig_cloud_spectrum) 所示。一個極端是你自己編寫和執行的定製軟體;另一個極端是廣泛使用的雲服務或軟體即服務(SaaS)產品,由外部供應商實施和運營,你只能透過 Web 介面或 API 訪問。
|
||||
|
||||
{{< figure src="/fig/ddia_0102.png" id="fig_cloud_spectrum" caption="圖 1-2. 軟體型別及其操作的範圍。" class="w-full my-4" >}}
|
||||
|
||||
中間地帶是你 **自託管** 的現成軟體(開源或商業),即自己部署——例如,如果你下載 MySQL 並將其安裝在你控制的伺服器上。這可能在你自己的硬體上(通常稱為 **本地部署**,即使伺服器實際上在租用的資料中心機架中,而不是字面上在你自己的場所),或者在雲中的虛擬機器上(**基礎設施即服務** 或 IaaS)。沿著這個範圍還有更多的點,例如,採用開源軟體並執行它的修改版本。
|
||||
|
||||
與這個範圍分開的還有 **如何** 部署服務的問題,無論是在雲中還是本地——例如,你是否使用像 Kubernetes 這樣的編排框架。然而,部署工具的選擇超出了本書的範圍,因為其他因素對資料系統的架構有更大的影響。
|
||||
|
||||
### 雲服務的利弊 {#sec_introduction_cloud_tradeoffs}
|
||||
|
||||
使用雲服務而不是自己執行類似的軟體,本質上是將該軟體的操作外包給雲提供商。有支援和反對雲服務的充分論據。雲提供商聲稱使用他們的服務可以節省你的時間和金錢,並且與設定自己的基礎設施相比,可以讓你更快地行動。
|
||||
|
||||
雲服務是否實際上比自託管更便宜、更容易,很大程度上取決於你的技能和系統的工作負載。如果你已經有設定和操作所需系統的經驗,並且你的負載相當可預測(即,你需要的機器數量不會劇烈波動),那麼購買自己的機器並自己執行軟體通常更便宜 [^22] [^23]。
|
||||
|
||||
另一方面,如果你需要一個你還不知道如何部署和操作的系統,那麼採用雲服務通常比自己學習管理系統更容易、更快。如果你必須專門僱用和培訓員工來維護和作業系統,那可能會變得非常昂貴。當你使用雲時,你仍然需要一個運維團隊(參見["雲時代的運維"](/tw/ch1#sec_introduction_operations)),但外包基本系統管理可以讓你的團隊專注於更高層次的問題。
|
||||
|
||||
當你將系統的操作外包給專門執行該服務的公司時,這可能會帶來更好的服務,因為提供商從為許多客戶提供服務中獲得了運營專業知識。另一方面,如果你自己執行服務,你可以配置和調整它以在你的特定工作負載上表現良好;雲服務不太可能願意代表你進行此類自定義。
|
||||
|
||||
如果你的系統負載隨時間變化很大,雲服務特別有價值。如果你配置機器以能夠處理峰值負載,但這些計算資源大部分時間都處於閒置狀態,系統就變得不太划算。在這種情況下,雲服務的優勢在於它們可以更容易地根據需求變化擴大或縮小你的計算資源。
|
||||
|
||||
例如,分析系統通常具有極其可變的負載:快速執行大型分析查詢需要並行的大量計算資源,但一旦查詢完成,這些資源就會閒置,直到使用者進行下一個查詢。預定義的查詢(例如,用於每日報告)可以排隊和排程以平滑負載,但對於互動式查詢,你希望它們完成得越快,工作負載就越可變。如果你的資料集如此之大,以至於快速查詢它需要大量的計算資源,使用雲可以節省資金,因為你可以將未使用的資源返回給提供商,而不是讓它們閒置。對於較小的資料集,這種差異不太顯著。
|
||||
|
||||
雲服務的最大缺點是你無法控制它:
|
||||
|
||||
* 如果它缺少你需要的功能,你所能做的就是禮貌地詢問供應商是否會新增它;你通常不能自己實現它。
|
||||
* 如果服務宕機,你所能做的就是等待它恢復。
|
||||
* 如果你使用服務的方式觸發了錯誤或導致效能問題,你將很難診斷問題。使用你自己執行的軟體,你可以從作業系統獲取效能指標和除錯資訊,以幫助你理解其行為,並且你可以檢視伺服器日誌,但使用供應商託管的服務,你通常無法訪問這些內部資訊。
|
||||
* 此外,如果服務關閉或變得不可接受地昂貴,或者如果供應商決定以你不喜歡的方式更改他們的產品,你將任由他們擺佈——繼續執行軟體的舊版本通常不是一個選項,因此你將被迫遷移到替代服務 [^24]。
|
||||
如果有其他服務公開相容的 API,這種風險會得到緩解,但對於許多雲服務,沒有標準 API,這提高了切換成本,使供應商鎖定成為一個問題。
|
||||
* 雲提供商需要被信任以保持資料安全,這可能會使遵守隱私和安全法規的過程複雜化。
|
||||
|
||||
儘管存在所有這些風險,組織在雲服務之上構建新應用程式或採用混合方法(雲服務用於系統的某些方面)變得越來越流行。然而,雲服務不會取代所有內部資料系統:許多較舊的系統早於雲,對於任何具有現有云服務無法滿足的專業要求的服務,內部系統仍然是必要的。例如,對延遲非常敏感的應用程式(如高頻交易)需要對硬體的完全控制。
|
||||
|
||||
### 雲原生系統架構 {#sec_introduction_cloud_native}
|
||||
|
||||
除了具有不同的經濟模型(訂閱服務而不是購買硬體和許可軟體在其上執行),雲的興起還對資料系統在技術層面上的實現產生了深遠的影響。術語 **雲原生** 用於描述旨在利用雲服務的架構。
|
||||
|
||||
原則上,幾乎任何你可以自託管的軟體也可以作為雲服務提供,實際上,許多流行的資料系統現在都有託管服務。然而,從頭開始設計為雲原生的系統已被證明具有幾個優勢:在相同硬體上具有更好的效能、更快地從故障中恢復、能夠快速擴充套件計算資源以匹配負載,以及支援更大的資料集 [^25] [^26] [^27]。
|
||||
[表 1-2](/tw/ch1#tab_cloud_native_dbs) 列出了兩種型別系統的一些示例。
|
||||
|
||||
{{< figure id="#tab_cloud_native_dbs" title="表 1-2. 自託管和雲原生資料庫系統的示例" class="w-full my-4" >}}
|
||||
|
||||
| 類別 | 自託管系統 | 雲原生系統 |
|
||||
|----------|---------------------------|---------------------------------------------------------------------|
|
||||
| 事務型/OLTP | MySQL、PostgreSQL、MongoDB | AWS Aurora [^25]、Azure SQL DB Hyperscale [^26]、Google Cloud Spanner |
|
||||
| 分析型/OLAP | Teradata、ClickHouse、Spark | Snowflake [^27]、Google BigQuery、Azure Synapse Analytics |
|
||||
|
||||
#### 雲服務的分層 {#layering-of-cloud-services}
|
||||
|
||||
許多自託管資料系統具有非常簡單的系統要求:它們在傳統作業系統(如 Linux 或 Windows)上執行,將資料作為檔案儲存在檔案系統上,並透過標準網路協議(如 TCP/IP)進行通訊。少數系統依賴於特殊硬體,如 GPU(用於機器學習)或 RDMA 網路介面,但總的來說,自託管軟體傾向於使用非常通用的計算資源:CPU、RAM、檔案系統和 IP 網路。
|
||||
|
||||
在雲中,這種型別的軟體可以在基礎設施即服務環境中執行,使用一個或多個具有一定 CPU、記憶體、磁碟和網路頻寬分配的虛擬機器(或 **例項**)。與物理機器相比,雲實例可以更快地配置,並且有更多種類的大小,但在其他方面它們類似於傳統計算機:你可以在其上執行任何你喜歡的軟體,但你負責自己管理它。
|
||||
|
||||
相比之下,雲原生服務的關鍵思想不僅是使用由你的作業系統管理的計算資源,還要基於較低級別的雲服務構建更高級別的服務。例如:
|
||||
|
||||
* **物件儲存** 服務(如 Amazon S3、Azure Blob Storage 和 Cloudflare R2)儲存大檔案。它們提供比典型檔案系統更有限的 API(基本檔案讀寫),但它們的優勢在於隱藏了底層物理機器:服務自動將資料分佈在許多機器上,因此你不必擔心任何一臺機器上的磁碟空間用完。即使某些機器或其磁碟完全故障,也不會丟失資料。
|
||||
* 許多其他服務又建立在物件儲存和其他雲服務之上:例如,Snowflake 是一個基於雲的分析資料庫(資料倉庫),它依賴於 S3 進行資料儲存 [^27],而其他一些服務又建立在 Snowflake 之上。
|
||||
|
||||
與計算中的抽象一樣,沒有一個你應該使用的正確答案。作為一般規則,更高級別的抽象往往更面向特定用例。如果你的需求與為其設計更高級別系統的情況相匹配,使用現有的更高級別系統可能會以比從較低級別系統自己構建它少得多的麻煩提供你需要的東西。另一方面,如果沒有滿足你需求的高階系統,那麼從較低級別的元件自己構建它是唯一的選擇。
|
||||
|
||||
#### 儲存與計算的分離 {#sec_introduction_storage_compute}
|
||||
|
||||
在傳統計算中,磁碟儲存被認為是持久的(我們假設一旦某些東西被寫入磁碟,它就不會丟失)。為了容忍單個硬碟的故障,RAID(獨立磁碟冗餘陣列)通常用於在連線到同一臺機器的多個磁碟上維護資料副本。RAID 可以在硬體中或由作業系統在軟體中執行,並且它對訪問檔案系統的應用程式是透明的。
|
||||
|
||||
在雲中,計算例項(虛擬機器)也可能有本地磁碟連線,但云原生系統通常將這些磁碟更像臨時快取,而不像長期儲存。這是因為如果關聯的例項發生故障,或者如果例項被更大或更小的例項(在不同的物理機器上)替換以適應負載變化,本地磁碟將變得不可訪問。
|
||||
|
||||
作為本地磁碟的替代方案,雲服務還提供虛擬磁碟儲存,可以從一個例項分離並附加到另一個例項(Amazon EBS、Azure 託管磁碟和 Google Cloud 中的持久磁碟)。這樣的虛擬磁碟實際上不是物理磁碟,而是由一組單獨的機器提供的雲服務,它模擬磁碟(**塊裝置**,其中每個塊通常為 4 KiB 大小)的行為。這項技術使得在雲中執行傳統的基於磁碟的軟體成為可能,但塊裝置模擬引入了可以在從頭開始為雲設計的系統中避免的開銷 [^25]。它還使應用程式對網路故障非常敏感,因為虛擬塊裝置上的每個 I/O 實際上都是網路呼叫 [^28]。
|
||||
|
||||
為了解決這個問題,雲原生服務通常避免使用虛擬磁碟,而是建立在針對特定工作負載最佳化的專用儲存服務上。物件儲存服務(如 S3)專為長期儲存相當大的檔案而設計,大小從數百千位元組到幾千兆位元組不等。儲存在資料庫中的單個行或值通常比這小得多;因此,雲資料庫通常在單獨的服務中管理較小的值,並在物件儲存中儲存較大的資料塊(包含許多單個值)[^26] [^29]。我們將在[第 4 章](/tw/ch4#ch_storage)中看到這樣做的方法。
|
||||
|
||||
在傳統系統架構中,同一臺計算機負責儲存(磁碟)和計算(CPU 和 RAM),但在雲原生系統中,這兩個職責已經在某種程度上分離或 **解耦** [^9] [^27] [^30] [^31]:
|
||||
例如,S3 只儲存檔案,如果你想分析該資料,你必須在 S3 之外的某個地方執行分析程式碼。這意味著透過網路傳輸資料,我們將在["分散式與單節點系統"](/tw/ch1#sec_introduction_distributed)中進一步討論。
|
||||
|
||||
此外,雲原生系統通常是 **多租戶** 的,這意味著不是每個客戶都有一個單獨的機器,而是來自多個不同客戶的資料和計算由同一服務在同一共享硬體上處理 [^32]。
|
||||
|
||||
多租戶可以實現更好的硬體利用率、更容易的可擴充套件性以及雲提供商更容易的管理,但它還需要仔細的工程設計,以確保一個客戶的活動不會影響其他客戶的系統性能或安全性 [^33]。
|
||||
|
||||
### 雲時代的運維 {#sec_introduction_operations}
|
||||
|
||||
傳統上,管理組織伺服器端資料基礎設施的人被稱為 **資料庫管理員**(DBA)或 **系統管理員**(sysadmins)。最近,許多組織試圖將軟體開發和運維的角色整合到具有後端服務和資料基礎設施共同責任的團隊中;**DevOps** 理念指導了這一趨勢。**站點可靠性工程師**(SRE)是 Google 對這一想法的實現 [^34]。
|
||||
|
||||
運維的作用是確保服務可靠地交付給使用者(包括配置基礎設施和部署應用程式),並確保穩定的生產環境(包括監控和診斷可能影響可靠性的任何問題)。對於自託管系統,運維傳統上涉及在單個機器級別上的大量工作,例如容量規劃(例如,監控可用磁碟空間並在空間用完之前新增更多磁碟)、配置新機器、將服務從一臺機器移動到另一臺機器以及安裝作業系統補丁。
|
||||
|
||||
許多雲服務提供了一個隱藏實際實現服務的單個機器的 API。例如,雲端儲存用 **計量計費** 取代了固定大小的磁碟,你可以儲存資料而無需提前規劃容量需求,然後根據實際使用的空間收費。此外,許多雲服務保持高可用,即使單個機器發生故障(參見["可靠性和容錯"](/tw/ch2#sec_introduction_reliability))。
|
||||
|
||||
從單個機器到服務的重點轉移伴隨著運維角色的變化。提供可靠服務的高階目標保持不變,但流程和工具已經發展。DevOps/SRE 理念更加強調:
|
||||
|
||||
* 自動化——偏好可重複的流程而不是手動一次性作業,
|
||||
* 偏好短暫的虛擬機器和服務而不是長期執行的伺服器,
|
||||
* 實現頻繁的應用程式更新,
|
||||
* 從事件中學習,以及
|
||||
* 保留組織關於系統的知識,即使個人來來去去 [^35]。
|
||||
|
||||
隨著雲服務的興起,角色出現了分歧:基礎設施公司的運維團隊專門為大量客戶提供可靠服務的細節,而服務的客戶在基礎設施上花費盡可能少的時間和精力 [^36]。
|
||||
|
||||
雲服務的客戶仍然需要運維,但他們專注於不同的方面,例如為給定任務選擇最合適的服務、將不同的服務相互整合以及從一個服務遷移到另一個服務。即使計量計費消除了傳統意義上的容量規劃需求,瞭解你為哪個目的使用哪些資源仍然很重要,這樣你就不會在不需要的雲資源上浪費金錢:容量規劃變成了財務規劃,效能最佳化變成了成本最佳化 [^37]。
|
||||
|
||||
此外,雲服務確實有資源限制或 **配額**(例如你可以同時執行的最大程序數),你需要在遇到它們之前瞭解並規劃它們 [^38]。
|
||||
|
||||
採用雲服務可能比執行自己的基礎設施更容易、更快,儘管即使在這裡,學習如何使用它也有成本,也許還要解決其限制。隨著越來越多的供應商提供針對不同用例的越來越廣泛的雲服務,不同服務之間的整合成為一個特別的挑戰 [^39] [^40]。
|
||||
|
||||
ETL(參見["資料倉庫"](/tw/ch1#sec_introduction_dwh))只是故事的一部分;事務型雲服務也需要相互整合。目前,缺乏促進這種整合的標準,因此它通常涉及大量的手動工作。
|
||||
|
||||
其他不能完全外包給雲服務的運維方面包括維護應用程式及其使用的庫的安全性、管理你自己的服務之間的互動、監控服務的負載以及追蹤問題的原因,例如效能下降或中斷。雖然雲正在改變運維的角色,但對運維的需求仍然很大。
|
||||
|
||||
|
||||
|
||||
## 分散式與單節點系統 {#sec_introduction_distributed}
|
||||
|
||||
涉及多臺透過網路通訊的機器的系統稱為 **分散式系統**。參與分散式系統的每個程序稱為一個 **節點**。你可能希望系統分散式的原因有很多:
|
||||
|
||||
固有分散式系統
|
||||
: 如果應用程式涉及兩個或多個互動使用者,每個使用者使用自己的裝置,那麼系統不可避免地是分散式的:裝置之間的通訊必須透過網路進行。
|
||||
|
||||
雲服務之間的請求
|
||||
: 如果資料儲存在一個服務中但在另一個服務中處理,則必須透過網路從一個服務傳輸到另一個服務。
|
||||
|
||||
容錯/高可用
|
||||
: 如果你的應用程式需要繼續工作,即使一臺機器(或幾臺機器、網路或整個資料中心)宕機,你可以使用多臺機器來提供冗餘。當一個失敗時,另一個可以接管。參見["可靠性和容錯"](/tw/ch2#sec_introduction_reliability)和[第 6 章](/tw/ch6#ch_replication)關於複製。
|
||||
|
||||
可擴充套件性
|
||||
: 如果你的資料量或計算需求增長超過單臺機器可以處理的範圍,你可以潛在地將負載分散到多臺機器上。參見["可擴充套件性"](/tw/ch2#sec_introduction_scalability)。
|
||||
|
||||
延遲
|
||||
: 如果你在世界各地都有使用者,你可能希望在全球各個地區都有伺服器,以便每個使用者都可以從地理上靠近他們的伺服器獲得服務。這避免了使用者必須等待網路資料包在世界各地傳輸才能回答他們的請求。參見["描述效能"](/tw/ch2#sec_introduction_percentiles)。
|
||||
|
||||
彈性
|
||||
: 如果你的應用程式在某些時候繁忙而在其他時候空閒,雲部署可以根據需求擴大或縮小,因此你只需為正在積極使用的資源付費。這在單臺機器上更困難,它需要配置以處理最大負載,即使在幾乎不使用時也是如此。
|
||||
|
||||
使用專用硬體
|
||||
: 系統的不同部分可以利用不同型別的硬體來匹配其工作負載。例如,物件儲存可能使用具有許多磁碟但很少 CPU 的機器,而資料分析系統可能使用具有大量 CPU 和記憶體但沒有磁碟的機器,機器學習系統可能使用具有 GPU 的機器(對於訓練深度神經網路和其他機器學習任務,GPU 比 CPU 高效得多)。
|
||||
|
||||
法律合規
|
||||
: 一些國家有資料駐留法,要求關於其管轄範圍內人員的資料必須在該國家地理範圍內儲存和處理 [^41]。
|
||||
這些規則的範圍各不相同——例如,在某些情況下,它僅適用於醫療或金融資料,而其他情況則更廣泛。因此,在多個此類管轄區擁有使用者的服務必須將其資料分佈在多個位置的伺服器上。
|
||||
|
||||
可持續性
|
||||
: 如果你在何時何地執行作業方面有靈活性,你可能能夠在可再生電力充足的時間和地點執行它們,並避免在電網承受壓力時執行它們。這可以減少你的碳排放,並讓你在電力便宜時利用便宜的電力 [^42] [^43]。
|
||||
|
||||
這些原因既適用於你自己編寫的服務(應用程式程式碼),也適用於由現成軟體(如資料庫)組成的服務。
|
||||
|
||||
### 分散式系統的問題 {#sec_introduction_dist_sys_problems}
|
||||
|
||||
分散式系統也有缺點。透過網路進行的每個請求和 API 呼叫都需要處理失敗的可能性:網路可能中斷,或者服務可能過載或崩潰,因此任何請求都可能超時而不收到響應。在這種情況下,我們不知道服務是否收到了請求,簡單地重試它可能不安全。我們將在[第 9 章](/tw/ch9#ch_distributed)中詳細討論這些問題。
|
||||
|
||||
雖然資料中心網路很快,但呼叫另一個服務仍然比在同一程序中呼叫函式慢得多 [^44]。
|
||||
|
||||
在處理大量資料時,與其將資料從儲存傳輸到處理它的單獨機器,不如將計算帶到已經擁有資料的機器上可能更快 [^45]。
|
||||
|
||||
更多的節點並不總是更快:在某些情況下,一臺計算機上的簡單單執行緒程式可以比擁有超過 100 個 CPU 核心的叢集表現得明顯更好 [^46]。
|
||||
|
||||
故障排除分散式系統通常很困難:如果系統響應緩慢,你如何找出問題所在?在 **可觀測性** 的標題下開發了診斷分散式系統問題的技術 [^47] [^48],
|
||||
這涉及收集有關係統執行的資料,並允許以允許分析高階指標和單個事件的方式對其進行查詢。**追蹤** 工具(如 OpenTelemetry、Zipkin 和 Jaeger)允許你跟蹤哪個客戶端為哪個操作呼叫了哪個伺服器,以及每個呼叫花費了多長時間 [^49]。
|
||||
|
||||
資料庫提供了各種機制來確保資料一致性,正如我們將在[第 6 章](/tw/ch6#ch_replication)和[第 8 章](/tw/ch8#ch_transactions)中看到的。然而,當每個服務都有自己的資料庫時,維護這些不同服務之間資料的一致性就成了應用程式的問題。
|
||||
分散式事務(我們在[第 8 章](/tw/ch8#ch_transactions)中探討)是確保一致性的一種可能技術,但它們很少在微服務環境中使用,因為它們與使服務彼此獨立的目標背道而馳,並且許多資料庫不支援它們 [^50]。
|
||||
|
||||
由於所有這些原因,如果你可以在單臺機器上做某事,與設定分散式系統相比,這通常要簡單得多,成本也低得多 [^23] [^46] [^51]。
|
||||
CPU、記憶體和磁碟已經變得更大、更快、更可靠。與單節點資料庫(如 DuckDB、SQLite 和 KùzuDB)結合使用時,許多工作負載現在可以在單個節點上執行。我們將在[第 4 章](/tw/ch4#ch_storage)中更多地探討這個主題。
|
||||
|
||||
### 微服務與 Serverless {#sec_introduction_microservices}
|
||||
|
||||
將系統分佈在多臺機器上的最常見方法是將它們分為客戶端和伺服器,並讓客戶端向伺服器發出請求。最常見的是,HTTP 用於此通訊,正如我們將在["透過服務的資料流:REST 和 RPC"](/tw/ch5#sec_encoding_dataflow_rpc)中討論的。同一程序可能既是伺服器(處理傳入請求)又是客戶端(向其他服務發出出站請求)。
|
||||
|
||||
這種構建應用程式的方式傳統上被稱為 **面向服務的架構**(SOA);最近,這個想法已經被細化為 **微服務** 架構 [^52] [^53]。
|
||||
在這種架構中,服務有一個明確定義的目的(例如,在 S3 的情況下,這將是檔案儲存);每個服務公開一個可以由客戶端透過網路呼叫的 API,每個服務有一個負責其維護的團隊。因此,複雜的應用程式可以分解為多個互動服務,每個服務由單獨的團隊管理。
|
||||
|
||||
將複雜的軟體分解為多個服務有幾個優點:每個服務可以獨立更新,減少團隊之間的協調工作;每個服務可以分配它需要的硬體資源;透過將實現細節隱藏在 API 後面,服務所有者可以自由更改實現而不影響客戶端。在資料儲存方面,每個服務擁有自己的資料庫並且不在服務之間共享資料庫是很常見的:共享資料庫將有效地使整個資料庫結構成為服務 API 的一部分,然後該結構將難以更改。共享資料庫還可能導致一個服務的查詢對其他服務的效能產生負面影響。
|
||||
|
||||
另一方面,擁有許多服務本身可能會滋生複雜性:每個服務都需要基礎設施來部署新版本、調整分配的硬體資源以匹配負載、收集日誌、監控服務健康狀況,並在出現問題時向值班工程師發出警報。**編排** 框架(如 Kubernetes)已成為部署服務的流行方式,因為它們為這種基礎設施提供了基礎。在開發期間測試服務可能會很複雜,因為你還需要執行它所依賴的所有其他服務。
|
||||
|
||||
微服務 API 的演進可能具有挑戰性。呼叫 API 的客戶端期望 API 具有某些欄位。開發人員可能希望隨著業務需求的變化向 API 新增或刪除欄位,但這樣做可能會導致客戶端失敗。更糟糕的是,這種失敗通常直到開發週期的後期才被發現,當更新的服務 API 部署到暫存或生產環境時。API 描述標準(如 OpenAPI 和 gRPC)有助於管理客戶端和伺服器 API 之間的關係;我們將在[第 5 章](/tw/ch5#ch_encoding)中進一步討論這些。
|
||||
|
||||
微服務主要是人員問題的技術解決方案:允許不同的團隊獨立取得進展,而無需相互協調。這在大公司中很有價值,但在沒有很多團隊的小公司中,使用微服務可能是不必要的開銷,最好以最簡單的方式實現應用程式 [^52]。
|
||||
|
||||
**無伺服器** 或 **函式即服務**(FaaS)是部署服務的另一種方法,其中基礎設施的管理外包給雲供應商 [^33]。
|
||||
使用虛擬機器時,你必須明確選擇何時啟動或關閉例項;相比之下,使用無伺服器模型,雲提供商根據對你的服務的傳入請求自動分配和釋放硬體資源 [^54]。
|
||||
無伺服器部署將更多的運營負擔轉移到雲提供商,並透過使用而不是機器例項實現靈活計費。為了提供這些好處,許多無伺服器基礎設施提供商對函式執行施加時間限制、限制執行時環境,並且在首次呼叫函式時可能會遭受緩慢的啟動時間。術語"無伺服器"也可能具有誤導性:每個無伺服器函式執行仍在伺服器上執行,但後續執行可能在不同的伺服器上執行。此外,BigQuery 和各種 Kafka 產品等基礎設施已採用"無伺服器"術語來表示其服務自動擴充套件,並且它們按使用而不是機器例項計費。
|
||||
|
||||
就像雲端儲存用計量計費模型取代了容量規劃(提前決定購買多少磁碟)一樣,無伺服器方法正在為程式碼執行帶來計量計費:你只需為應用程式程式碼實際執行的時間付費,而不必提前配置資源。
|
||||
|
||||
### 雲計算與超級計算 {#id17}
|
||||
|
||||
雲計算不是構建大規模計算系統的唯一方式;另一種選擇是 **高效能計算**(HPC),也稱為 **超級計算**。雖然有重疊,但與雲計算和企業資料中心繫統相比,HPC 通常有不同的優先順序並使用不同的技術。其中一些差異是:
|
||||
|
||||
* 超級計算機通常用於計算密集型科學計算任務,如天氣預報、氣候建模、分子動力學(模擬原子和分子的運動)、複雜最佳化問題和求解偏微分方程。另一方面,雲計算往往用於線上服務、業務資料系統以及需要以高可用性為使用者請求提供服務的類似系統。
|
||||
* 超級計算機通常執行大型批處理作業,這些作業會定期將其計算狀態檢查點到磁碟。如果節點發生故障,常見的解決方案是簡單地停止整個叢集工作負載,修復故障節點,然後從最後一個檢查點重新啟動計算 [^55] [^56]。
|
||||
對於雲服務,通常不希望停止整個叢集,因為服務需要以最小的中斷持續為使用者提供服務。
|
||||
* 超級計算機節點通常透過共享記憶體和遠端直接記憶體訪問(RDMA)進行通訊,這支援高頻寬和低延遲,但假設系統使用者之間具有高度信任 [^57]。
|
||||
在雲計算中,網路和機器通常由相互不信任的組織共享,需要更強的安全機制,如資源隔離(例如,虛擬機器)、加密和身份驗證。
|
||||
* 雲資料中心網路通常基於 IP 和乙太網,以 Clos 拓撲排列以提供高二分頻寬——網路整體效能的常用度量 [^55] [^58]。
|
||||
超級計算機通常使用專門的網路拓撲,如多維網格和環面 [^59],
|
||||
這些拓撲為具有已知通訊模式的 HPC 工作負載產生更好的效能。
|
||||
* 雲計算允許節點分佈在多個地理區域,而超級計算機通常假設其所有節點都緊密相鄰。
|
||||
|
||||
大規模分析系統有時與超級計算共享一些特徵,如果你在這個領域工作,瞭解這些技術可能是值得的。然而,本書主要關注需要持續可用的服務,如["可靠性和容錯"](/tw/ch2#sec_introduction_reliability)中所討論的。
|
||||
|
||||
## 資料系統、法律與社會 {#sec_introduction_compliance}
|
||||
|
||||
到目前為止,你已經在本章中看到,資料系統的架構不僅受技術目標和要求的影響,還受它們支援的組織的人類需求的影響。越來越多的資料系統工程師意識到,僅為自己的業務需求服務是不夠的:我們還對整個社會負有責任。
|
||||
|
||||
一個特別關注的問題是儲存關於人及其行為的資料的系統。自 2018 年以來,**通用資料保護條例**(GDPR)賦予許多歐洲國家的居民對其個人資料的更大控制和法律權利,類似的隱私法規已在世界各地的各個國家和州採用,例如加州消費者隱私法案(CCPA)。關於 AI 的法規,如 **歐盟 AI 法案**,對個人資料的使用方式施加了進一步的限制。
|
||||
|
||||
此外,即使在不直接受監管的領域,人們也越來越認識到計算機系統對人和社會的影響。社交媒體改變了個人消費新聞的方式,這影響了他們的政治觀點,因此可能影響選舉結果。自動化系統越來越多地做出對個人產生深遠影響的決定,例如決定誰應該獲得貸款或保險、誰應該被邀請參加工作面試,或者誰應該被懷疑犯罪 [^60]。
|
||||
|
||||
從事此類系統工作的每個人都有責任考慮道德影響並確保它們符合相關法律。沒有必要讓每個人都成為法律和道德專家,但對法律和道德原則的基本認識與對分散式系統的一些基礎知識一樣重要。
|
||||
|
||||
法律考慮正在影響資料系統設計的基礎 [^61]。
|
||||
例如,GDPR 授予個人根據要求刪除其資料的權利(有時稱為 **被遺忘權**)。然而,正如我們將在本書中看到的,許多資料系統依賴於不可變構造(如僅追加日誌)作為其設計的一部分;我們如何確保刪除應該是不可變的檔案中間的某些資料?我們如何處理已納入派生資料集(參見["權威記錄系統與派生資料"](/tw/ch1#sec_introduction_derived))的資料的刪除,例如機器學習模型的訓練資料?回答這些問題會帶來新的工程挑戰。
|
||||
|
||||
目前,我們沒有關於哪些特定技術或系統架構應被視為"符合 GDPR"的明確指導方針。該法規故意不強制要求特定技術,因為隨著技術的進步,這些技術可能會迅速變化。相反,法律文字闡述了需要解釋的高階原則。這意味著如何遵守隱私法規的問題沒有簡單的答案,但我們將透過這個視角來看待本書中的一些技術。
|
||||
|
||||
一般來說,我們儲存資料是因為我們認為其價值大於儲存它的成本。然而,值得記住的是,儲存成本不僅僅是你為 Amazon S3 或其他服務支付的賬單:成本效益計算還應考慮到如果資料被洩露或被對手破壞的責任和聲譽損害風險,以及如果資料的儲存和處理被發現不符合法律的法律成本和罰款風險 [^51]。
|
||||
|
||||
政府或警察部隊也可能迫使公司交出資料。當存在資料可能揭示犯罪行為的風險時(例如,在幾個中東和非洲國家的同性戀,或在幾個美國州尋求墮胎),儲存該資料會給使用者帶來真正的安全風險。例如,前往墮胎診所的旅行很容易透過位置資料揭示,甚至可能透過使用者 IP 地址隨時間的日誌(表示大致位置)。
|
||||
|
||||
一旦考慮到所有風險,可能合理地決定某些資料根本不值得儲存,因此應該刪除。這種 **資料最小化** 原則(有時用德語術語 **Datensparsamkeit** 稱呼)與"大資料"理念相反,後者是推測性地儲存大量資料,以防將來有用 [^62]。
|
||||
但它符合 GDPR,該法規要求個人資料只能為指定的明確目的收集,該資料以後不得用於任何其他目的,並且資料不得保留超過收集目的所需的時間 [^63]。
|
||||
|
||||
企業也注意到了隱私和安全問題。信用卡公司要求支付處理企業遵守嚴格的支付卡行業(PCI)標準。處理器經常接受獨立審計師的評估,以驗證持續合規性。軟體供應商也受到了更多的審查。許多買家現在要求他們的供應商遵守服務組織控制(SOC)第 2 類標準。與 PCI 合規性一樣,供應商接受第三方審計以驗證遵守情況。
|
||||
|
||||
一般來說,重要的是要平衡你的業務需求與你正在收集和處理其資料的人的需求。這個話題還有很多內容;在[待補充連結]中,我們將更深入地探討道德和法律合規主題,包括偏見和歧視問題。
|
||||
|
||||
## 總結 {#summary}
|
||||
|
||||
本章的主題是理解權衡:也就是說,認識到對於許多問題沒有一個正確的答案,而是有幾種不同的方法,每種方法都有各種利弊。我們探討了影響資料系統架構的一些最重要的選擇,並介紹了本書其餘部分所需的術語。
|
||||
|
||||
我們首先區分了事務型(事務處理,OLTP)和分析型(OLAP)系統,並看到了它們的不同特徵:不僅管理具有不同訪問模式的不同型別的資料,而且還服務於不同的受眾。我們遇到了資料倉庫和資料湖的概念,它們透過 ETL 從事務型系統接收資料饋送。在[第 4 章](/tw/ch4#ch_storage)中,我們將看到事務型和分析型系統由於需要服務的不同型別的查詢,通常使用非常不同的內部資料佈局。
|
||||
|
||||
然後,我們將雲服務(一個相對較新的發展)與傳統的自託管軟體正規化進行了比較,後者以前主導著資料系統架構。這些方法中哪一個更具成本效益在很大程度上取決於你的特定情況,但不可否認的是,雲原生方法正在為資料系統的架構帶來重大變化,例如它們分離儲存和計算的方式。
|
||||
|
||||
雲系統本質上是分散式的,我們簡要地研究了分散式系統與使用單臺機器相比的一些權衡。在某些情況下,你無法避免分散式,但如果可能將其保留在單臺機器上,建議不要急於使系統分散式。在[第 9 章](/tw/ch9#ch_distributed)中,我們將更詳細地介紹分散式系統的挑戰。
|
||||
|
||||
最後,我們看到資料系統架構不僅由部署系統的業務需求決定,還由保護其資料被處理的人的權利的隱私法規決定——這是許多工程師傾向於忽視的一個方面。我們如何將法律要求轉化為技術實現還不太清楚,但在我們瀏覽本書的其餘部分時,記住這個問題很重要。
|
||||
|
||||
### 參考
|
||||
|
||||
|
||||
[^1]: Richard T. Kouzes, Gordon A. Anderson, Stephen T. Elbert, Ian Gorton, and Deborah K. Gracio. [The Changing Paradigm of Data-Intensive Computing](http://www2.ic.uff.br/~boeres/slides_AP/papers/TheChanginParadigmDataIntensiveComputing_2009.pdf). *IEEE Computer*, volume 42, issue 1, January 2009. [doi:10.1109/MC.2009.26](https://doi.org/10.1109/MC.2009.26)
|
||||
[^2]: Martin Kleppmann, Adam Wiggins, Peter van Hardenberg, and Mark McGranaghan. [Local-first software: you own your data, in spite of the cloud](https://www.inkandswitch.com/local-first/). At *2019 ACM SIGPLAN International Symposium on New Ideas, New Paradigms, and Reflections on Programming and Software* (Onward!), October 2019. [doi:10.1145/3359591.3359737](https://doi.org/10.1145/3359591.3359737)
|
||||
[^3]: Joe Reis and Matt Housley. [*Fundamentals of Data Engineering*](https://www.oreilly.com/library/view/fundamentals-of-data/9781098108298/). O’Reilly Media, 2022. ISBN: 9781098108304
|
||||
[^4]: Rui Pedro Machado and Helder Russa. [*Analytics Engineering with SQL and dbt*](https://www.oreilly.com/library/view/analytics-engineering-with/9781098142377/). O’Reilly Media, 2023. ISBN: 9781098142384
|
||||
[^5]: Edgar F. Codd, S. B. Codd, and C. T. Salley. [Providing OLAP to User-Analysts: An IT Mandate](https://www.estgv.ipv.pt/PaginasPessoais/jloureiro/ESI_AID2007_2008/fichas/codd.pdf). E. F. Codd Associates, 1993. Archived at [perma.cc/RKX8-2GEE](https://perma.cc/RKX8-2GEE)
|
||||
[^6]: Chinmay Soman and Neha Pawar. [Comparing Three Real-Time OLAP Databases: Apache Pinot, Apache Druid, and ClickHouse](https://startree.ai/blog/a-tale-of-three-real-time-olap-databases). *startree.ai*, April 2023. Archived at [perma.cc/8BZP-VWPA](https://perma.cc/8BZP-VWPA)
|
||||
[^7]: Surajit Chaudhuri and Umeshwar Dayal. [An Overview of Data Warehousing and OLAP Technology](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/sigrecord.pdf). *ACM SIGMOD Record*, volume 26, issue 1, pages 65–74, March 1997. [doi:10.1145/248603.248616](https://doi.org/10.1145/248603.248616)
|
||||
[^8]: Fatma Özcan, Yuanyuan Tian, and Pinar Tözün. [Hybrid Transactional/Analytical Processing: A Survey](https://humming80.github.io/papers/sigmod-htaptut.pdf). At *ACM International Conference on Management of Data* (SIGMOD), May 2017. [doi:10.1145/3035918.3054784](https://doi.org/10.1145/3035918.3054784)
|
||||
[^9]: Adam Prout, Szu-Po Wang, Joseph Victor, Zhou Sun, Yongzhu Li, Jack Chen, Evan Bergeron, Eric Hanson, Robert Walzer, Rodrigo Gomes, and Nikita Shamgunov. [Cloud-Native Transactions and Analytics in SingleStore](https://dl.acm.org/doi/abs/10.1145/3514221.3526055). At *International Conference on Management of Data* (SIGMOD), June 2022. [doi:10.1145/3514221.3526055](https://doi.org/10.1145/3514221.3526055)
|
||||
[^10]: Chao Zhang, Guoliang Li, Jintao Zhang, Xinning Zhang, and Jianhua Feng. [HTAP Databases: A Survey](https://arxiv.org/pdf/2404.15670). *IEEE Transactions on Knowledge and Data Engineering*, April 2024. [doi:10.1109/TKDE.2024.3389693](https://doi.org/10.1109/TKDE.2024.3389693)
|
||||
[^11]: Michael Stonebraker and Uğur Çetintemel. [‘One Size Fits All’: An Idea Whose Time Has Come and Gone](https://pages.cs.wisc.edu/~shivaram/cs744-readings/fits_all.pdf). At *21st International Conference on Data Engineering* (ICDE), April 2005. [doi:10.1109/ICDE.2005.1](https://doi.org/10.1109/ICDE.2005.1)
|
||||
[^12]: Jeffrey Cohen, Brian Dolan, Mark Dunlap, Joseph M. Hellerstein, and Caleb Welton. [MAD Skills: New Analysis Practices for Big Data](https://www.vldb.org/pvldb/vol2/vldb09-219.pdf). *Proceedings of the VLDB Endowment*, volume 2, issue 2, pages 1481–1492, August 2009. [doi:10.14778/1687553.1687576](https://doi.org/10.14778/1687553.1687576)
|
||||
[^13]: Dan Olteanu. [The Relational Data Borg is Learning](https://www.vldb.org/pvldb/vol13/p3502-olteanu.pdf). *Proceedings of the VLDB Endowment*, volume 13, issue 12, August 2020. [doi:10.14778/3415478.3415572](https://doi.org/10.14778/3415478.3415572)
|
||||
[^14]: Matt Bornstein, Martin Casado, and Jennifer Li. [Emerging Architectures for Modern Data Infrastructure: 2020](https://future.a16z.com/emerging-architectures-for-modern-data-infrastructure-2020/). *future.a16z.com*, October 2020. Archived at [perma.cc/LF8W-KDCC](https://perma.cc/LF8W-KDCC)
|
||||
[^15]: Martin Fowler. [DataLake](https://www.martinfowler.com/bliki/DataLake.html). *martinfowler.com*, February 2015. Archived at [perma.cc/4WKN-CZUK](https://perma.cc/4WKN-CZUK)
|
||||
[^16]: Bobby Johnson and Joseph Adler. [The Sushi Principle: Raw Data Is Better](https://learning.oreilly.com/videos/strata-hadoop/9781491924143/9781491924143-video210840/). At *Strata+Hadoop World*, February 2015.
|
||||
[^17]: Michael Armbrust, Ali Ghodsi, Reynold Xin, and Matei Zaharia. [Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics](https://www.cidrdb.org/cidr2021/papers/cidr2021_paper17.pdf). At *11th Annual Conference on Innovative Data Systems Research* (CIDR), January 2021.
|
||||
[^18]: DataKitchen, Inc. [The DataOps Manifesto](https://dataopsmanifesto.org/en/). *dataopsmanifesto.org*, 2017. Archived at [perma.cc/3F5N-FUQ4](https://perma.cc/3F5N-FUQ4)
|
||||
[^19]: Tejas Manohar. [What is Reverse ETL: A Definition & Why It’s Taking Off](https://hightouch.io/blog/reverse-etl/). *hightouch.io*, November 2021. Archived at [perma.cc/A7TN-GLYJ](https://perma.cc/A7TN-GLYJ)
|
||||
[^20]: Simon O’Regan. [Designing Data Products](https://towardsdatascience.com/designing-data-products-b6b93edf3d23). *towardsdatascience.com*, August 2018. Archived at [perma.cc/HU67-3RV8](https://perma.cc/HU67-3RV8)
|
||||
[^21]: Camille Fournier. [Why is it so hard to decide to buy?](https://skamille.medium.com/why-is-it-so-hard-to-decide-to-buy-d86fee98e88e) *skamille.medium.com*, July 2021. Archived at [perma.cc/6VSG-HQ5X](https://perma.cc/6VSG-HQ5X)
|
||||
[^22]: David Heinemeier Hansson. [Why we’re leaving the cloud](https://world.hey.com/dhh/why-we-re-leaving-the-cloud-654b47e0). *world.hey.com*, October 2022. Archived at [perma.cc/82E6-UJ65](https://perma.cc/82E6-UJ65)
|
||||
[^23]: Nima Badizadegan. [Use One Big Server](https://specbranch.com/posts/one-big-server/). *specbranch.com*, August 2022. Archived at [perma.cc/M8NB-95UK](https://perma.cc/M8NB-95UK)
|
||||
[^24]: Steve Yegge. [Dear Google Cloud: Your Deprecation Policy is Killing You](https://steve-yegge.medium.com/dear-google-cloud-your-deprecation-policy-is-killing-you-ee7525dc05dc). *steve-yegge.medium.com*, August 2020. Archived at [perma.cc/KQP9-SPGU](https://perma.cc/KQP9-SPGU)
|
||||
[^25]: Alexandre Verbitski, Anurag Gupta, Debanjan Saha, Murali Brahmadesam, Kamal Gupta, Raman Mittal, Sailesh Krishnamurthy, Sandor Maurice, Tengiz Kharatishvili, and Xiaofeng Bao. [Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases](https://media.amazonwebservices.com/blog/2017/aurora-design-considerations-paper.pdf). At *ACM International Conference on Management of Data* (SIGMOD), pages 1041–1052, May 2017. [doi:10.1145/3035918.3056101](https://doi.org/10.1145/3035918.3056101)
|
||||
[^26]: Panagiotis Antonopoulos, Alex Budovski, Cristian Diaconu, Alejandro Hernandez Saenz, Jack Hu, Hanuma Kodavalla, Donald Kossmann, Sandeep Lingam, Umar Farooq Minhas, Naveen Prakash, Vijendra Purohit, Hugh Qu, Chaitanya Sreenivas Ravella, Krystyna Reisteter, Sheetal Shrotri, Dixin Tang, and Vikram Wakade. [Socrates: The New SQL Server in the Cloud](https://www.microsoft.com/en-us/research/uploads/prod/2019/05/socrates.pdf). At *ACM International Conference on Management of Data* (SIGMOD), pages 1743–1756, June 2019. [doi:10.1145/3299869.3314047](https://doi.org/10.1145/3299869.3314047)
|
||||
[^27]: Midhul Vuppalapati, Justin Miron, Rachit Agarwal, Dan Truong, Ashish Motivala, and Thierry Cruanes. [Building An Elastic Query Engine on Disaggregated Storage](https://www.usenix.org/system/files/nsdi20-paper-vuppalapati.pdf). At *17th USENIX Symposium on Networked Systems Design and Implementation* (NSDI), February 2020.
|
||||
[^28]: Nick Van Wiggeren. [The Real Failure Rate of EBS](https://planetscale.com/blog/the-real-fail-rate-of-ebs). *planetscale.com*, March 2025. Archived at [perma.cc/43CR-SAH5](https://perma.cc/43CR-SAH5)
|
||||
[^29]: Colin Breck. [Predicting the Future of Distributed Systems](https://blog.colinbreck.com/predicting-the-future-of-distributed-systems/). *blog.colinbreck.com*, August 2024. Archived at [perma.cc/K5FC-4XX2](https://perma.cc/K5FC-4XX2)
|
||||
[^30]: Gwen Shapira. [Compute-Storage Separation Explained](https://www.thenile.dev/blog/storage-compute). *thenile.dev*, January 2023. Archived at [perma.cc/QCV3-XJNZ](https://perma.cc/QCV3-XJNZ)
|
||||
[^31]: Ravi Murthy and Gurmeet Goindi. [AlloyDB for PostgreSQL under the hood: Intelligent, database-aware storage](https://cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storage). *cloud.google.com*, May 2022. Archived at [archive.org](https://web.archive.org/web/20220514021120/https%3A//cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storage)
|
||||
[^32]: Jack Vanlightly. [The Architecture of Serverless Data Systems](https://jack-vanlightly.com/blog/2023/11/14/the-architecture-of-serverless-data-systems). *jack-vanlightly.com*, November 2023. Archived at [perma.cc/UDV4-TNJ5](https://perma.cc/UDV4-TNJ5)
|
||||
[^33]: Eric Jonas, Johann Schleier-Smith, Vikram Sreekanti, Chia-Che Tsai, Anurag Khandelwal, Qifan Pu, Vaishaal Shankar, Joao Carreira, Karl Krauth, Neeraja Yadwadkar, Joseph E. Gonzalez, Raluca Ada Popa, Ion Stoica, David A. Patterson. [Cloud Programming Simplified: A Berkeley View on Serverless Computing](https://arxiv.org/abs/1902.03383). *arxiv.org*, February 2019.
|
||||
[^34]: Betsy Beyer, Jennifer Petoff, Chris Jones, and Niall Richard Murphy. [*Site Reliability Engineering: How Google Runs Production Systems*](https://www.oreilly.com/library/view/site-reliability-engineering/9781491929117/). O’Reilly Media, 2016. ISBN: 9781491929124
|
||||
[^35]: Thomas Limoncelli. [The Time I Stole $10,000 from Bell Labs](https://queue.acm.org/detail.cfm?id=3434773). *ACM Queue*, volume 18, issue 5, November 2020. [doi:10.1145/3434571.3434773](https://doi.org/10.1145/3434571.3434773)
|
||||
[^36]: Charity Majors. [The Future of Ops Jobs](https://acloudguru.com/blog/engineering/the-future-of-ops-jobs). *acloudguru.com*, August 2020. Archived at [perma.cc/GRU2-CZG3](https://perma.cc/GRU2-CZG3)
|
||||
[^37]: Boris Cherkasky. [(Over)Pay As You Go for Your Datastore](https://medium.com/riskified-technology/over-pay-as-you-go-for-your-datastore-11a29ae49a8b). *medium.com*, September 2021. Archived at [perma.cc/Q8TV-2AM2](https://perma.cc/Q8TV-2AM2)
|
||||
[^38]: Shlomi Kushchi. [Serverless Doesn’t Mean DevOpsLess or NoOps](https://thenewstack.io/serverless-doesnt-mean-devopsless-or-noops/). *thenewstack.io*, February 2023. Archived at [perma.cc/3NJR-AYYU](https://perma.cc/3NJR-AYYU)
|
||||
[^39]: Erik Bernhardsson. [Storm in the stratosphere: how the cloud will be reshuffled](https://erikbern.com/2021/11/30/storm-in-the-stratosphere-how-the-cloud-will-be-reshuffled.html). *erikbern.com*, November 2021. Archived at [perma.cc/SYB2-99P3](https://perma.cc/SYB2-99P3)
|
||||
[^40]: Benn Stancil. [The data OS](https://benn.substack.com/p/the-data-os). *benn.substack.com*, September 2021. Archived at [perma.cc/WQ43-FHS6](https://perma.cc/WQ43-FHS6)
|
||||
[^41]: Maria Korolov. [Data residency laws pushing companies toward residency as a service](https://www.csoonline.com/article/3647761/data-residency-laws-pushing-companies-toward-residency-as-a-service.html). *csoonline.com*, January 2022. Archived at [perma.cc/CHE4-XZZ2](https://perma.cc/CHE4-XZZ2)
|
||||
[^42]: Severin Borenstein. [Can Data Centers Flex Their Power Demand?](https://energyathaas.wordpress.com/2025/04/14/can-data-centers-flex-their-power-demand/) *energyathaas.wordpress.com*, April 2025. Archived at <https://perma.cc/MUD3-A6FF>
|
||||
[^43]: Bilge Acun, Benjamin Lee, Fiodar Kazhamiaka, Aditya Sundarrajan, Kiwan Maeng, Manoj Chakkaravarthy, David Brooks, and Carole-Jean Wu. [Carbon Dependencies in Datacenter Design and Management](https://hotcarbon.org/assets/2022/pdf/hotcarbon22-acun.pdf). *ACM SIGENERGY Energy Informatics Review*, volume 3, issue 3, pages 21–26. [doi:10.1145/3630614.3630619](https://doi.org/10.1145/3630614.3630619)
|
||||
[^44]: Kousik Nath. [These are the numbers every computer engineer should know](https://www.freecodecamp.org/news/must-know-numbers-for-every-computer-engineer/). *freecodecamp.org*, September 2019. Archived at [perma.cc/RW73-36RL](https://perma.cc/RW73-36RL)
|
||||
[^45]: Joseph M. Hellerstein, Jose Faleiro, Joseph E. Gonzalez, Johann Schleier-Smith, Vikram Sreekanti, Alexey Tumanov, and Chenggang Wu. [Serverless Computing: One Step Forward, Two Steps Back](https://arxiv.org/abs/1812.03651). At *Conference on Innovative Data Systems Research* (CIDR), January 2019.
|
||||
[^46]: Frank McSherry, Michael Isard, and Derek G. Murray. [Scalability! But at What COST?](https://www.usenix.org/system/files/conference/hotos15/hotos15-paper-mcsherry.pdf) At *15th USENIX Workshop on Hot Topics in Operating Systems* (HotOS), May 2015.
|
||||
[^47]: Cindy Sridharan. *[Distributed Systems Observability: A Guide to Building Robust Systems](https://unlimited.humio.com/rs/756-LMY-106/images/Distributed-Systems-Observability-eBook.pdf)*. Report, O’Reilly Media, May 2018. Archived at [perma.cc/M6JL-XKCM](https://perma.cc/M6JL-XKCM)
|
||||
[^48]: Charity Majors. [Observability — A 3-Year Retrospective](https://thenewstack.io/observability-a-3-year-retrospective/). *thenewstack.io*, August 2019. Archived at [perma.cc/CG62-TJWL](https://perma.cc/CG62-TJWL)
|
||||
[^49]: Benjamin H. Sigelman, Luiz André Barroso, Mike Burrows, Pat Stephenson, Manoj Plakal, Donald Beaver, Saul Jaspan, and Chandan Shanbhag. [Dapper, a Large-Scale Distributed Systems Tracing Infrastructure](https://research.google/pubs/pub36356/). Google Technical Report dapper-2010-1, April 2010. Archived at [perma.cc/K7KU-2TMH](https://perma.cc/K7KU-2TMH)
|
||||
[^50]: Rodrigo Laigner, Yongluan Zhou, Marcos Antonio Vaz Salles, Yijian Liu, and Marcos Kalinowski. [Data management in microservices: State of the practice, challenges, and research directions](https://www.vldb.org/pvldb/vol14/p3348-laigner.pdf). *Proceedings of the VLDB Endowment*, volume 14, issue 13, pages 3348–3361, September 2021. [doi:10.14778/3484224.3484232](https://doi.org/10.14778/3484224.3484232)
|
||||
[^51]: Jordan Tigani. [Big Data is Dead](https://motherduck.com/blog/big-data-is-dead/). *motherduck.com*, February 2023. Archived at [perma.cc/HT4Q-K77U](https://perma.cc/HT4Q-K77U)
|
||||
[^52]: Sam Newman. [*Building Microservices*, second edition](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/). O’Reilly Media, 2021. ISBN: 9781492034025
|
||||
[^53]: Chris Richardson. [Microservices: Decomposing Applications for Deployability and Scalability](https://www.infoq.com/articles/microservices-intro/). *infoq.com*, May 2014. Archived at [perma.cc/CKN4-YEQ2](https://perma.cc/CKN4-YEQ2)
|
||||
[^54]: Mohammad Shahrad, Rodrigo Fonseca, Íñigo Goiri, Gohar Chaudhry, Paul Batum, Jason Cooke, Eduardo Laureano, Colby Tresness, Mark Russinovich, Ricardo Bianchini. [Serverless in the Wild: Characterizing and Optimizing the Serverless Workload at a Large Cloud Provider](https://www.usenix.org/system/files/atc20-shahrad.pdf). At *USENIX Annual Technical Conference* (ATC), July 2020.
|
||||
[^55]: Luiz André Barroso, Urs Hölzle, and Parthasarathy Ranganathan. [The Datacenter as a Computer: Designing Warehouse-Scale Machines](https://www.morganclaypool.com/doi/10.2200/S00874ED3V01Y201809CAC046), third edition. Morgan & Claypool Synthesis Lectures on Computer Architecture, October 2018. [doi:10.2200/S00874ED3V01Y201809CAC046](https://doi.org/10.2200/S00874ED3V01Y201809CAC046)
|
||||
[^56]: David Fiala, Frank Mueller, Christian Engelmann, Rolf Riesen, Kurt Ferreira, and Ron Brightwell. [Detection and Correction of Silent Data Corruption for Large-Scale High-Performance Computing](https://arcb.csc.ncsu.edu/~mueller/ftp/pub/mueller/papers/sc12.pdf),” at *International Conference for High Performance Computing, Networking, Storage and Analysis* (SC), November 2012. [doi:10.1109/SC.2012.49](https://doi.org/10.1109/SC.2012.49)
|
||||
[^57]: Anna Kornfeld Simpson, Adriana Szekeres, Jacob Nelson, and Irene Zhang. [Securing RDMA for High-Performance Datacenter Storage Systems](https://www.usenix.org/conference/hotcloud20/presentation/kornfeld-simpson). At *12th USENIX Workshop on Hot Topics in Cloud Computing* (HotCloud), July 2020.
|
||||
[^58]: Arjun Singh, Joon Ong, Amit Agarwal, Glen Anderson, Ashby Armistead, Roy Bannon, Seb Boving, Gaurav Desai, Bob Felderman, Paulie Germano, Anand Kanagala, Jeff Provost, Jason Simmons, Eiichi Tanda, Jim Wanderer, Urs Hölzle, Stephen Stuart, and Amin Vahdat. [Jupiter Rising: A Decade of Clos Topologies and Centralized Control in Google’s Datacenter Network](https://conferences.sigcomm.org/sigcomm/2015/pdf/papers/p183.pdf). At *Annual Conference of the ACM Special Interest Group on Data Communication* (SIGCOMM), August 2015. [doi:10.1145/2785956.2787508](https://doi.org/10.1145/2785956.2787508)
|
||||
[^59]: Glenn K. Lockwood. [Hadoop’s Uncomfortable Fit in HPC](https://blog.glennklockwood.com/2014/05/hadoops-uncomfortable-fit-in-hpc.html). *glennklockwood.blogspot.co.uk*, May 2014. Archived at [perma.cc/S8XX-Y67B](https://perma.cc/S8XX-Y67B)
|
||||
[^60]: Cathy O’Neil: *Weapons of Math Destruction: How Big Data Increases Inequality and Threatens Democracy*. Crown Publishing, 2016. ISBN: 9780553418811
|
||||
[^61]: Supreeth Shastri, Vinay Banakar, Melissa Wasserman, Arun Kumar, and Vijay Chidambaram. [Understanding and Benchmarking the Impact of GDPR on Database Systems](https://www.vldb.org/pvldb/vol13/p1064-shastri.pdf). *Proceedings of the VLDB Endowment*, volume 13, issue 7, pages 1064–1077, March 2020. [doi:10.14778/3384345.3384354](https://doi.org/10.14778/3384345.3384354)
|
||||
[^62]: Martin Fowler. [Datensparsamkeit](https://www.martinfowler.com/bliki/Datensparsamkeit.html). *martinfowler.com*, December 2013. Archived at [perma.cc/R9QX-CME6](https://perma.cc/R9QX-CME6)
|
||||
[^63]: [Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 (General Data Protection Regulation)](https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:32016R0679&from=EN). *Official Journal of the European Union* L 119/1, May 2016.
|
||||
File diff suppressed because it is too large
Load diff
1467
content/tw/ch11.md
1467
content/tw/ch11.md
File diff suppressed because it is too large
Load diff
1295
content/tw/ch12.md
1295
content/tw/ch12.md
File diff suppressed because it is too large
Load diff
1010
content/tw/ch13.md
Normal file
1010
content/tw/ch13.md
Normal file
File diff suppressed because it is too large
Load diff
1124
content/tw/ch2.md
1124
content/tw/ch2.md
File diff suppressed because it is too large
Load diff
1466
content/tw/ch3.md
1466
content/tw/ch3.md
File diff suppressed because it is too large
Load diff
1087
content/tw/ch4.md
1087
content/tw/ch4.md
File diff suppressed because it is too large
Load diff
1327
content/tw/ch5.md
1327
content/tw/ch5.md
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1532
content/tw/ch8.md
1532
content/tw/ch8.md
File diff suppressed because it is too large
Load diff
1670
content/tw/ch9.md
1670
content/tw/ch9.md
File diff suppressed because it is too large
Load diff
|
|
@ -4,6 +4,10 @@ weight: 600
|
|||
breadcrumbs: false
|
||||
---
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
當前頁面來自本書第一版,第二版尚不可用
|
||||
{{< /callout >}}
|
||||
|
||||
## 關於作者
|
||||
|
||||
**Martin Kleppmann** 是英國劍橋大學分散式系統的研究員。此前他曾在網際網路公司擔任過軟體工程師和企業家,其中包括 LinkedIn 和 Rapportive,負責大規模資料基礎架構。在這個過程中,他以艱難的方式學習了一些東西,他希望這本書能夠讓你避免重蹈覆轍。
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ weight: 500
|
|||
breadcrumbs: false
|
||||
---
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
當前頁面來自本書第一版,第二版尚不可用
|
||||
{{< /callout >}}
|
||||
|
||||
> 請注意,本術語表中的定義簡短而簡單,旨在傳達核心思想,而非死扣完整細節。有關更多詳細資訊,請參閱正文中的參考資料。
|
||||
|
||||
|
||||
|
|
@ -61,9 +65,9 @@ breadcrumbs: false
|
|||
|
||||
為了加速讀取,在標準資料集中引入一些冗餘或重複資料,通常採用快取或索引的形式。非規範化的值是一種預先計算的查詢結果,像物化檢視。請參閱“[單物件和多物件操作](/tw/ch7#單物件和多物件操作)”和“[從同一事件日誌中派生多個檢視](/tw/ch11#從同一事件日誌中派生多個檢視)”。
|
||||
|
||||
## **衍生資料(derived data)**
|
||||
## **派生資料(derived data)**
|
||||
|
||||
一種資料集,根據其他資料透過可重複執行的流程建立。必要時,你可以執行該流程再次建立衍生資料。衍生資料通常用於提高特定資料的讀取速度。常見的衍生資料有索引、快取和物化檢視。請參閱[第三部分](/tw/part-iii)的介紹。
|
||||
一種資料集,根據其他資料透過可重複執行的流程建立。必要時,你可以執行該流程再次建立派生資料。派生資料通常用於提高特定資料的讀取速度。常見的派生資料有索引、快取和物化檢視。請參閱[第三部分](/tw/part-iii)的介紹。
|
||||
|
||||
## **確定性(deterministic)**
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ weight: 100
|
|||
breadcrumbs: false
|
||||
---
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
當前頁面來自本書第一版,第二版尚不可用
|
||||
{{< /callout >}}
|
||||
|
||||
本書前四章介紹了資料系統底層的基礎概念,無論是在單臺機器上執行的單點資料系統,還是分佈在多臺機器上的分散式資料系統都適用。
|
||||
|
||||
1. [第一章](/tw/ch1) 將介紹本書使用的術語和方法。**可靠性,可伸縮性和可維護性** ,這些詞彙到底意味著什麼?如何實現這些目標?
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ weight: 200
|
|||
breadcrumbs: false
|
||||
---
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
當前頁面來自本書第一版,第二版尚不可用
|
||||
{{< /callout >}}
|
||||
|
||||
> 一個成功的技術,現實的優先順序必須高於公關,你可以糊弄別人,但糊弄不了自然規律。
|
||||
>
|
||||
> —— 羅傑斯委員會報告(1986)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
---
|
||||
title: 第三部分:衍生資料
|
||||
title: 第三部分:派生資料
|
||||
weight: 300
|
||||
breadcrumbs: false
|
||||
---
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
當前頁面來自本書第一版,第二版尚不可用
|
||||
{{< /callout >}}
|
||||
|
||||
在本書的 [第一部分](/tw/part-i) 和 [第二部分](/tw/part-ii) 中,我們自底向上地把所有關於分散式資料庫的主要考量都過了一遍。從資料在磁碟上的佈局,一直到出現故障時分散式系統一致性的侷限。但所有的討論都假定了應用中只用了一種資料庫。
|
||||
|
||||
|
|
@ -11,7 +14,7 @@ breadcrumbs: false
|
|||
|
||||
本書的最後一部分,會研究將多個不同資料系統(可能有著不同資料模型,並針對不同的訪問模式進行最佳化)整合為一個協調一致的應用架構時,會遇到的問題。軟體供應商經常會忽略這一方面的生態建設,並聲稱他們的產品能夠滿足你的所有需求。在現實世界中,整合不同的系統是實際應用中最重要的事情之一。
|
||||
|
||||
## 記錄系統和衍生資料系統
|
||||
## 記錄系統和派生資料系統
|
||||
|
||||
從高層次上看,儲存和處理資料的系統可以分為兩大類:
|
||||
|
||||
|
|
@ -19,37 +22,38 @@ breadcrumbs: false
|
|||
|
||||
**記錄系統**,也被稱為 **真相源(source of truth)**,持有資料的權威版本。當新的資料進入時(例如,使用者輸入)首先會記錄在這裡。每個事實正正好好表示一次(表示通常是 **正規化的**,即 normalized)。如果其他系統和 **記錄系統** 之間存在任何差異,那麼記錄系統中的值是正確的(根據定義)。
|
||||
|
||||
* 衍生資料系統(Derived data systems)
|
||||
* 派生資料系統(Derived data systems)
|
||||
|
||||
**衍生系統** 中的資料,通常是另一個系統中的現有資料以某種方式進行轉換或處理的結果。如果丟失衍生資料,可以從原始來源重新建立。典型的例子是 **快取(cache)**:如果資料在快取中,就可以由快取提供服務;如果快取不包含所需資料,則降級由底層資料庫提供。非規範化的值,索引和物化檢視亦屬此類。在推薦系統中,預測彙總資料通常衍生自使用者日誌。
|
||||
**派生系統** 中的資料,通常是另一個系統中的現有資料以某種方式進行轉換或處理的結果。如果丟失派生資料,可以從原始來源重新建立。典型的例子是 **快取(cache)**:如果資料在快取中,就可以由快取提供服務;如果快取不包含所需資料,則降級由底層資料庫提供。非規範化的值,索引和物化檢視亦屬此類。在推薦系統中,預測彙總資料通常衍生自使用者日誌。
|
||||
|
||||
從技術上講,衍生資料是 **冗餘的(redundant)**,因為它重複了已有的資訊。但是衍生資料對於獲得良好的只讀查詢效能通常是至關重要的。它通常是非規範化的。可以從單個源頭衍生出多個不同的資料集,使你能從不同的 “視角” 洞察資料。
|
||||
從技術上講,派生資料是 **冗餘的(redundant)**,因為它重複了已有的資訊。但是派生資料對於獲得良好的只讀查詢效能通常是至關重要的。它通常是非規範化的。可以從單個源頭衍生出多個不同的資料集,使你能從不同的 “視角” 洞察資料。
|
||||
|
||||
並不是所有的系統都在其架構中明確區分 **記錄系統** 和 **衍生資料系統**,但是這是一種有用的區分方式,因為它明確了系統中的資料流:系統的哪一部分具有哪些輸入和哪些輸出,以及它們如何相互依賴。
|
||||
並不是所有的系統都在其架構中明確區分 **記錄系統** 和 **派生資料系統**,但是這是一種有用的區分方式,因為它明確了系統中的資料流:系統的哪一部分具有哪些輸入和哪些輸出,以及它們如何相互依賴。
|
||||
|
||||
大多數資料庫,儲存引擎和查詢語言,本質上既不是記錄系統也不是衍生系統。資料庫只是一個工具:如何使用它取決於你自己。**記錄系統和衍生資料系統之間的區別不在於工具,而在於應用程式中的使用方式。**
|
||||
大多數資料庫,儲存引擎和查詢語言,本質上既不是記錄系統也不是派生系統。資料庫只是一個工具:如何使用它取決於你自己。**記錄系統和派生資料系統之間的區別不在於工具,而在於應用程式中的使用方式。**
|
||||
|
||||
透過梳理資料的衍生關係,可以清楚地理解一個令人困惑的系統架構。這將貫穿本書的這一部分。
|
||||
|
||||
## 章節概述
|
||||
|
||||
我們將從 [第十章](/tw/ch10) 開始,研究例如 MapReduce 這樣 **面向批處理(batch-oriented)** 的資料流系統。對於建設大規模資料系統,我們將看到,它們提供了優秀的工具和思想。[第十一章](/tw/ch11) 將把這些思想應用到 **流式資料(data streams)** 中,使我們能用更低的延遲完成同樣的任務。[第十二章](/tw/ch12) 將對本書進行總結,探討如何使用這些工具來構建可靠,可伸縮和可維護的應用。
|
||||
我們將從 [第十一章](/tw/ch11) 開始,研究例如 MapReduce 這樣 **面向批處理(batch-oriented)** 的資料流系統。對於建設大規模資料系統,我們將看到,它們提供了優秀的工具和思想。
|
||||
[第十二章](/tw/ch12) 將把這些思想應用到 **流式資料(data streams)** 中,使我們能用更低的延遲完成同樣的任務。[第十三章](/ch13) 將對本書進行總結,探討如何使用這些工具來構建可靠,可伸縮和可維護的應用。
|
||||
|
||||
## 索引
|
||||
|
||||
* [第十章:批處理](/tw/ch10)
|
||||
* [使用Unix工具的批處理](/tw/ch10#使用Unix工具的批處理)
|
||||
* [MapReduce和分散式檔案系統](/tw/ch10#MapReduce和分散式檔案系統)
|
||||
* [MapReduce之後](/tw/ch10#MapReduce之後)
|
||||
* [本章小結](/tw/ch10#本章小結)
|
||||
* [第十一章:流處理](/tw/ch11)
|
||||
* [傳遞事件流](/tw/ch11#傳遞事件流)
|
||||
* [資料庫與流](/tw/ch11#資料庫與流)
|
||||
* [流處理](/tw/ch11#流處理)
|
||||
* [第十章:批處理](/tw/ch11)
|
||||
* [使用Unix工具的批處理](/tw/ch11#使用Unix工具的批處理)
|
||||
* [MapReduce和分散式檔案系統](/tw/ch11#MapReduce和分散式檔案系統)
|
||||
* [MapReduce之後](/tw/ch11#MapReduce之後)
|
||||
* [本章小結](/tw/ch11#本章小結)
|
||||
* [第十二章:資料系統的未來](/tw/ch12)
|
||||
* [資料整合](/tw/ch12#資料整合)
|
||||
* [分拆資料庫](/tw/ch12#分拆資料庫)
|
||||
* [將事情做正確](/tw/ch12#將事情做正確)
|
||||
* [做正確的事情](/tw/ch12#做正確的事情)
|
||||
* [本章小結](/tw/ch12#本章小結)
|
||||
* [第十一章:流處理](/tw/ch12)
|
||||
* [傳遞事件流](/tw/ch12#傳遞事件流)
|
||||
* [資料庫與流](/tw/ch12#資料庫與流)
|
||||
* [流處理](/tw/ch12#流處理)
|
||||
* [本章小結](/tw/ch12#本章小結)
|
||||
* [第十三章:做正確的事](/ch13)
|
||||
* [資料整合](/ch13#資料整合)
|
||||
* [分拆資料庫](/ch13#分拆資料庫)
|
||||
* [將事情做正確](/ch13#將事情做正確)
|
||||
* [做正確的事情](/ch13#做正確的事情)
|
||||
* [本章小結](/ch13#本章小結)
|
||||
|
|
@ -4,6 +4,9 @@ weight: 50
|
|||
breadcrumbs: false
|
||||
---
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
當前頁面來自本書第一版,第二版尚不可用
|
||||
{{< /callout >}}
|
||||
|
||||
如果近幾年從業於軟體工程,特別是伺服器端和後端系統開發,那麼你很有可能已經被大量關於資料儲存和處理的時髦詞彙轟炸過了: NoSQL!大資料!Web-Scale!分片!最終一致性!ACID!CAP 定理!雲服務!MapReduce!即時!
|
||||
|
||||
|
|
@ -69,7 +72,7 @@ breadcrumbs: false
|
|||
|
||||
2. 在 [第二部分](/tw/part-ii) 中,我們從討論儲存在一臺機器上的資料轉向討論分佈在多臺機器上的資料。這對於可伸縮性通常是必需的,但帶來了各種獨特的挑戰。我們首先討論複製([第五章](/tw/ch5))、分割槽 / 分片([第六章](/tw/ch6))和事務([第七章](/tw/ch7))。然後我們將探索關於分散式系統問題的更多細節([第八章](/tw/ch8)),以及在分散式系統中實現一致性與共識意味著什麼([第九章](/tw/ch9))。
|
||||
|
||||
3. 在 [第三部分](/tw/part-iii) 中,我們討論那些從其他資料集衍生出一些資料集的系統。衍生資料經常出現在異構系統中:當沒有單個數據庫可以把所有事情都做的很好時,應用需要整合幾種不同的資料庫、快取、索引等。在 [第十章](/tw/ch10) 中我們將從一種衍生資料的批處理方法開始,然後在此基礎上建立在 [第十一章](/tw/ch11) 中討論的流處理。最後,在 [第十二章](/tw/ch12) 中,我們將所有內容彙總,討論在將來構建可靠、可伸縮和可維護的應用程式的方法。
|
||||
3. 在 [第三部分](/tw/part-iii) 中,我們討論那些從其他資料集衍生出一些資料集的系統。派生資料經常出現在異構系統中:當沒有單個數據庫可以把所有事情都做的很好時,應用需要整合幾種不同的資料庫、快取、索引等。在 [第十一章](/tw/ch11) 中我們將從一種派生資料的批處理方法開始,然後在此基礎上建立在 [第十二章](/tw/ch12) 中討論的流處理。最後,在 [第十三章](/ch13) 中,我們將所有內容彙總,討論在將來構建可靠、可伸縮和可維護的應用程式的方法。
|
||||
|
||||
|
||||
## 參考文獻與延伸閱讀
|
||||
|
|
|
|||
|
|
@ -6,87 +6,213 @@ breadcrumbs: false
|
|||
---
|
||||
|
||||
|
||||

|
||||
|
||||
## [序言](/tw/preface)
|
||||

|
||||
|
||||
## [第一部分:資料系統基礎](/tw/part-i)
|
||||
|
||||
### [第一章:可靠性、可伸縮性和可維護性](/tw/ch1)
|
||||
* [關於資料系統的思考](/tw/ch1#關於資料系統的思考)
|
||||
* [可靠性](/tw/ch1#可靠性)
|
||||
* [可伸縮性](/tw/ch1#可伸縮性)
|
||||
* [可維護性](/tw/ch1#可維護性)
|
||||
* [本章小結](/tw/ch1#本章小結)
|
||||
### [第二章:資料模型與查詢語言](/tw/ch2)
|
||||
* [關係模型與文件模型](/tw/ch2#關係模型與文件模型)
|
||||
* [資料查詢語言](/tw/ch2#資料查詢語言)
|
||||
* [圖資料模型](/tw/ch2#圖資料模型)
|
||||
* [本章小結](/tw/ch2#本章小結)
|
||||
### [第三章:儲存與檢索](/tw/ch3)
|
||||
* [驅動資料庫的資料結構](/tw/ch3#驅動資料庫的資料結構)
|
||||
* [事務處理還是分析?](/tw/ch3#事務處理還是分析)
|
||||
* [列式儲存](/tw/ch3#列式儲存)
|
||||
* [本章小結](/tw/ch3#本章小結)
|
||||
### [第四章:編碼與演化](/tw/ch4)
|
||||
* [編碼資料的格式](/tw/ch4#編碼資料的格式)
|
||||
* [資料流的型別](/tw/ch4#資料流的型別)
|
||||
* [本章小結](/tw/ch4#本章小結)
|
||||
## [序言](/tw/preface)
|
||||
|
||||
## [第二部分:分散式資料](/tw/part-ii)
|
||||
## [1. 資料系統架構中的權衡](/tw/ch1)
|
||||
|
||||
### [第五章:複製](/tw/ch5)
|
||||
* [領導者與追隨者](/tw/ch5#領導者與追隨者)
|
||||
* [複製延遲問題](/tw/ch5#複製延遲問題)
|
||||
* [多主複製](/tw/ch5#多主複製)
|
||||
* [無主複製](/tw/ch5#無主複製)
|
||||
* [本章小結](/tw/ch5#本章小結)
|
||||
### [第六章:分割槽](/tw/ch6)
|
||||
* [分割槽與複製](/tw/ch6#分割槽與複製)
|
||||
* [鍵值資料的分割槽](/tw/ch6#鍵值資料的分割槽)
|
||||
* [分割槽與次級索引](/tw/ch6#分割槽與次級索引)
|
||||
* [分割槽再平衡](/tw/ch6#分割槽再平衡)
|
||||
* [請求路由](/tw/ch6#請求路由)
|
||||
* [本章小結](/tw/ch6#本章小結)
|
||||
### [第七章:事務](/tw/ch7)
|
||||
* [事務的棘手概念](/tw/ch7#事務的棘手概念)
|
||||
* [弱隔離級別](/tw/ch7#弱隔離級別)
|
||||
* [可序列化](/tw/ch7#可序列化)
|
||||
* [本章小結](/tw/ch7#本章小結)
|
||||
### [第八章:分散式系統的麻煩](/tw/ch8)
|
||||
* [故障與部分失效](/tw/ch8#故障與部分失效)
|
||||
* [不可靠的網路](/tw/ch8#不可靠的網路)
|
||||
* [不可靠的時鐘](/tw/ch8#不可靠的時鐘)
|
||||
* [知識、真相與謊言](/tw/ch8#知識真相與謊言)
|
||||
* [本章小結](/tw/ch8#本章小結)
|
||||
### [第九章:一致性與共識](/tw/ch9)
|
||||
* [一致性保證](/tw/ch9#一致性保證)
|
||||
* [線性一致性](/tw/ch9#線性一致性)
|
||||
* [順序保證](/tw/ch9#順序保證)
|
||||
* [分散式事務與共識](/tw/ch9#分散式事務與共識)
|
||||
* [本章小結](/tw/ch9#本章小結)
|
||||
- [分析型與事務型系統](/tw/ch1#sec_introduction_analytics)
|
||||
- [事務處理與分析的特徵](/tw/ch1#sec_introduction_oltp)
|
||||
- [資料倉庫](/tw/ch1#sec_introduction_dwh)
|
||||
- [權威資料來源與派生資料](/tw/ch1#sec_introduction_derived)
|
||||
- [雲服務與自託管](/tw/ch1#sec_introduction_cloud)
|
||||
- [雲服務的利弊](/tw/ch1#sec_introduction_cloud_tradeoffs)
|
||||
- [雲原生系統架構](/tw/ch1#sec_introduction_cloud_native)
|
||||
- [雲時代的運維](/tw/ch1#sec_introduction_operations)
|
||||
- [分散式與單節點系統](/tw/ch1#sec_introduction_distributed)
|
||||
- [分散式系統的問題](/tw/ch1#sec_introduction_dist_sys_problems)
|
||||
- [微服務與 Serverless](/tw/ch1#sec_introduction_microservices)
|
||||
- [雲計算與超級計算](/tw/ch1#id17)
|
||||
- [資料系統、法律與社會](/tw/ch1#sec_introduction_compliance)
|
||||
- [總結](/tw/ch1#summary)
|
||||
|
||||
## [第三部分:衍生資料](/tw/part-iii)
|
||||
|
||||
### [第十章:批處理](/tw/ch10)
|
||||
* [使用Unix工具的批處理](/tw/ch10#使用Unix工具的批處理)
|
||||
* [MapReduce和分散式檔案系統](/tw/ch10#MapReduce和分散式檔案系統)
|
||||
* [MapReduce之後](/tw/ch10#MapReduce之後)
|
||||
* [本章小結](/tw/ch10#本章小結)
|
||||
### [第十一章:流處理](/tw/ch11)
|
||||
* [傳遞事件流](/tw/ch11#傳遞事件流)
|
||||
* [資料庫與流](/tw/ch11#資料庫與流)
|
||||
* [流處理](/tw/ch11#流處理)
|
||||
* [本章小結](/tw/ch11#本章小結)
|
||||
### [第十二章:資料系統的未來](/tw/ch12)
|
||||
* [資料整合](/tw/ch12#資料整合)
|
||||
* [分拆資料庫](/tw/ch12#分拆資料庫)
|
||||
* [將事情做正確](/tw/ch12#將事情做正確)
|
||||
* [做正確的事情](/tw/ch12#做正確的事情)
|
||||
* [本章小結](/tw/ch12#本章小結)
|
||||
## [2. 定義非功能性需求](/tw/ch2)
|
||||
|
||||
### [術語表](/tw/glossary)
|
||||
- [案例研究:社交網路首頁時間線](/tw/ch2#sec_introduction_twitter)
|
||||
- [表示使用者、帖子與關注關係](/tw/ch2#id20)
|
||||
- [時間線的物化與更新](/tw/ch2#sec_introduction_materializing)
|
||||
- [描述效能](/tw/ch2#sec_introduction_percentiles)
|
||||
- [延遲與響應時間](/tw/ch2#id23)
|
||||
- [平均值、中位數與百分位數](/tw/ch2#id24)
|
||||
- [響應時間指標的應用](/tw/ch2#sec_introduction_slo_sla)
|
||||
- [可靠性與容錯](/tw/ch2#sec_introduction_reliability)
|
||||
- [容錯](/tw/ch2#id27)
|
||||
- [硬體與軟體故障](/tw/ch2#sec_introduction_hardware_faults)
|
||||
- [人類與可靠性](/tw/ch2#id31)
|
||||
- [可伸縮性](/tw/ch2#sec_introduction_scalability)
|
||||
- [描述負載](/tw/ch2#id33)
|
||||
- [共享記憶體、共享磁碟與無共享架構](/tw/ch2#sec_introduction_shared_nothing)
|
||||
- [可伸縮性原則](/tw/ch2#id35)
|
||||
- [可維護性](/tw/ch2#sec_introduction_maintainability)
|
||||
- [可操作性:讓運維更輕鬆](/tw/ch2#id37)
|
||||
- [簡單性:管理複雜度](/tw/ch2#id38)
|
||||
- [可演化性:讓變化更容易](/tw/ch2#sec_introduction_evolvability)
|
||||
- [總結](/tw/ch2#summary)
|
||||
|
||||
### [後記](/tw/colophon)
|
||||
|
||||

|
||||
## [3. 資料模型與查詢語言](/tw/ch3)
|
||||
|
||||
- [關係模型與文件模型](/tw/ch3#sec_datamodels_history)
|
||||
- [物件關係不匹配](/tw/ch3#sec_datamodels_document)
|
||||
- [規範化、反規範化與連線](/tw/ch3#sec_datamodels_normalization)
|
||||
- [多對一與多對多關係](/tw/ch3#sec_datamodels_many_to_many)
|
||||
- [星型與雪花型:分析模式](/tw/ch3#sec_datamodels_analytics)
|
||||
- [何時使用哪種模型](/tw/ch3#sec_datamodels_document_summary)
|
||||
- [圖資料模型](/tw/ch3#sec_datamodels_graph)
|
||||
- [屬性圖](/tw/ch3#id56)
|
||||
- [Cypher 查詢語言](/tw/ch3#id57)
|
||||
- [SQL 中的圖查詢](/tw/ch3#id58)
|
||||
- [三元組儲存與 SPARQL](/tw/ch3#id59)
|
||||
- [Datalog:遞迴關係查詢](/tw/ch3#id62)
|
||||
- [GraphQL](/tw/ch3#id63)
|
||||
- [事件溯源與 CQRS](/tw/ch3#sec_datamodels_events)
|
||||
- [資料框、矩陣與陣列](/tw/ch3#sec_datamodels_dataframes)
|
||||
- [總結](/tw/ch3#summary)
|
||||
|
||||
|
||||
## [4. 儲存與檢索](/tw/ch4)
|
||||
|
||||
- [OLTP 系統的儲存與索引](/tw/ch4#sec_storage_oltp)
|
||||
- [日誌結構儲存](/tw/ch4#sec_storage_log_structured)
|
||||
- [B 樹](/tw/ch4#sec_storage_b_trees)
|
||||
- [比較 B 樹與 LSM 樹](/tw/ch4#sec_storage_btree_lsm_comparison)
|
||||
- [多列索引與二級索引](/tw/ch4#sec_storage_index_multicolumn)
|
||||
- [全記憶體儲存](/tw/ch4#sec_storage_inmemory)
|
||||
- [分析型資料儲存](/tw/ch4#sec_storage_analytics)
|
||||
- [雲資料倉庫](/tw/ch4#sec_cloud_data_warehouses)
|
||||
- [列式儲存](/tw/ch4#sec_storage_column)
|
||||
- [查詢執行:編譯與向量化](/tw/ch4#sec_storage_vectorized)
|
||||
- [物化檢視與多維資料集](/tw/ch4#sec_storage_materialized_views)
|
||||
- [多維索引與全文索引](/tw/ch4#sec_storage_multidimensional)
|
||||
- [全文檢索](/tw/ch4#sec_storage_full_text)
|
||||
- [向量嵌入](/tw/ch4#id92)
|
||||
- [總結](/tw/ch4#summary)
|
||||
|
||||
|
||||
## [5. 編碼與演化](/tw/ch5)
|
||||
|
||||
- [編碼資料的格式](/tw/ch5#sec_encoding_formats)
|
||||
- [特定語言的格式](/tw/ch5#id96)
|
||||
- [JSON、XML 及其二進位制變體](/tw/ch5#sec_encoding_json)
|
||||
- [Protocol Buffers](/tw/ch5#sec_encoding_protobuf)
|
||||
- [Avro](/tw/ch5#sec_encoding_avro)
|
||||
- [模式的優點](/tw/ch5#sec_encoding_schemas)
|
||||
- [資料流的模式](/tw/ch5#sec_encoding_dataflow)
|
||||
- [流經資料庫的資料流](/tw/ch5#sec_encoding_dataflow_db)
|
||||
- [流經服務的資料流:REST 與 RPC](/tw/ch5#sec_encoding_dataflow_rpc)
|
||||
- [持久化執行與工作流](/tw/ch5#sec_encoding_dataflow_workflows)
|
||||
- [事件驅動的架構](/tw/ch5#sec_encoding_dataflow_msg)
|
||||
- [總結](/tw/ch5#summary)
|
||||
|
||||
|
||||
## [6. 複製](/tw/ch6)
|
||||
|
||||
- [單主複製](/tw/ch6#sec_replication_leader)
|
||||
- [同步複製與非同步複製](/tw/ch6#sec_replication_sync_async)
|
||||
- [設定新的副本](/tw/ch6#sec_replication_new_replica)
|
||||
- [處理節點故障](/tw/ch6#sec_replication_failover)
|
||||
- [複製日誌的實現](/tw/ch6#sec_replication_implementation)
|
||||
- [複製延遲的問題](/tw/ch6#sec_replication_lag)
|
||||
- [讀己之寫](/tw/ch6#sec_replication_ryw)
|
||||
- [單調讀](/tw/ch6#sec_replication_monotonic_reads)
|
||||
- [一致字首讀](/tw/ch6#sec_replication_consistent_prefix)
|
||||
- [複製延遲的解決方案](/tw/ch6#id131)
|
||||
- [多主複製](/tw/ch6#sec_replication_multi_leader)
|
||||
- [跨地域執行](/tw/ch6#sec_replication_multi_dc)
|
||||
- [同步引擎與本地優先軟體](/tw/ch6#sec_replication_offline_clients)
|
||||
- [處理寫入衝突](/tw/ch6#sec_replication_write_conflicts)
|
||||
- [CRDT 與操作變換](/tw/ch6#sec_replication_crdts)
|
||||
- [無主複製](/tw/ch6#sec_replication_leaderless)
|
||||
- [當節點故障時寫入資料庫](/tw/ch6#id287)
|
||||
- [仲裁一致性的侷限](/tw/ch6#sec_replication_quorum_limitations)
|
||||
- [單主與無主複製的效能](/tw/ch6#sec_replication_leaderless_perf)
|
||||
- [檢測併發寫入](/tw/ch6#sec_replication_concurrent)
|
||||
- [總結](/tw/ch6#summary)
|
||||
|
||||
|
||||
## [7. 分片](/tw/ch7)
|
||||
|
||||
- [分片的利與弊](/tw/ch7#sec_sharding_reasons)
|
||||
- [面向多租戶的分片](/tw/ch7#sec_sharding_multitenancy)
|
||||
- [鍵值資料的分片](/tw/ch7#sec_sharding_key_value)
|
||||
- [按鍵的範圍分片](/tw/ch7#sec_sharding_key_range)
|
||||
- [按鍵的雜湊分片](/tw/ch7#sec_sharding_hash)
|
||||
- [傾斜的工作負載與緩解熱點](/tw/ch7#sec_sharding_skew)
|
||||
- [運維:自動/手動再均衡](/tw/ch7#sec_sharding_operations)
|
||||
- [請求路由](/tw/ch7#sec_sharding_routing)
|
||||
- [分片與二級索引](/tw/ch7#sec_sharding_secondary_indexes)
|
||||
- [本地二級索引](/tw/ch7#id166)
|
||||
- [全域性二級索引](/tw/ch7#id167)
|
||||
- [總結](/tw/ch7#summary)
|
||||
|
||||
|
||||
## [8. 事務](/tw/ch8)
|
||||
|
||||
- [事務到底是什麼?](/tw/ch8#sec_transactions_overview)
|
||||
- [ACID 的含義](/tw/ch8#sec_transactions_acid)
|
||||
- [單物件與多物件操作](/tw/ch8#sec_transactions_multi_object)
|
||||
- [弱隔離級別](/tw/ch8#sec_transactions_isolation_levels)
|
||||
- [讀已提交](/tw/ch8#sec_transactions_read_committed)
|
||||
- [快照隔離與可重複讀](/tw/ch8#sec_transactions_snapshot_isolation)
|
||||
- [防止丟失更新](/tw/ch8#sec_transactions_lost_update)
|
||||
- [寫偏斜與幻讀](/tw/ch8#sec_transactions_write_skew)
|
||||
- [可序列化](/tw/ch8#sec_transactions_serializability)
|
||||
- [實際序列執行](/tw/ch8#sec_transactions_serial)
|
||||
- [兩階段鎖定(2PL)](/tw/ch8#sec_transactions_2pl)
|
||||
- [可序列化快照隔離(SSI)](/tw/ch8#sec_transactions_ssi)
|
||||
- [分散式事務](/tw/ch8#sec_transactions_distributed)
|
||||
- [兩階段提交(2PC)](/tw/ch8#sec_transactions_2pc)
|
||||
- [跨不同系統的分散式事務](/tw/ch8#sec_transactions_xa)
|
||||
- [資料庫內部的分散式事務](/tw/ch8#sec_transactions_internal)
|
||||
- [總結](/tw/ch8#summary)
|
||||
|
||||
|
||||
## [9. 分散式系統的麻煩](/tw/ch9)
|
||||
|
||||
- [故障與部分失效](/tw/ch9#sec_distributed_partial_failure)
|
||||
- [不可靠的網路](/tw/ch9#sec_distributed_networks)
|
||||
- [TCP 的侷限性](/tw/ch9#sec_distributed_tcp)
|
||||
- [實踐中的網路故障](/tw/ch9#sec_distributed_network_faults)
|
||||
- [故障檢測](/tw/ch9#id307)
|
||||
- [超時與無界延遲](/tw/ch9#sec_distributed_queueing)
|
||||
- [同步網路與非同步網路](/tw/ch9#sec_distributed_sync_networks)
|
||||
- [不可靠的時鐘](/tw/ch9#sec_distributed_clocks)
|
||||
- [單調時鐘與日曆時鐘](/tw/ch9#sec_distributed_monotonic_timeofday)
|
||||
- [時鐘同步與準確性](/tw/ch9#sec_distributed_clock_accuracy)
|
||||
- [對同步時鐘的依賴](/tw/ch9#sec_distributed_clocks_relying)
|
||||
- [程序暫停](/tw/ch9#sec_distributed_clocks_pauses)
|
||||
- [知識、真相與謊言](/tw/ch9#sec_distributed_truth)
|
||||
- [多數派原則](/tw/ch9#sec_distributed_majority)
|
||||
- [分散式鎖與租約](/tw/ch9#sec_distributed_lock_fencing)
|
||||
- [拜占庭故障](/tw/ch9#sec_distributed_byzantine)
|
||||
- [系統模型與現實](/tw/ch9#sec_distributed_system_model)
|
||||
- [形式化方法與隨機測試](/tw/ch9#sec_distributed_formal)
|
||||
- [總結](/tw/ch9#summary)
|
||||
|
||||
|
||||
## [10. 一致性與共識](/tw/ch10)
|
||||
|
||||
- [線性一致性](/tw/ch10#sec_consistency_linearizability)
|
||||
- [什麼使系統具有線性一致性?](/tw/ch10#sec_consistency_lin_definition)
|
||||
- [依賴線性一致性](/tw/ch10#sec_consistency_linearizability_usage)
|
||||
- [實現線性一致性系統](/tw/ch10#sec_consistency_implementing_linearizable)
|
||||
- [線性一致性的代價](/tw/ch10#sec_linearizability_cost)
|
||||
- [ID 生成器與邏輯時鐘](/tw/ch10#sec_consistency_logical)
|
||||
- [邏輯時鐘](/tw/ch10#sec_consistency_timestamps)
|
||||
- [線性一致的 ID 生成器](/tw/ch10#sec_consistency_linearizable_id)
|
||||
- [共識](/tw/ch10#sec_consistency_consensus)
|
||||
- [共識的多面性](/tw/ch10#sec_consistency_faces)
|
||||
- [共識的實踐](/tw/ch10#sec_consistency_total_order)
|
||||
- [協調服務](/tw/ch10#sec_consistency_coordination)
|
||||
- [總結](/tw/ch10#summary)
|
||||
|
||||
|
||||
## [11. 批處理](/tw/ch11)(未釋出)
|
||||
## [12. 流處理](/tw/ch12)(未釋出)
|
||||
## [13. 做正確的事](/ch13)(未釋出)
|
||||
## [術語表](/tw/glossary)
|
||||
## [後記](/tw/colophon)
|
||||
|
|
@ -1,316 +0,0 @@
|
|||
---
|
||||
title: 设计数据密集型应用(第二版)
|
||||
linkTitle: DDIA
|
||||
cascade:
|
||||
type: docs
|
||||
breadcrumbs: false
|
||||
---
|
||||
|
||||
|
||||
**作者**: [Martin Kleppmann](https://martin.kleppmann.com),[《Designing Data-Intensive Applications 2nd Edition》](https://learning.oreilly.com/library/view/designing-data-intensive-applications/9781098119058/ch01.html) : 英国剑桥大学分布式系统研究员,演讲者,博主和开源贡献者,软件工程师和企业家,曾在 LinkedIn 和 Rapportive 负责数据基础架构。
|
||||
|
||||
**译者**:[**冯若航**](https://vonng.com),网名 [@Vonng](https://github.com/Vonng)。
|
||||
PostgreSQL 专家,数据库老司机,云计算泥石流。
|
||||
[**Pigsty**](https://pgsty.com) 作者与创始人。
|
||||
架构师,DBA,全栈工程师 @ TanTan,Alibaba,Apple。
|
||||
独立开源贡献者,[GitStar Ranking 585](https://gitstar-ranking.com/Vonng),[国区活跃 Top20](https://committers.top/china)。
|
||||
[DDIA](https://ddia.pigsty.io) / [PG Internal](https://pgint.vonng.com) 中文版译者,公众号:《老冯云数》,数据库 KOL。
|
||||
|
||||
**校订**: [@yingang](https://github.com/yingang) | [繁體中文](/tw) **版本维护** by [@afunTW](https://github.com/afunTW) | [完整贡献者列表](/contrib)
|
||||
|
||||
> [!NOTE]
|
||||
> DDIA [**第二版**](https://github.com/Vonng/ddia/tree/v2) 正在翻译中 ([`hugo`](https://github.com/Vonng/ddia/tree/v2) 分支 `content/v2` 目录),欢迎加入并提出您的宝贵意见!
|
||||
|
||||
|
||||
## 译序
|
||||
|
||||
> 不懂数据库的全栈工程师不是好架构师 —— 冯若航 / Vonng
|
||||
|
||||
现今,尤其是在互联网领域,大多数应用都属于数据密集型应用。本书从底层数据结构到顶层架构设计,将数据系统设计中的精髓娓娓道来。其中的宝贵经验无论是对架构师、DBA、还是后端工程师、甚至产品经理都会有帮助。
|
||||
|
||||
这是一本理论结合实践的书,书中很多问题,译者在实际场景中都曾遇到过,读来让人击节扼腕。如果能早点读到这本书,该少走多少弯路啊!
|
||||
|
||||
这也是一本深入浅出的书,讲述概念的来龙去脉而不是卖弄定义,介绍事物发展演化历程而不是事实堆砌,将复杂的概念讲述的浅显易懂,但又直击本质不失深度。每章最后的引用质量非常好,是深入学习各个主题的绝佳索引。
|
||||
|
||||
本书为数据系统的设计、实现、与评价提供了很好的概念框架。读完并理解本书内容后,读者可以轻松看破大多数的技术忽悠,与技术砖家撕起来虎虎生风。
|
||||
|
||||
这是 2017 年译者读过最好的一本技术类书籍,这么好的书没有中文翻译,实在是遗憾。某不才,愿为先进技术文化的传播贡献一份力量。既可以深入学习有趣的技术主题,又可以锻炼中英文语言文字功底,何乐而不为?
|
||||
|
||||
|
||||
|
||||
## 前言
|
||||
|
||||
> 在我们的社会中,技术是一种强大的力量。数据、软件、通信可以用于坏的方面:不公平的阶级固化,损害公民权利,保护既得利益集团。但也可以用于好的方面:让底层人民发出自己的声音,让每个人都拥有机会,避免灾难。本书献给所有将技术用于善途的人们。
|
||||
|
||||
|
||||
> 计算是一种流行文化,流行文化鄙视历史。流行文化关乎个体身份和参与感,但与合作无关。流行文化活在当下,也与过去和未来无关。我认为大部分(为了钱)编写代码的人就是这样的,他们不知道自己的文化来自哪里。
|
||||
>
|
||||
> —— 阿兰・凯接受 Dobb 博士的杂志采访时(2012 年)
|
||||
|
||||
|
||||
|
||||
## 目录
|
||||
|
||||
### [序言](/preface)
|
||||
|
||||
### [第一部分:数据系统基础](/part-i)
|
||||
|
||||
* [第一章:数据系统架构中的利弊权衡](/v2/ch1)
|
||||
* [第二章:定义非功能性要求](/v2/ch2)
|
||||
* [第三章:数据模型与查询语言](/v2/ch3)
|
||||
* [第四章:编码与演化](/v2/ch4) (TBD)
|
||||
|
||||
### [第二部分:分布式数据](/part-ii)
|
||||
|
||||
* [第五章:复制](/v2/ch5)(TBD)
|
||||
* [第六章:分区](/v2/ch6)(TBD)
|
||||
* [第七章:事务](/v2/ch7)(TBD)
|
||||
* [第八章:分布式系统的麻烦](/v2/ch8)(TBD)
|
||||
* [第九章:一致性与共识](/v2/ch9)(TBD)
|
||||
|
||||
### [第三部分:衍生数据](/part-iii)
|
||||
|
||||
* [第十章:批处理](/v2/ch10)(尚未发布)
|
||||
* [第十一章:流处理](/v2/ch11)(尚未发布)
|
||||
* [第十二章:数据系统的未来](/v2/ch12)(尚未发布)
|
||||
|
||||
### [术语表](/v2/glossary)
|
||||
|
||||
### [后记](/v2/colophon)
|
||||
|
||||
<br>
|
||||
|
||||
---------
|
||||
|
||||
## 法律声明
|
||||
|
||||
从原作者处得知,已经有简体中文的翻译计划,将于 2018 年末完成。[购买地址](https://search.jd.com/Search?keyword=设计数据密集型应用)
|
||||
|
||||
译者纯粹出于 **学习目的** 与 **个人兴趣** 翻译本书,不追求任何经济利益。
|
||||
|
||||
译者保留对此版本译文的署名权,其他权利以原作者和出版社的主张为准。
|
||||
|
||||
本译文只供学习研究参考之用,不得公开传播发行或用于商业用途。有能力阅读英文书籍者请购买正版支持。
|
||||
|
||||
|
||||
---------
|
||||
|
||||
## 贡献
|
||||
|
||||
0. 全文校订 by [@yingang](https://github.com/Vonng/ddia/commits?author=yingang)
|
||||
1. [序言初翻修正](https://github.com/Vonng/ddia/commit/afb5edab55c62ed23474149f229677e3b42dfc2c) by [@seagullbird](https://github.com/Vonng/ddia/commits?author=seagullbird)
|
||||
2. [第一章语法标点校正](https://github.com/Vonng/ddia/commit/973b12cd8f8fcdf4852f1eb1649ddd9d187e3644) by [@nevertiree](https://github.com/Vonng/ddia/commits?author=nevertiree)
|
||||
3. [第六章部分校正](https://github.com/Vonng/ddia/commit/d4eb0852c0ec1e93c8aacc496c80b915bb1e6d48) 与[第十章的初翻](https://github.com/Vonng/ddia/commit/9de8dbd1bfe6fbb03b3bf6c1a1aa2291aed2490e) by [@MuAlex](https://github.com/Vonng/ddia/commits?author=MuAlex)
|
||||
4. [第一部分](/part-i)前言,[ch2](/v2/ch2)校正 by [@jiajiadebug](https://github.com/Vonng/ddia/commits?author=jiajiadebug)
|
||||
5. [词汇表](/glossary)、[后记](/colophon)关于野猪的部分 by [@Chowss](https://github.com/Vonng/ddia/commits?author=Chowss)
|
||||
6. [繁體中文](https://github.com/Vonng/ddia/pulls)版本与转换脚本 by [@afunTW](https://github.com/afunTW)
|
||||
7. 多处翻译修正 by [@songzhibin97](https://github.com/Vonng/ddia/commits?author=songzhibin97) [@MamaShip](https://github.com/Vonng/ddia/commits?author=MamaShip) [@FangYuan33](https://github.com/Vonng/ddia/commits?author=FangYuan33)
|
||||
8. [感谢所有作出贡献,提出意见的朋友们](/contrib):
|
||||
|
||||
<details>
|
||||
<summary><a href="https://github.com/Vonng/ddia/pulls">Pull Requests</a> & <a href="https://github.com/Vonng/ddia/issues">Issues</a></summary>
|
||||
|
||||
| ISSUE & Pull Requests | USER | Title |
|
||||
|-------------------------------------------------|------------------------------------------------------------|----------------------------------------------------------------|
|
||||
| [359](https://github.com/Vonng/ddia/pull/359) | [@c25423](https://github.com/c25423) | ch10: 修正一处拼写错误 |
|
||||
| [358](https://github.com/Vonng/ddia/pull/358) | [@lewiszlw](https://github.com/lewiszlw) | ch4: 修正一处拼写错误 |
|
||||
| [356](https://github.com/Vonng/ddia/pull/356) | [@lewiszlw](https://github.com/lewiszlw) | ch2: 修正一处标点错误 |
|
||||
| [355](https://github.com/Vonng/ddia/pull/355) | [@DuroyGeorge](https://github.com/DuroyGeorge) | ch12: 修正一处格式错误 |
|
||||
| [354](https://github.com/Vonng/ddia/pull/354) | [@justlorain](https://github.com/justlorain) | ch7: 修正一处参考链接 |
|
||||
| [353](https://github.com/Vonng/ddia/pull/353) | [@fantasyczl](https://github.com/fantasyczl) | ch3&9: 修正两处引用错误 |
|
||||
| [352](https://github.com/Vonng/ddia/pull/352) | [@fantasyczl](https://github.com/fantasyczl) | 支持输出为 EPUB 格式 |
|
||||
| [349](https://github.com/Vonng/ddia/pull/349) | [@xiyihan0](https://github.com/xiyihan0) | ch1: 修正一处格式错误 |
|
||||
| [348](https://github.com/Vonng/ddia/pull/348) | [@omegaatt36](https://github.com/omegaatt36) | ch3: 修正一处图像链接 |
|
||||
| [346](https://github.com/Vonng/ddia/issues/346) | [@Vermouth1995](https://github.com/Vermouth1995) | ch1: 优化一处翻译 |
|
||||
| [343](https://github.com/Vonng/ddia/pull/343) | [@kehao-chen](https://github.com/kehao-chen) | ch10: 优化一处翻译 |
|
||||
| [341](https://github.com/Vonng/ddia/pull/341) | [@YKIsTheBest](https://github.com/YKIsTheBest) | ch3: 优化两处翻译 |
|
||||
| [340](https://github.com/Vonng/ddia/pull/340) | [@YKIsTheBest](https://github.com/YKIsTheBest) | ch2: 优化多处翻译 |
|
||||
| [338](https://github.com/Vonng/ddia/pull/338) | [@YKIsTheBest](https://github.com/YKIsTheBest) | ch1: 优化一处翻译 |
|
||||
| [335](https://github.com/Vonng/ddia/pull/335) | [@kimi0230](https://github.com/kimi0230) | 修正一处繁体中文错误 |
|
||||
| [334](https://github.com/Vonng/ddia/pull/334) | [@soulrrrrr](https://github.com/soulrrrrr) | ch2: 修正一处繁体中文错误 |
|
||||
| [332](https://github.com/Vonng/ddia/pull/332) | [@justlorain](https://github.com/justlorain) | ch5: 修正一处翻译错误 |
|
||||
| [331](https://github.com/Vonng/ddia/pull/331) | [@Lyianu](https://github.com/Lyianu) | ch9: 更正几处拼写错误 |
|
||||
| [330](https://github.com/Vonng/ddia/pull/330) | [@Lyianu](https://github.com/Lyianu) | ch7: 优化一处翻译 |
|
||||
| [329](https://github.com/Vonng/ddia/issues/329) | [@Lyianu](https://github.com/Lyianu) | ch6: 指出一处翻译错误 |
|
||||
| [328](https://github.com/Vonng/ddia/pull/328) | [@justlorain](https://github.com/justlorain) | ch4: 更正一处翻译遗漏 |
|
||||
| [326](https://github.com/Vonng/ddia/pull/326) | [@liangGTY](https://github.com/liangGTY) | ch1: 优化一处翻译 |
|
||||
| [323](https://github.com/Vonng/ddia/pull/323) | [@marvin263](https://github.com/marvin263) | ch5: 优化一处翻译 |
|
||||
| [322](https://github.com/Vonng/ddia/pull/322) | [@marvin263](https://github.com/marvin263) | ch8: 优化一处翻译 |
|
||||
| [304](https://github.com/Vonng/ddia/pull/304) | [@spike014](https://github.com/spike014) | ch11: 优化一处翻译 |
|
||||
| [298](https://github.com/Vonng/ddia/pull/298) | [@Makonike](https://github.com/Makonike) | ch11&12: 修正两处错误 |
|
||||
| [284](https://github.com/Vonng/ddia/pull/284) | [@WAangzE](https://github.com/WAangzE) | ch4: 更正一处列表错误 |
|
||||
| [283](https://github.com/Vonng/ddia/pull/283) | [@WAangzE](https://github.com/WAangzE) | ch3: 更正一处错别字 |
|
||||
| [282](https://github.com/Vonng/ddia/pull/282) | [@WAangzE](https://github.com/WAangzE) | ch2: 更正一处公式问题 |
|
||||
| [281](https://github.com/Vonng/ddia/pull/281) | [@lyuxi99](https://github.com/lyuxi99) | 更正多处内部链接错误 |
|
||||
| [280](https://github.com/Vonng/ddia/pull/280) | [@lyuxi99](https://github.com/lyuxi99) | ch9: 更正内部链接错误 |
|
||||
| [279](https://github.com/Vonng/ddia/issues/279) | [@codexvn](https://github.com/codexvn) | ch9: 指出公式在 GitHub Pages 显示的问题 |
|
||||
| [278](https://github.com/Vonng/ddia/pull/278) | [@LJlkdskdjflsa](https://github.com/LJlkdskdjflsa) | 发现了繁体中文版本中的错误翻译 |
|
||||
| [275](https://github.com/Vonng/ddia/pull/275) | [@117503445](https://github.com/117503445) | 更正 LICENSE 链接 |
|
||||
| [274](https://github.com/Vonng/ddia/pull/274) | [@uncle-lv](https://github.com/uncle-lv) | ch7: 修正错别字 |
|
||||
| [273](https://github.com/Vonng/ddia/pull/273) | [@Sdot-Python](https://github.com/Sdot-Python) | ch7: 统一了 write skew 的翻译 |
|
||||
| [271](https://github.com/Vonng/ddia/pull/271) | [@Makonike](https://github.com/Makonike) | ch6: 统一了 rebalancing 的翻译 |
|
||||
| [270](https://github.com/Vonng/ddia/pull/270) | [@Ynjxsjmh](https://github.com/Ynjxsjmh) | ch7: 修正不一致的翻译 |
|
||||
| [263](https://github.com/Vonng/ddia/pull/263) | [@zydmayday](https://github.com/zydmayday) | ch5: 修正译文中的重复单词 |
|
||||
| [260](https://github.com/Vonng/ddia/pull/260) | [@haifeiWu](https://github.com/haifeiWu) | ch4: 修正部分不准确的翻译 |
|
||||
| [258](https://github.com/Vonng/ddia/pull/258) | [@bestgrc](https://github.com/bestgrc) | ch3: 修正一处翻译错误 |
|
||||
| [257](https://github.com/Vonng/ddia/pull/257) | [@UnderSam](https://github.com/UnderSam) | ch8: 修正一处拼写错误 |
|
||||
| [256](https://github.com/Vonng/ddia/pull/256) | [@AlphaWang](https://github.com/AlphaWang) | ch7: 修正“可串行化”相关内容的多处翻译不当 |
|
||||
| [255](https://github.com/Vonng/ddia/pull/255) | [@AlphaWang](https://github.com/AlphaWang) | ch7: 修正“可重复读”相关内容的多处翻译不当 |
|
||||
| [253](https://github.com/Vonng/ddia/pull/253) | [@AlphaWang](https://github.com/AlphaWang) | ch7: 修正“读已提交”相关内容的多处翻译不当 |
|
||||
| [246](https://github.com/Vonng/ddia/pull/246) | [@derekwu0101](https://github.com/derekwu0101) | ch3: 修正繁体中文的转译错误 |
|
||||
| [245](https://github.com/Vonng/ddia/pull/245) | [@skyran1278](https://github.com/skyran1278) | ch12: 修正繁体中文的转译错误 |
|
||||
| [244](https://github.com/Vonng/ddia/pull/244) | [@Axlgrep](https://github.com/Axlgrep) | ch9: 修正不通顺的翻译 |
|
||||
| [242](https://github.com/Vonng/ddia/pull/242) | [@lynkeib](https://github.com/lynkeib) | ch9: 修正不通顺的翻译 |
|
||||
| [241](https://github.com/Vonng/ddia/pull/241) | [@lynkeib](https://github.com/lynkeib) | ch8: 修正不正确的公式格式 |
|
||||
| [240](https://github.com/Vonng/ddia/pull/240) | [@8da2k](https://github.com/8da2k) | ch9: 修正不通顺的翻译 |
|
||||
| [239](https://github.com/Vonng/ddia/pull/239) | [@BeBraveBeCurious](https://github.com/BeBraveBeCurious) | ch7: 修正不一致的翻译 |
|
||||
| [237](https://github.com/Vonng/ddia/pull/237) | [@zhangnew](https://github.com/zhangnew) | ch3: 修正错误的图片链接 |
|
||||
| [229](https://github.com/Vonng/ddia/pull/229) | [@lis186](https://github.com/lis186) | 指出繁体中文的转译错误:复杂 |
|
||||
| [226](https://github.com/Vonng/ddia/pull/226) | [@chroming](https://github.com/chroming) | ch1: 修正导航栏中的章节名称 |
|
||||
| [220](https://github.com/Vonng/ddia/pull/220) | [@skyran1278](https://github.com/skyran1278) | ch9: 修正线性一致的繁体中文翻译 |
|
||||
| [194](https://github.com/Vonng/ddia/pull/194) | [@BeBraveBeCurious](https://github.com/BeBraveBeCurious) | ch4: 修正错误的翻译 |
|
||||
| [193](https://github.com/Vonng/ddia/pull/193) | [@BeBraveBeCurious](https://github.com/BeBraveBeCurious) | ch4: 优化译文 |
|
||||
| [192](https://github.com/Vonng/ddia/pull/192) | [@BeBraveBeCurious](https://github.com/BeBraveBeCurious) | ch4: 修正不一致和不通顺的翻译 |
|
||||
| [190](https://github.com/Vonng/ddia/pull/190) | [@Pcrab](https://github.com/Pcrab) | ch1: 修正不准确的翻译 |
|
||||
| [187](https://github.com/Vonng/ddia/pull/187) | [@narojay](https://github.com/narojay) | ch9: 修正生硬的翻译 |
|
||||
| [186](https://github.com/Vonng/ddia/pull/186) | [@narojay](https://github.com/narojay) | ch8: 修正错别字 |
|
||||
| [185](https://github.com/Vonng/ddia/issues/185) | [@8da2k](https://github.com/8da2k) | 指出小标题跳转的问题 |
|
||||
| [184](https://github.com/Vonng/ddia/pull/184) | [@DavidZhiXing](https://github.com/DavidZhiXing) | ch10: 修正失效的网址 |
|
||||
| [183](https://github.com/Vonng/ddia/pull/183) | [@OneSizeFitsQuorum](https://github.com/OneSizeFitsQuorum) | ch8: 修正错别字 |
|
||||
| [182](https://github.com/Vonng/ddia/issues/182) | [@lroolle](https://github.com/lroolle) | 建议docsify的主题风格 |
|
||||
| [181](https://github.com/Vonng/ddia/pull/181) | [@YunfengGao](https://github.com/YunfengGao) | ch2: 修正翻译错误 |
|
||||
| [180](https://github.com/Vonng/ddia/pull/180) | [@skyran1278](https://github.com/skyran1278) | ch3: 指出繁体中文的转译错误 |
|
||||
| [177](https://github.com/Vonng/ddia/pull/177) | [@exzhawk](https://github.com/exzhawk) | 支持 Github Pages 里的公式显示 |
|
||||
| [176](https://github.com/Vonng/ddia/pull/176) | [@haifeiWu](https://github.com/haifeiWu) | ch2: 语义网相关翻译更正 |
|
||||
| [175](https://github.com/Vonng/ddia/pull/175) | [@cwr31](https://github.com/cwr31) | ch7: 不变式相关翻译更正 |
|
||||
| [174](https://github.com/Vonng/ddia/pull/174) | [@BeBraveBeCurious](https://github.com/BeBraveBeCurious) | README & preface: 更正不正确的中文用词和标点符号 |
|
||||
| [173](https://github.com/Vonng/ddia/pull/173) | [@ZvanYang](https://github.com/ZvanYang) | ch12: 修正不完整的翻译 |
|
||||
| [171](https://github.com/Vonng/ddia/pull/171) | [@ZvanYang](https://github.com/ZvanYang) | ch12: 修正重复的译文 |
|
||||
| [169](https://github.com/Vonng/ddia/pull/169) | [@ZvanYang](https://github.com/ZvanYang) | ch12: 更正不太通顺的翻译 |
|
||||
| [166](https://github.com/Vonng/ddia/pull/166) | [@bp4m4h94](https://github.com/bp4m4h94) | ch1: 发现错误的文献索引 |
|
||||
| [164](https://github.com/Vonng/ddia/pull/164) | [@DragonDriver](https://github.com/DragonDriver) | preface: 更正错误的标点符号 |
|
||||
| [163](https://github.com/Vonng/ddia/pull/163) | [@llmmddCoder](https://github.com/llmmddCoder) | ch1: 更正错误字 |
|
||||
| [160](https://github.com/Vonng/ddia/pull/160) | [@Zhayhp](https://github.com/Zhayhp) | ch2: 建议将 network model 翻译为网状模型 |
|
||||
| [159](https://github.com/Vonng/ddia/pull/159) | [@1ess](https://github.com/1ess) | ch4: 更正错误字 |
|
||||
| [157](https://github.com/Vonng/ddia/pull/157) | [@ZvanYang](https://github.com/ZvanYang) | ch7: 更正不太通顺的翻译 |
|
||||
| [155](https://github.com/Vonng/ddia/pull/155) | [@ZvanYang](https://github.com/ZvanYang) | ch7: 更正不太通顺的翻译 |
|
||||
| [153](https://github.com/Vonng/ddia/pull/153) | [@DavidZhiXing](https://github.com/DavidZhiXing) | ch9: 修正缩略图的错别字 |
|
||||
| [152](https://github.com/Vonng/ddia/pull/152) | [@ZvanYang](https://github.com/ZvanYang) | ch7: 除重->去重 |
|
||||
| [151](https://github.com/Vonng/ddia/pull/151) | [@ZvanYang](https://github.com/ZvanYang) | ch5: 修订sibling相关的翻译 |
|
||||
| [147](https://github.com/Vonng/ddia/pull/147) | [@ZvanYang](https://github.com/ZvanYang) | ch5: 更正一处不准确的翻译 |
|
||||
| [145](https://github.com/Vonng/ddia/pull/145) | [@Hookey](https://github.com/Hookey) | 识别了当前简繁转译过程中处理不当的地方,暂通过转换脚本规避 |
|
||||
| [144](https://github.com/Vonng/ddia/issues/144) | [@secret4233](https://github.com/secret4233) | ch7: 不翻译`next-key locking` |
|
||||
| [143](https://github.com/Vonng/ddia/issues/143) | [@imcheney](https://github.com/imcheney) | ch3: 更新残留的机翻段落 |
|
||||
| [142](https://github.com/Vonng/ddia/issues/142) | [@XIJINIAN](https://github.com/XIJINIAN) | 建议去除段首的制表符 |
|
||||
| [141](https://github.com/Vonng/ddia/issues/141) | [@Flyraty](https://github.com/Flyraty) | ch5: 发现一处错误格式的章节引用 |
|
||||
| [140](https://github.com/Vonng/ddia/pull/140) | [@Bowser1704](https://github.com/Bowser1704) | ch5: 修正章节Summary中多处不通顺的翻译 |
|
||||
| [139](https://github.com/Vonng/ddia/pull/139) | [@Bowser1704](https://github.com/Bowser1704) | ch2&ch3: 修正多处不通顺的或错误的翻译 |
|
||||
| [137](https://github.com/Vonng/ddia/pull/137) | [@fuxuemingzhu](https://github.com/fuxuemingzhu) | ch5&ch6: 优化多处不通顺的或错误的翻译 |
|
||||
| [134](https://github.com/Vonng/ddia/pull/134) | [@fuxuemingzhu](https://github.com/fuxuemingzhu) | ch4: 优化多处不通顺的或错误的翻译 |
|
||||
| [133](https://github.com/Vonng/ddia/pull/133) | [@fuxuemingzhu](https://github.com/fuxuemingzhu) | ch3: 优化多处错误的或不通顺的翻译 |
|
||||
| [132](https://github.com/Vonng/ddia/pull/132) | [@fuxuemingzhu](https://github.com/fuxuemingzhu) | ch3: 优化一处容易产生歧义的翻译 |
|
||||
| [131](https://github.com/Vonng/ddia/pull/131) | [@rwwg4](https://github.com/rwwg4) | ch6: 修正两处错误的翻译 |
|
||||
| [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 的翻译(如何以最佳方式) |
|
||||
| [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 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 |
|
||||
| [113](https://github.com/Vonng/ddia/pull/113) | [@lpxxn](https://github.com/lpxxn) | 修改语句 |
|
||||
| [112](https://github.com/Vonng/ddia/pull/112) | [@ibyte2011](https://github.com/ibyte2011) | Update ch9.md |
|
||||
| [110](https://github.com/Vonng/ddia/pull/110) | [@lpxxn](https://github.com/lpxxn) | 读已写入数据 |
|
||||
| [107](https://github.com/Vonng/ddia/pull/107) | [@abbychau](https://github.com/abbychau) | 單調鐘和好死还是赖活着 |
|
||||
| [106](https://github.com/Vonng/ddia/pull/106) | [@enochii](https://github.com/enochii) | typo in ch2: fix braces typo |
|
||||
| [105](https://github.com/Vonng/ddia/pull/105) | [@LiminCode](https://github.com/LiminCode) | Chronicle translation error |
|
||||
| [104](https://github.com/Vonng/ddia/pull/104) | [@Sunt-ing](https://github.com/Sunt-ing) | several advice for better translation |
|
||||
| [103](https://github.com/Vonng/ddia/pull/103) | [@Sunt-ing](https://github.com/Sunt-ing) | typo in ch4: should be 完成 rather than 完全 |
|
||||
| [102](https://github.com/Vonng/ddia/pull/102) | [@Sunt-ing](https://github.com/Sunt-ing) | ch4: better-translation: 扼杀 → 破坏 |
|
||||
| [101](https://github.com/Vonng/ddia/pull/101) | [@Sunt-ing](https://github.com/Sunt-ing) | typo in Ch4: should be "改变" rathr than "盖面" |
|
||||
| [100](https://github.com/Vonng/ddia/pull/100) | [@LiminCode](https://github.com/LiminCode) | fix missing translation |
|
||||
| [99 ](https://github.com/Vonng/ddia/pull/99) | [@mrdrivingduck](https://github.com/mrdrivingduck) | ch6: fix the word rebalancing |
|
||||
| [98 ](https://github.com/Vonng/ddia/pull/98) | [@jacklightChen](https://github.com/jacklightChen) | fix ch7.md: fix wrong references |
|
||||
| [97 ](https://github.com/Vonng/ddia/pull/97) | [@jenac](https://github.com/jenac) | 96 |
|
||||
| [96 ](https://github.com/Vonng/ddia/pull/96) | [@PragmaTwice](https://github.com/PragmaTwice) | ch2: fix typo about 'may or may not be' |
|
||||
| [95 ](https://github.com/Vonng/ddia/pull/95) | [@EvanMu96](https://github.com/EvanMu96) | fix translation of "the battle cry" in ch5 |
|
||||
| [94 ](https://github.com/Vonng/ddia/pull/94) | [@kemingy](https://github.com/kemingy) | ch6: fix markdown and punctuations |
|
||||
| [93 ](https://github.com/Vonng/ddia/pull/93) | [@kemingy](https://github.com/kemingy) | ch5: fix markdown and some typos |
|
||||
| [92 ](https://github.com/Vonng/ddia/pull/92) | [@Gilbert1024](https://github.com/Gilbert1024) | Merge pull request #1 from Vonng/master |
|
||||
| [88 ](https://github.com/Vonng/ddia/pull/88) | [@kemingy](https://github.com/kemingy) | fix typo for ch1, ch2, ch3, ch4 |
|
||||
| [87 ](https://github.com/Vonng/ddia/pull/87) | [@wynn5a](https://github.com/wynn5a) | Update ch3.md |
|
||||
| [86 ](https://github.com/Vonng/ddia/pull/86) | [@northmorn](https://github.com/northmorn) | Update ch1.md |
|
||||
| [85 ](https://github.com/Vonng/ddia/pull/85) | [@sunbuhui](https://github.com/sunbuhui) | fix ch2.md: fix ch2 ambiguous translation |
|
||||
| [84 ](https://github.com/Vonng/ddia/pull/84) | [@ganler](https://github.com/ganler) | Fix translation: use up |
|
||||
| [83 ](https://github.com/Vonng/ddia/pull/83) | [@afunTW](https://github.com/afunTW) | Using OpenCC to convert from zh-cn to zh-tw |
|
||||
| [82 ](https://github.com/Vonng/ddia/pull/82) | [@kangni](https://github.com/kangni) | fix gitbook url |
|
||||
| [78 ](https://github.com/Vonng/ddia/pull/78) | [@hanyu2](https://github.com/hanyu2) | Fix unappropriated translation |
|
||||
| [77 ](https://github.com/Vonng/ddia/pull/77) | [@Ozarklake](https://github.com/Ozarklake) | fix typo |
|
||||
| [75 ](https://github.com/Vonng/ddia/pull/75) | [@2997ms](https://github.com/2997ms) | Fix typo |
|
||||
| [74 ](https://github.com/Vonng/ddia/pull/74) | [@2997ms](https://github.com/2997ms) | Update ch9.md |
|
||||
| [70 ](https://github.com/Vonng/ddia/pull/70) | [@2997ms](https://github.com/2997ms) | Update ch7.md |
|
||||
| [67 ](https://github.com/Vonng/ddia/pull/67) | [@jiajiadebug](https://github.com/jiajiadebug) | fix issues in ch2 - ch9 and glossary |
|
||||
| [66 ](https://github.com/Vonng/ddia/pull/66) | [@blindpirate](https://github.com/blindpirate) | Fix typo |
|
||||
| [63 ](https://github.com/Vonng/ddia/pull/63) | [@haifeiWu](https://github.com/haifeiWu) | Update ch10.md |
|
||||
| [62 ](https://github.com/Vonng/ddia/pull/62) | [@ych](https://github.com/ych) | fix ch1.md typesetting problem |
|
||||
| [61 ](https://github.com/Vonng/ddia/pull/61) | [@xianlaioy](https://github.com/xianlaioy) | docs:钟-->种,去掉ou |
|
||||
| [60 ](https://github.com/Vonng/ddia/pull/60) | [@Zombo1296](https://github.com/Zombo1296) | 否则 -> 或者 |
|
||||
| [59 ](https://github.com/Vonng/ddia/pull/59) | [@AlexanderMisel](https://github.com/AlexanderMisel) | 呼叫->调用,显着->显著 |
|
||||
| [58 ](https://github.com/Vonng/ddia/pull/58) | [@ibyte2011](https://github.com/ibyte2011) | Update ch8.md |
|
||||
| [55 ](https://github.com/Vonng/ddia/pull/55) | [@saintube](https://github.com/saintube) | ch8: 修改链接错误 |
|
||||
| [54 ](https://github.com/Vonng/ddia/pull/54) | [@Panmax](https://github.com/Panmax) | Update ch2.md |
|
||||
| [53 ](https://github.com/Vonng/ddia/pull/53) | [@ibyte2011](https://github.com/ibyte2011) | Update ch9.md |
|
||||
| [52 ](https://github.com/Vonng/ddia/pull/52) | [@hecenjie](https://github.com/hecenjie) | Update ch1.md |
|
||||
| [51 ](https://github.com/Vonng/ddia/pull/51) | [@latavin243](https://github.com/latavin243) | fix 修正ch3 ch4几处翻译 |
|
||||
| [50 ](https://github.com/Vonng/ddia/pull/50) | [@AlexZFX](https://github.com/AlexZFX) | 几个疏漏和格式错误 |
|
||||
| [49 ](https://github.com/Vonng/ddia/pull/49) | [@haifeiWu](https://github.com/haifeiWu) | Update ch1.md |
|
||||
| [48 ](https://github.com/Vonng/ddia/pull/48) | [@scaugrated](https://github.com/scaugrated) | fix typo |
|
||||
| [47 ](https://github.com/Vonng/ddia/pull/47) | [@lzwill](https://github.com/lzwill) | Fixed typos in ch2 |
|
||||
| [45 ](https://github.com/Vonng/ddia/pull/45) | [@zenuo](https://github.com/zenuo) | 删除一个多余的右括号 |
|
||||
| [44 ](https://github.com/Vonng/ddia/pull/44) | [@akxxsb](https://github.com/akxxsb) | 修正第七章底部链接错误 |
|
||||
| [43 ](https://github.com/Vonng/ddia/pull/43) | [@baijinping](https://github.com/baijinping) | "更假简单"->"更加简单" |
|
||||
| [42 ](https://github.com/Vonng/ddia/pull/42) | [@tisonkun](https://github.com/tisonkun) | 修复 ch1 中的无序列表格式 |
|
||||
| [38 ](https://github.com/Vonng/ddia/pull/38) | [@renjie-c](https://github.com/renjie-c) | 纠正多处的翻译小错误 |
|
||||
| [37 ](https://github.com/Vonng/ddia/pull/37) | [@tankilo](https://github.com/tankilo) | fix translation mistakes in ch4.md |
|
||||
| [36 ](https://github.com/Vonng/ddia/pull/36) | [@wwek](https://github.com/wwek) | 1.修复多个链接错误 2.名词优化修订 3.错误修订 |
|
||||
| [35 ](https://github.com/Vonng/ddia/pull/35) | [@wwek](https://github.com/wwek) | fix ch7.md to ch8.md link error |
|
||||
| [34 ](https://github.com/Vonng/ddia/pull/34) | [@wwek](https://github.com/wwek) | Merge pull request #1 from Vonng/master |
|
||||
| [33 ](https://github.com/Vonng/ddia/pull/33) | [@wwek](https://github.com/wwek) | fix part-ii.md link error |
|
||||
| [32 ](https://github.com/Vonng/ddia/pull/32) | [@JCYoky](https://github.com/JCYoky) | Update ch2.md |
|
||||
| [31 ](https://github.com/Vonng/ddia/pull/31) | [@elsonLee](https://github.com/elsonLee) | Update ch7.md |
|
||||
| [26 ](https://github.com/Vonng/ddia/pull/26) | [@yjhmelody](https://github.com/yjhmelody) | 修复一些明显错误 |
|
||||
| [25 ](https://github.com/Vonng/ddia/pull/25) | [@lqbilbo](https://github.com/lqbilbo) | 修复链接错误 |
|
||||
| [24 ](https://github.com/Vonng/ddia/pull/24) | [@artiship](https://github.com/artiship) | 修改词语顺序 |
|
||||
| [23 ](https://github.com/Vonng/ddia/pull/23) | [@artiship](https://github.com/artiship) | 修正错别字 |
|
||||
| [22 ](https://github.com/Vonng/ddia/pull/22) | [@artiship](https://github.com/artiship) | 纠正翻译错误 |
|
||||
| [21 ](https://github.com/Vonng/ddia/pull/21) | [@zhtisi](https://github.com/zhtisi) | 修正目录和本章标题不符的情况 |
|
||||
| [20 ](https://github.com/Vonng/ddia/pull/20) | [@rentiansheng](https://github.com/rentiansheng) | Update ch7.md |
|
||||
| [19 ](https://github.com/Vonng/ddia/pull/19) | [@LHRchina](https://github.com/LHRchina) | 修复语句小bug |
|
||||
| [16 ](https://github.com/Vonng/ddia/pull/16) | [@MuAlex](https://github.com/MuAlex) | Master |
|
||||
| [15 ](https://github.com/Vonng/ddia/pull/15) | [@cg-zhou](https://github.com/cg-zhou) | Update translation progress |
|
||||
| [14 ](https://github.com/Vonng/ddia/pull/14) | [@cg-zhou](https://github.com/cg-zhou) | Translate glossary |
|
||||
| [13 ](https://github.com/Vonng/ddia/pull/13) | [@cg-zhou](https://github.com/cg-zhou) | 详细修改了后记中和印度野猪相关的描述 |
|
||||
| [12 ](https://github.com/Vonng/ddia/pull/12) | [@ibyte2011](https://github.com/ibyte2011) | 修改了部分翻译 |
|
||||
| [11 ](https://github.com/Vonng/ddia/pull/11) | [@jiajiadebug](https://github.com/jiajiadebug) | ch2 100% |
|
||||
| [10 ](https://github.com/Vonng/ddia/pull/10) | [@jiajiadebug](https://github.com/jiajiadebug) | ch2 20% |
|
||||
| [9 ](https://github.com/Vonng/ddia/pull/9) | [@jiajiadebug](https://github.com/jiajiadebug) | Preface, ch1, part-i translation minor fixes |
|
||||
| [7 ](https://github.com/Vonng/ddia/pull/7) | [@MuAlex](https://github.com/MuAlex) | Ch6 translation pull request |
|
||||
| [6 ](https://github.com/Vonng/ddia/pull/6) | [@MuAlex](https://github.com/MuAlex) | Ch6 change version1 |
|
||||
| [5 ](https://github.com/Vonng/ddia/pull/5) | [@nevertiree](https://github.com/nevertiree) | Chapter 01语法微调 |
|
||||
| [2 ](https://github.com/Vonng/ddia/pull/2) | [@seagullbird](https://github.com/seagullbird) | 序言初翻 |
|
||||
|
||||
</details><br />
|
||||
|
||||
|
||||
---------
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用 [CC-BY 4.0](https://github.com/Vonng/ddia/blob/master/LICENSE) 许可证,您可以在这里找到完整说明:
|
||||
|
||||
- [署名 4.0 协议国际版 CC BY 4.0 Deed](https://creativecommons.org/licenses/by/4.0/deed.zh-hans)
|
||||
- [Attribution 4.0 International CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.en)
|
||||
|
|
@ -1,875 +0,0 @@
|
|||
---
|
||||
title: "第一章:数据系统架构中的利弊权衡"
|
||||
linkTitle: "1. 数据系统架构中的利弊权衡"
|
||||
weight: 101
|
||||
breadcrumbs: false
|
||||
---
|
||||
|
||||

|
||||
|
||||
> *没有解决方案,只有利弊权衡。[…] 尽你所能获取最好的利弊权衡,这是你唯一能指望的事。*
|
||||
>
|
||||
> [Thomas Sowell](https://www.youtube.com/watch?v=2YUtKr8-_Fg), 与 Fred Barnes 的采访 (2005)
|
||||
|
||||
数据在今天的许多应用程序开发中居于核心地位。随着网络和移动应用、软件即服务(SaaS)以及云服务的普及,将来自不同用户的数据存储在共享的基于服务器的数据基础设施中已成为常态。需要存储和供分析使用的数据包括用户活动、商业交易、设备和传感器的数据。当用户与应用程序交互时,他们既读取存储的数据,也生成更多数据。
|
||||
|
||||
小量数据,可在单一机器上存储和处理,通常相对容易处理。然而,随着数据量或查询率的增加,需要将数据分布到多台机器上,这引入了许多挑战。随着应用程序需求的复杂化,仅在一个系统中存储所有数据已不再足够,可能需要结合多个提供不同功能的存储或处理系统。
|
||||
|
||||
如果数据管理是开发应用程序的主要挑战之一,我们称这类应用为*数据密集型* [[1](ch01.html#Kouzes2009)]。而在*计算密集型*系统中,挑战在于并行处理一些非常大的计算,在数据密集型应用中,我们通常更关心的是如何存储和处理大数据量、管理数据变化、在出现故障和并发时确保一致性以及确保服务的高可用性。
|
||||
|
||||
这类应用通常由提供常用功能的标准构建块构成。例如,许多应用需要:
|
||||
|
||||
- 存储数据,以便它们或其他应用程序稍后可以再次找到它(*数据库*)
|
||||
- 记住一次昂贵操作的结果,以加速读取(*缓存*)
|
||||
- 允许用户按关键词搜索数据或以各种方式过滤数据(*搜索索引*)
|
||||
- 当事件和数据变化发生时立即处理(*流处理*)
|
||||
- 定期处理大量积累的数据(*批处理*)
|
||||
|
||||
在构建应用程序时,我们通常会采用几个软件系统或服务,如数据库或 API,并用一些应用代码将它们粘合在一起。如果你完全按照数据系统的设计目的去做,那么这个过程可能会非常容易。
|
||||
|
||||
然而,随着你的应用变得更加雄心勃勃,挑战也随之而来。有许多不同特性的数据库系统,适用于不同的目的——你该如何选择使用哪一个?有各种各样的缓存方法,几种构建搜索索引的方式等等——你该如何权衡它们的利弊?你需要弄清楚哪些工具和哪些方法最适合手头的任务,而且将多款工具组合起来以完成单独一款工具无法完成的事情也可能是很困难的。
|
||||
|
||||
本书是一本指南,旨在帮助你做出关于使用哪些技术以及如何组合它们的决策。正如你将看到的,没有任何一种方法从根本上比其他所有方法都好;每种方法都有其优缺点。通过这本书,你将学会提出正确的问题,以评估和比较数据系统,从而找出最适合你的特定应用需求的方法。
|
||||
|
||||
我们将从探索数据在当今组织中的典型使用方式开始我们的旅程。这里的许多想法起源于*企业软件*(即大型组织如大公司和政府的软件需求和工程实践),因为历史上只有大型组织拥有需要复杂技术解决方案的大数据量。如果你的数据量足够小,你甚至可以简单地将其保存在电子表格中!然而,最近,较小的公司和初创企业管理大数据量并构建数据密集型系统也变得普遍。
|
||||
|
||||
关于数据系统的一个关键挑战是,不同的人需要用数据做非常不同的事情。如果你在一家公司工作,你和你的团队会有一套优先事项,而另一个团队可能完全有不同的目标,尽管你们可能都在处理同一数据集!此外,这些目标可能不会明确表达,这可能会导致误解和对正确方法的争议。
|
||||
|
||||
为了帮助你了解你可以做出哪些选择,本章将比较几个对比概念,并探讨它们的利弊:
|
||||
|
||||
- 事务处理与分析之间的区别([“事务处理与分析”](#事务处理与分析))
|
||||
- 云服务与自托管系统的优缺点([“云服务与自托管”](#云服务与自托管))
|
||||
- 何时从单节点系统迁移到分布式系统([“分布式与单节点系统”](#分布式与单节点系统))
|
||||
- 平衡业务需求与用户权利([“数据系统、法律与社会”](#数据系统法律与社会))
|
||||
|
||||
此外,本章将为我们接下来的书中的内容提供必需的术语。
|
||||
|
||||
Data is central to much application development today. With web and mobile apps, software as a service (SaaS), and cloud services, it has become normal to store data from many different users in a shared server-based data infrastructure. Data from user activity, business transactions, devices and sensors needs to be stored and made available for analysis. As users interact with an application, they both read the data that is stored, and also generate more data.
|
||||
|
||||
Small amounts of data, which can be stored and processed on a single machine, are often fairly easy to deal with. However, as the data volume or the rate of queries grows, it needs to be distributed across multiple machines, which introduces many challenges. As the needs of the application become more complex, it is no longer sufficient to store everything in one system, but it might be necessary to combine multiple storage or processing systems that provide different capabilities.
|
||||
|
||||
We call an application *data-intensive* if data management is one of the primary challenges in developing the application [[1](ch01.html#Kouzes2009)]. While in *compute-intensive* systems the challenge is parallelizing some very large computation, in data-intensive applications we usually worry more about things like storing and processing large data volumes, managing changes to data, ensuring consistency in the face of failures and concurrency, and making sure services are highly available.
|
||||
|
||||
Such applications are typically built from standard building blocks that provide commonly needed functionality. For example, many applications need to:
|
||||
|
||||
- Store data so that they, or another application, can find it again later (*databases*)
|
||||
- Remember the result of an expensive operation, to speed up reads (*caches*)
|
||||
- Allow users to search data by keyword or filter it in various ways (*search indexes*)
|
||||
- Handle events and data changes as soon as they occur (*stream processing*)
|
||||
- Periodically crunch a large amount of accumulated data (*batch processing*)
|
||||
|
||||
In building an application we typically take several software systems or services, such as databases or APIs, and glue them together with some application code. If you are doing exactly what the data systems were designed for, then this process can be quite easy.
|
||||
|
||||
However, as your application becomes more ambitious, challenges arise. There are many database systems with different characteristics, suitable for different purposes—how do you choose which one to use? There are various approaches to caching, several ways of building search indexes, and so on—how do you reason about their trade-offs? You need to figure out which tools and which approaches are the most appropriate for the task at hand, and it can be difficult to combine tools when you need to do something that a single tool cannot do alone.
|
||||
|
||||
This book is a guide to help you make decisions about which technologies to use and how to combine them. As you will see, there is no one approach that is fundamentally better than others; everything has pros and cons. With this book, you will learn to ask the right questions to evaluate and compare data systems, so that you can figure out which approach will best serve the needs of your particular application.
|
||||
|
||||
We will start our journey by looking at some of the ways that data is typically used in organizations today. Many of the ideas here have their origin in *enterprise software* (i.e., the software needs and engineering practices of large organizations, such as big corporations and governments), since historically, only large organizations had the large data volumes that required sophisticated technical solutions. If your data volume is small enough, you can simply keep it in a spreadsheet! However, more recently it has also become common for smaller companies and startups to manage large data volumes and build data-intensive systems.
|
||||
|
||||
One of the key challenges with data systems is that different people need to do very different things with data. If you are working at a company, you and your team will have one set of priorities, while another team may have entirely different goals, although you might even be working with the same dataset! Moreover, those goals might not be explicitly articulated, which can lead to misunderstandings and disagreement about the right approach.
|
||||
|
||||
To help you understand what choices you can make, this chapter compares several contrasting concepts, and explores their trade-offs:
|
||||
|
||||
- the difference between transaction processing and analytics ([“Transaction Processing versus Analytics”](ch01.html#sec_introduction_analytics));
|
||||
- pros and cons of cloud services and self-hosted systems ([“Cloud versus Self-Hosting”](ch01.html#sec_introduction_cloud));
|
||||
- when to move from single-node systems to distributed systems ([“Distributed versus Single-Node Systems”](ch01.html#sec_introduction_distributed)); and
|
||||
- balancing the needs of the business and the rights of the user ([“Data Systems, Law, and Society”](ch01.html#sec_introduction_compliance)).
|
||||
|
||||
Moreover, this chapter will provide you with terminology that we will need for the rest of the book.
|
||||
|
||||
--------
|
||||
|
||||
### 术语:前端与后端
|
||||
|
||||
我们在本书中将讨论的许多内容涉及*后端开发*。解释该术语:对于网络应用程序,客户端代码(在网页浏览器中运行)被称为*前端*,处理用户请求的服务器端代码被称为*后端*。移动应用与前端类似,它们提供用户界面,通常通过互联网与服务器端后端通信。前端有时会在用户设备上本地管理数据[[2](ch01.html#Kleppmann2019)],但最大的数据基础设施挑战通常存在于后端:前端只需要处理一个用户的数据,而后端则代表*所有*用户管理数据。
|
||||
|
||||
后端服务通常可以通过 HTTP 访问;它通常包含一些应用程序代码,这些代码在一个或多个数据库中读写数据,有时还会与额外的数据系统(如缓存或消息队列)交互(我们可能统称为*数据基础设施*)。应用程序代码通常是*无状态的*(即,当它完成处理一个 HTTP 请求后,它会忘记该请求的所有信息),并且任何需要从一个请求传递到另一个请求的信息都需要存储在客户端或服务器端的数据基础设施中。
|
||||
|
||||
Much of what we will discuss in this book relates to *backend development*. To explain that term: for web applications, the client-side code (which runs in a web browser) is called the *frontend*, and the server-side code that handles user requests is known as the *backend*. Mobile apps are similar to frontends in that they provide user interfaces, which often communicate over the Internet with a server-side backend. Frontends sometimes manage data locally on the user’s device [[2](ch01.html#Kleppmann2019)], but the greatest data infrastructure challenges often lie in the backend: a frontend only needs to handle one user’s data, whereas the backend manages data on behalf of *all* of the users.
|
||||
|
||||
A backend service is often reachable via HTTP; it usually consists of some application code that reads and writes data in one or more databases, and sometimes interfaces with additional data systems such as caches or message queues (which we might collectively call *data infrastructure*). The application code is often *stateless* (i.e., when it finishes handling one HTTP request, it forgets everything about that request), and any information that needs to persist from one request to another needs to be stored either on the client, or in the server-side data infrastructure.
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 事务处理与分析
|
||||
|
||||
如果你在企业中从事数据系统工作,你可能会遇到几种不同类型的处理数据的人。第一种是*后端工程师*,他们构建处理读取和更新数据请求的服务;这些服务通常直接或间接通过其他服务为外部用户提供服务(见[“微服务和无服务器”](ch01.html#sec_introduction_microservices))。有时服务是供组织内部其他部分使用的。
|
||||
|
||||
除了管理后端服务的团队外,还有两个群体通常需要访问组织的数据:*商业分析师*,他们生成有关组织活动的报告以帮助管理层做出更好的决策(*商业智能*或*BI*),以及*数据科学家*,他们在数据中寻找新的见解或创建由数据分析和机器学习/AI支持的面向用户的产品功能(例如,电子商务网站上的“购买 X 的人也购买了 Y”推荐、风险评分或垃圾邮件过滤等预测分析,以及搜索结果的排名)。
|
||||
|
||||
尽管商业分析师和数据科学家倾向于使用不同的工具并以不同的方式操作,但他们有一些共同点:两者都进行*分析*,这意味着他们查看用户和后端服务生成的数据,但他们通常不修改这些数据(除了可能修正错误)。他们可能创建派生数据集,其中原始数据已以某种方式处理。这导致了两种系统之间的分离——这是我们将在整本书中使用的区分:
|
||||
|
||||
- *业务系统*包括后端服务和数据基础设施,数据是在那里创建的,例如通过服务外部用户。在这里,应用程序代码根据用户的操作读取并修改其数据库中的数据。
|
||||
- *分析系统*满足商业分析师和数据科学家的需求。它们包含来自业务系统的数据的只读副本,并针对分析所需的数据处理类型进行了优化。
|
||||
|
||||
正如我们将在下一节中看到的,出于充分的理由,业务和分析系统通常保持独立。随着这些系统的成熟,出现了两个新的专业角色:*数据工程师*和*分析工程师*。数据工程师是了解如何整合业务和分析系统的人,他们负责组织的数据基础设施的更广泛管理[[3](ch01.html#Reis2022)]。分析工程师建模和转换数据,使其对查询组织中的数据的最终用户更有用[[4](ch01.html#Machado2023)]。
|
||||
|
||||
许多工程师专注于业务或分析的一侧。然而,这本书涵盖了业务和分析数据系统,因为两者在组织内的数据生命周期中都扮演着重要的角色。我们将深入探讨用于向内部和外部用户提供服务的数据基础设施,以便你能更好地与这一界限另一侧的同事合作。
|
||||
|
||||
If you are working on data systems in an enterprise, you are likely to encounter several different types of people who work with data. The first type are *backend engineers* who build services that handle requests for reading and updating data; these services often serve external users, either directly or indirectly via other services (see [“Microservices and Serverless”](ch01.html#sec_introduction_microservices)). Sometimes services are for internal use by other parts of the organization.
|
||||
|
||||
In addition to the teams managing backend services, two other groups of people typically require access to an organization’s data: *business analysts*, who generate reports about the activities of the organization in order to help the management make better decisions (*business intelligence* or *BI*), and *data scientists*, who look for novel insights in data or who create user-facing product features that are enabled by data analysis and machine learning/AI (for example, “people who bought X also bought Y” recommendations on an e-commerce website, predictive analytics such as risk scoring or spam filtering, and ranking of search results).
|
||||
|
||||
Although business analysts and data scientists tend to use different tools and operate in different ways, they have some things in common: both perform *analytics*, which means they look at the data that the users and backend services have generated, but they generally do not modify this data (except perhaps for fixing mistakes). They might create derived datasets in which the original data has been processed in some way. This has led to a split between two types of systems—a distinction that we will use throughout this book:
|
||||
|
||||
- *Operational systems* consist of the backend services and data infrastructure where data is created, for example by serving external users. Here, the application code both reads and modifies the data in its databases, based on the actions performed by the users.
|
||||
- *Analytical systems* serve the needs of business analysts and data scientists. They contain a read-only copy of the data from the operational systems, and they are optimized for the types of data processing that are needed for analytics.
|
||||
|
||||
As we shall see in the next section, operational and analytical systems are often kept separate, for good reasons. As these systems have matured, two new specialized roles have emerged: *data engineers* and *analytics engineers*. Data engineers are the people who know how to integrate the operational and the analytical systems, and who take responsibility for the organization’s data infrastructure more widely [[3](ch01.html#Reis2022)]. Analytics engineers model and transform data to make it more useful for end users querying data in an organization [[4](ch01.html#Machado2023)].
|
||||
|
||||
Many engineers specialize on either the operational or the analytical side. However, this book covers both operational and analytical data systems, since both play an important role in the lifecycle of data within an organization. We will explore in-depth the data infrastructure that is used to deliver services both to internal and external users, so that you can work better with your colleagues on the other side of this divide.
|
||||
|
||||
|
||||
### 分析与业务系统的特征
|
||||
|
||||
在商业数据处理的早期,数据库的写入通常对应于正在发生的*商业交易*:进行销售、向供应商下订单、支付员工的薪水等。随着数据库扩展到不涉及金钱交换的领域,*交易*一词仍然沿用,指的是构成逻辑单元的一组读写操作。
|
||||
|
||||
In the early days of business data processing, a write to the database typically corresponded to a *commercial transaction* taking place: making a sale, placing an order with a supplier, paying an employee’s salary, etc. As databases expanded into areas that didn’t involve money changing hands, the term *transaction* nevertheless stuck, referring to a group of reads and writes that form a logical unit.
|
||||
|
||||
> **注意**
|
||||
>
|
||||
> [即将提供链接]将详细探讨我们对交易的定义。本章宽泛地使用这个术语,指代低延迟的读写操作。
|
||||
|
||||
尽管数据库开始被用于许多不同类型的数据——社交媒体上的帖子、游戏中的移动、地址簿中的联系人等——基本的访问模式仍与处理商业交易类似。业务系统通常通过某个键查找少量记录(这称为*点查询*)。根据用户的输入,记录被插入、更新或删除。因为这些应用是交互式的,这种访问模式被称为*在线事务处理*(OLTP)。
|
||||
|
||||
然而,数据库也越来越多地被用于分析,其访问模式与 OLTP 有很大不同。通常,分析查询会扫描大量记录,并计算聚合统计数据(如计数、求和或平均值),而不是将个别记录返回给用户。例如,连锁超市的商业分析师可能希望回答诸如此类的问题:
|
||||
|
||||
- 我们的每家店在一月份的总收入是多少?
|
||||
- 我们在最近的促销活动中卖出的香蕉比平时多多少?
|
||||
- 哪种品牌的婴儿食品最常与某品牌的尿布一起购买?
|
||||
|
||||
这些类型的查询所产生的报告对于商业智能至关重要,帮助管理层决定下一步做什么。为了区分使用数据库的这种模式与事务处理的不同,它被称为*在线分析处理*(OLAP)[[5](ch01.html#Codd1993)]。OLTP 和分析之间的区别并不总是明确的,但[表1-1](ch01.html#tab_oltp_vs_olap)列出了一些典型的特征。
|
||||
|
||||
| 属性 | 业务系统 (OLTP) | 分析系统 (OLAP) |
|
||||
|--------|----------------|---------------|
|
||||
| 主要读取模式 | 点查询(按键提取个别记录) | 在大量记录上聚合 |
|
||||
| 主要写入模式 | 创建、更新和删除个别记录 | 批量导入(ETL)或事件流 |
|
||||
| 人类用户示例 | 网络/移动应用的终端用户 | 内部分析师,用于决策支持 |
|
||||
| 机器使用示例 | 检查是否授权某项行动 | 检测欺诈/滥用模式 |
|
||||
| 查询类型 | 固定的查询集合,由应用预定义 | 分析师可以进行任意查询 |
|
||||
| 数据表示 | 数据的最新状态(当前时间点) | 随时间发生的事件历史 |
|
||||
| 数据集大小 | 千兆字节至太字节 | 太字节至拍字节 |
|
||||
|
||||
[Link to Come] explores in detail what we mean with a transaction. This chapter uses the term loosely to refer to low-latency reads and writes.
|
||||
|
||||
Even though databases started being used for many different kinds of data—posts on social media, moves in a game, contacts in an address book, and many others—the basic access pattern remained similar to processing business transactions. An operational system typically looks up a small number of records by some key (this is called a *point query*). Records are inserted, updated, or deleted based on the user’s input. Because these applications are interactive, this access pattern became known as *online transaction processing* (OLTP).
|
||||
|
||||
However, databases also started being increasingly used for analytics, which has very different access patterns compared to OLTP. Usually an analytic query scans over a huge number of records, and calculates aggregate statistics (such as count, sum, or average) rather than returning the individual records to the user. For example, a business analyst at a supermarket chain may want to answer analytic queries such as:
|
||||
|
||||
- What was the total revenue of each of our stores in January?
|
||||
- How many more bananas than usual did we sell during our latest promotion?
|
||||
- Which brand of baby food is most often purchased together with brand X diapers?
|
||||
|
||||
The reports that result from these types of queries are important for business intelligence, helping the management decide what to do next. In order to differentiate this pattern of using databases from transaction processing, it has been called *online analytic processing* (OLAP) [[5](ch01.html#Codd1993)]. The difference between OLTP and analytics is not always clear-cut, but some typical characteristics are listed in [Table 1-1](ch01.html#tab_oltp_vs_olap).
|
||||
|
||||
| Property | Operational System (OLTP) | Analytical System (OLAP) |
|
||||
|:--------------------|:------------------------------------------------|:------------------------------------------|
|
||||
| Main read pattern | Point queries (fetch individual records by key) | Aggregate over large number of records |
|
||||
| Main write pattern | Create, update, and delete individual records | Bulk import (ETL) or event stream |
|
||||
| Human user example | End user of web/mobile application | Internal analyst, for decision support |
|
||||
| Machine use example | Checking if an action is authorized | Detecting fraud/abuse patterns |
|
||||
| Type of queries | Fixed set of queries, predefined by application | Analyst can make arbitrary queries |
|
||||
| Data represents | Latest state of data (current point in time) | History of events that happened over time |
|
||||
| Dataset size | Gigabytes to terabytes | Terabytes to petabytes |
|
||||
|
||||
> 注意
|
||||
>
|
||||
> *在线分析处理*中的*在线*一词的含义并不清晰;它可能指的是分析师不仅仅查询预定义的报告,而且还可以交互式地进行探索性的查询。
|
||||
|
||||
在业务系统中,用户通常不被允许构建自定义 SQL 查询并在数据库上运行,因为这可能允许他们读取或修改他们无权访问的数据。此外,他们可能编写执行成本高昂的查询,从而影响其他用户的数据库性能。因此,OLTP 系统大多运行固定的查询集,这些查询嵌入在应用程序代码中,仅偶尔使用一次性自定义查询进行维护或故障排除。另一方面,分析数据库通常允许用户手动编写任意 SQL 查询,或使用数据可视化或仪表板工具(如 Tableau、Looker 或 Microsoft Power BI)自动生成查询。
|
||||
|
||||
The meaning of *online* in *OLAP* is unclear; it probably refers to the fact that queries are not just for predefined reports, but that analysts use the OLAP system interactively for explorative queries.
|
||||
|
||||
With operational systems, users are generally not allowed to construct custom SQL queries and run them on the database, since that would potentially allow them to read or modify data that they do not have permission to access. Moreover, they might write queries that are expensive to execute, and hence affect the database performance for other users. For these reasons, OLTP systems mostly run a fixed set of queries that are baked into the application code, and use one-off custom queries only occasionally for maintenance or troubleshooting. On the other hand, analytic databases usually give their users the freedom to write arbitrary SQL queries by hand, or to generate queries automatically using a data visualization or dashboard tool such as Tableau, Looker, or Microsoft Power BI.
|
||||
|
||||
|
||||
### 数据仓库
|
||||
|
||||
起初,同一数据库既用于交易处理也用于分析查询。SQL在这方面证明是相当灵活的:它适用于两种类型的查询。然而,在1980年代末和1990年代初,公司停止使用OLTP系统进行分析目的,并在单独的数据库系统上运行分析的趋势日益明显。这种单独的数据库被称为*数据仓库*。
|
||||
|
||||
一家大型企业可能有几十个甚至上百个操作性交易处理系统:支撑面向客户的网站、控制实体店的销售点(结账)系统、跟踪仓库库存、规划车辆路线、管理供应商、管理员工以及执行许多其他任务的系统。每个系统都很复杂,需要一个团队来维护,因此这些系统大多独立运行。
|
||||
|
||||
通常不希望商业分析师和数据科学家直接查询这些OLTP系统,原因有几个:
|
||||
|
||||
- 感兴趣的数据可能分布在多个业务系统中,将这些数据集合并到单一查询中很困难(一个称为*数据孤岛*的问题);
|
||||
- 适合OLTP的模式和数据布局不太适合分析(见[“星型和雪花型:分析的模式”](ch03.html#sec_datamodels_analytics));
|
||||
- 分析查询可能相当昂贵,如果在OLTP数据库上运行,将影响其他用户的性能;以及
|
||||
- OLTP系统可能位于一个不允许用户直接访问的单独网络中,出于安全或合规原因。
|
||||
|
||||
与此相反,*数据仓库*是一个单独的数据库,分析师可以尽情查询,而不影响OLTP操作[[6](ch01.html#Chaudhuri1997)]。正如我们将在[即将提供链接]中看到的,数据仓库通常以与OLTP数据库非常不同的方式存储数据,以优化常见于分析的查询类型。
|
||||
|
||||
数据仓库包含公司所有各种OLTP系统中的数据的只读副本。数据从OLTP数据库中提取(使用定期数据转储或持续更新流),转换成便于分析的模式,清理后,然后加载到数据仓库中。将数据获取到数据仓库的过程称为*提取-转换-加载*(ETL),并在[图1-1](ch01.html#fig_dwh_etl)中进行了说明。有时*转换*和*加载*的顺序被交换(即在数据仓库中加载后进行转换),这就变成了*ELT*。
|
||||
|
||||
At first, the same databases were used for both transaction processing and analytic queries. SQL turned out to be quite flexible in this regard: it works well for both types of queries. Nevertheless, in the late 1980s and early 1990s, there was a trend for companies to stop using their OLTP systems for analytics purposes, and to run the analytics on a separate database system instead. This separate database was called a *data warehouse*.
|
||||
|
||||
A large enterprise may have dozens, even hundreds, of operational transaction processing systems: systems powering the customer-facing website, controlling point of sale (checkout) systems in physical stores, tracking inventory in warehouses, planning routes for vehicles, managing suppliers, administering employees, and performing many other tasks. Each of these systems is complex and needs a team of people to maintain it, so these systems end up operating mostly independently from each other.
|
||||
|
||||
It is usually undesirable for business analysts and data scientists to directly query these OLTP systems, for several reasons:
|
||||
|
||||
- the data of interest may be spread across multiple operational systems, making it difficult to combine those datasets in a single query (a problem known as *data silos*);
|
||||
- the kinds of schemas and data layouts that are good for OLTP are less well suited for analytics (see [“Stars and Snowflakes: Schemas for Analytics”](ch03.html#sec_datamodels_analytics));
|
||||
- analytic queries can be quite expensive, and running them on an OLTP database would impact the performance for other users; and
|
||||
- the OLTP systems might reside in a separate network that users are not allowed direct access to for security or compliance reasons.
|
||||
|
||||
A *data warehouse*, by contrast, is a separate database that analysts can query to their hearts’ content, without affecting OLTP operations [[6](ch01.html#Chaudhuri1997)]. As we shall see in [Link to Come], data warehouses often store data in a way that is very different from OLTP databases, in order to optimize for the types of queries that are common in analytics.
|
||||
|
||||
The data warehouse contains a read-only copy of the data in all the various OLTP systems in the company. Data is extracted from OLTP databases (using either a periodic data dump or a continuous stream of updates), transformed into an analysis-friendly schema, cleaned up, and then loaded into the data warehouse. This process of getting data into the data warehouse is known as *Extract–Transform–Load* (ETL) and is illustrated in [Figure 1-1](ch01.html#fig_dwh_etl). Sometimes the order of the *transform* and *load* steps is swapped (i.e., the transformation is done in the data warehouse, after loading), resulting in *ELT*.
|
||||
|
||||
|
||||

|
||||
|
||||
###### 图1-1 数仓ETL简化框架
|
||||
|
||||
|
||||
在某些情况下,ETL过程的数据来源是外部的SaaS产品,如客户关系管理(CRM)、电子邮件营销或信用卡处理系统。在这些情况下,你无法直接访问原始数据库,因为它只能通过软件供应商的API访问。将这些外部系统的数据引入你自己的数据仓库,可以启用SaaS API无法实现的分析。对于SaaS API的ETL通常由专业的数据连接服务实现,如Fivetran、Singer或AirByte。
|
||||
|
||||
有些数据库系统提供*混合事务/分析处理*(HTAP),旨在在单一系统中同时启用OLTP和分析,无需从一个系统向另一个系统进行ETL [[7](ch01.html#Ozcan2017),[8](ch01.html#Prout2022)]。然而,许多HTAP系统内部由一个OLTP系统与一个独立的分析系统组成,这些系统通过一个公共界面隐藏——因此,理解这两者之间的区别对于理解这些系统的工作方式非常重要。
|
||||
|
||||
此外,尽管存在HTAP,由于它们目标和要求的不同,事务性和分析性系统之间的分离仍然很常见。特别是,每个业务系统拥有自己的数据库被视为良好的实践(见[“微服务与无服务器”](ch01.html#sec_introduction_microservices)),导致有数百个独立的操作数据库;另一方面,一个企业通常只有一个数据仓库,这样业务分析师可以在单个查询中合并来自几个业务系统的数据。
|
||||
|
||||
业务系统和分析系统之间的分离是一个更广泛趋势的一部分:随着工作负载变得更加苛刻,系统变得更加专业化,并为特定工作负载优化。通用系统可以舒适地处理小数据量,但规模越大,系统趋向于变得更加专业化 [[9](ch01.html#Stonebraker2005fitsall)]。
|
||||
|
||||
In some cases the data sources of the ETL processes are external SaaS products such as customer relationship management (CRM), email marketing, or credit card processing systems. In those cases, you do not have direct access to the original database, since it is accessible only via the software vendor’s API. Bringing the data from these external systems into your own data warehouse can enable analyses that are not possible via the SaaS API. ETL for SaaS APIs is often implemented by specialist data connector services such as Fivetran, Singer, or AirByte.
|
||||
|
||||
Some database systems offer *hybrid transactional/analytic processing* (HTAP), which aims to enable OLTP and analytics in a single system without requiring ETL from one system into another [[7](ch01.html#Ozcan2017), [8](ch01.html#Prout2022)]. However, many HTAP systems internally consist of an OLTP system coupled with a separate analytical system, hidden behind a common interface—so the distinction beween the two remains important for understanding how these systems work.
|
||||
|
||||
Moreover, even though HTAP exists, it is common to have a separation between transactional and analytic systems due to their different goals and requirements. In particular, it is considered good practice for each operational system to have its own database (see [“Microservices and Serverless”](ch01.html#sec_introduction_microservices)), leading to hundreds of separate operational databases; on the other hand, an enterprise usually has a single data warehouse, so that business analysts can combine data from several operational systems in a single query.
|
||||
|
||||
The separation between operational and analytical systems is part of a wider trend: as workloads have become more demanding, systems have become more specialized and optimized for particular workloads. General-purpose systems can handle small data volumes comfortably, but the greater the scale, the more specialized systems tend to become [[9](ch01.html#Stonebraker2005fitsall)].
|
||||
|
||||
#### 从数据仓库到数据湖
|
||||
|
||||
数据仓库通常使用*关系*数据模型,通过SQL查询(见[第3章](ch03.html#ch_datamodels)),可能使用专业的商业智能软件。这种模型很适合业务分析师需要进行的类型的查询,但它不太适合数据科学家的需求,他们可能需要执行的任务如下:
|
||||
|
||||
- 将数据转换成适合训练机器学习模型的形式;这通常需要将数据库表的行和列转换为称为*特征*的数字值向量或矩阵。以一种最大化训练模型性能的方式执行这种转换的过程称为*特征工程*,它通常需要使用SQL难以表达的自定义代码。
|
||||
- 获取文本数据(例如,产品评论)并使用自然语言处理技术尝试从中提取结构化信息(例如,作者的情感或他们提到的主题)。类似地,他们可能需要使用计算机视觉技术从照片中提取结构化信息。
|
||||
|
||||
尽管已经努力在SQL数据模型中添加机器学习操作符 [[10](ch01.html#Cohen2009)] 并在关系基础上构建高效的机器学习系统 [[11](ch01.html#Olteanu2020)],许多数据科学家更喜欢不在数据仓库这类关系数据库中工作。相反,许多人更喜欢使用如pandas和scikit-learn这样的Python数据分析库,统计分析语言如R,以及分布式分析框架如Spark [[12](ch01.html#Bornstein2020)]。我们在[“数据框架、矩阵和数组”](ch03.html#sec_datamodels_dataframes)中进一步讨论这些内容。
|
||||
|
||||
因此,组织面临着使数据以适合数据科学家使用的形式可用的需求。答案是*数据湖*:一个集中的数据存储库,存放可能对分析有用的任何数据,通过ETL过程从业务系统获取。与数据仓库的不同之处在于,数据湖只包含文件,不强加任何特定的文件格式或数据模型。数据湖中的文件可能是使用如Avro或Parquet等文件格式编码的数据库记录集合(见[链接即将到来]),但它们同样可能包含文本、图像、视频、传感器读数、稀疏矩阵、特征向量、基因序列或任何其他类型的数据 [[13](ch01.html#Fowler2015)]。
|
||||
|
||||
ETL过程已经概括为*数据管道*,在某些情况下,数据湖已成为从业务系统到数据仓库的中间停靠点。数据湖包含由业务系统产生的“原始”形式的数据,而不是转换成关系数据仓库架构的数据。这种方法的优点是,每个数据的消费者都可以将原始数据转换成最适合其需要的形式。这被称为*寿司原则*:“原始数据更好” [[14](ch01.html#Johnson2015)]。
|
||||
|
||||
除了从数据湖加载数据到单独的数据仓库外,还可以直接在数据湖中的文件上运行典型的数据仓库工作负载(SQL查询和商业分析),以及数据科学/机器学习工作负载。这种架构被称为*数据湖仓*,它需要一个查询执行引擎和一个元数据(例如,模式管理)层来扩展数据湖的文件存储 [[15](ch01.html#Armbrust2021)]。Apache Hive、Spark SQL、Presto和Trino是这种方法的例子。
|
||||
|
||||
A data warehouse often uses a *relational* data model that is queried through SQL (see [Chapter 3](ch03.html#ch_datamodels)), perhaps using specialized business intelligence software. This model works well for the types of queries that business analysts need to make, but it is less well suited to the needs of data scientists, who might need to perform tasks such as:
|
||||
|
||||
- Transform data into a form that is suitable for training a machine learning model; often this requires turning the rows and columns of a database table into a vector or matrix of numerical values called *features*. The process of performing this transformation in a way that maximizes the performance of the trained model is called *feature engineering*, and it often requires custom code that is difficult to express using SQL.
|
||||
- Take textual data (e.g., reviews of a product) and use natural language processing techniques to try to extract structured information from it (e.g., the sentiment of the author, or which topics they mention). Similarly, they might need to extract structured information from photos using computer vision techniques.
|
||||
|
||||
Although there have been efforts to add machine learning operators to a SQL data model [[10](ch01.html#Cohen2009)] and to build efficient machine learning systems on top of a relational foundation [[11](ch01.html#Olteanu2020)], many data scientists prefer not to work in a relational database such as a data warehouse. Instead, many prefer to use Python data analysis libraries such as pandas and scikit-learn, statistical analysis languages such as R, and distributed analytics frameworks such as Spark [[12](ch01.html#Bornstein2020)]. We discuss these further in [“Dataframes, Matrices, and Arrays”](ch03.html#sec_datamodels_dataframes).
|
||||
|
||||
Consequently, organizations face a need to make data available in a form that is suitable for use by data scientists. The answer is a *data lake*: a centralized data repository that holds a copy of any data that might be useful for analysis, obtained from operational systems via ETL processes. The difference from a data warehouse is that a data lake simply contains files, without imposing any particular file format or data model. Files in a data lake might be collections of database records, encoded using a file format such as Avro or Parquet (see [Link to Come]), but they can equally well contain text, images, videos, sensor readings, sparse matrices, feature vectors, genome sequences, or any other kind of data [[13](ch01.html#Fowler2015)].
|
||||
|
||||
ETL processes have been generalized to *data pipelines*, and in some cases the data lake has become an intermediate stop on the path from the operational systems to the data warehouse. The data lake contains data in a “raw” form produced by the operational systems, without the transformation into a relational data warehouse schema. This approach has the advantage that each consumer of the data can transform the raw data into a form that best suits their needs. It has been dubbed the *sushi principle*: “raw data is better” [[14](ch01.html#Johnson2015)].
|
||||
|
||||
Besides loading data from a data lake into a separate data warehouse, it is also possible to run typical data warehousing workloads (SQL queries and business analytics) directly on the files in the data lake, alongside data science/machine learning workloads. This architecture is known as a *data lakehouse*, and it requires a query execution engine and a metadata (e.g., schema management) layer that extend the data lake’s file storage [[15](ch01.html#Armbrust2021)]. Apache Hive, Spark SQL, Presto, and Trino are examples of this approach.
|
||||
|
||||
|
||||
#### 数据湖之外
|
||||
|
||||
随着分析实践的成熟,组织越来越关注分析系统和数据管道的管理和运营,例如在DataOps宣言中捕捉到的内容 [[16](ch01.html#DataOps)]。其中包括治理、隐私和遵守像GDPR和CCPA这样的法规问题,我们将在[“数据系统、法律与社会”](ch01.html#sec_introduction_compliance)和[即将到来的链接]中讨论。
|
||||
|
||||
此外,分析数据越来越多地不仅以文件和关系表的形式提供,还以事件流的形式提供(见[即将到来的链接])。使用基于文件的数据分析,你可以定期(例如,每天)重新运行分析,以响应数据的变化,但流处理允许分析系统更快地响应事件,大约在几秒钟的数量级。根据应用程序和时间敏感性,流处理方法可以很有价值,例如识别并阻止潜在的欺诈或滥用行为。
|
||||
|
||||
在某些情况下,分析系统的输出会提供给业务系统(有时被称为*反向ETL* [[17](ch01.html#Manohar2021)])。例如,一个在分析系统中训练的机器学习模型可能被部署到生产中,以便它可以为终端用户生成推荐,如“购买X的人也买了Y”。这些部署的分析系统输出也被称为*数据产品* [[18](ch01.html#ORegan2018)]。机器学习模型可以使用TFX、Kubeflow或MLflow等专门工具部署到业务系统中。
|
||||
|
||||
As analytics practices have matured, organizations have been increasingly paying attention to the management and operations of analytics systems and data pipelines, as captured for example in the DataOps manifesto [[16](ch01.html#DataOps)]. Part of this are issues of governance, privacy, and compliance with regulation such as GDPR and CCPA, which we discuss in [“Data Systems, Law, and Society”](ch01.html#sec_introduction_compliance) and [Link to Come].
|
||||
|
||||
Moreover, analytical data is increasingly made available not only as files and relational tables, but also as streams of events (see [Link to Come]). With file-based data analysis you can re-run the analysis periodically (e.g., daily) in order to respond to changes in the data, but stream processing allows analytics systems to respond to events much faster, on the order of seconds. Depending on the application and how time-sensitive it is, a stream processing approach can be valuable, for example to identify and block potentially fraudulent or abusive activity.
|
||||
|
||||
In some cases the outputs of analytics systems are made available to operational systems (a process sometimes known as *reverse ETL* [[17](ch01.html#Manohar2021)]). For example, a machine-learning model that was trained on data in an analytics system may be deployed to production, so that it can generate recommendations for end-users, such as “people who bought X also bought Y”. Such deployed outputs of analytics systems are also known as *data products* [[18](ch01.html#ORegan2018)]. Machine learning models can be deployed to operational systems using specialized tools such as TFX, Kubeflow, or MLflow.
|
||||
|
||||
|
||||
### 记录系统与衍生数据系统
|
||||
|
||||
与业务系统和分析系统之间的区别相关,本书还区分了*记录系统*和*衍生数据系统*。这些术语有用,因为它们可以帮助你澄清系统中的数据流动:
|
||||
|
||||
- 记录系统
|
||||
|
||||
记录系统,也称为*真实来源*,持有某些数据的权威或*规范*版本。当新数据进入时,例如作为用户输入,首先在此处写入。每个事实只表示一次(通常是*规范化*的;见[“规范化、反规范化和连接”](ch03.html#sec_datamodels_normalization))。如果另一个系统与记录系统之间存在任何差异,则记录系统中的值(按定义)是正确的。
|
||||
|
||||
- 衍生数据系统
|
||||
|
||||
衍生系统中的数据是从另一个系统获取一些现有数据并以某种方式转换或处理的结果。如果你丢失了衍生数据,你可以从原始来源重新创建它。一个典型的例子是缓存:如果存在,可以从缓存中提供数据,但如果缓存中没有你需要的内容,你可以回退到底层数据库。非规范化的值、索引、物化视图、转换的数据表示和在数据集上训练的模型也属于这一类别。
|
||||
|
||||
从技术上讲,衍生数据是*冗余的*,因为它复制了现有的信息。然而,它通常对于读取查询的良好性能是必不可少的。你可以从单一来源派生出几个不同的数据集,使你能够从不同的“视点”查看数据。
|
||||
|
||||
分析系统通常是衍生数据系统,因为它们是在其他地方创建的数据的消费者。操作服务可能包含记录系统和衍生数据系统的混合。记录系统是首次写入数据的主要数据库,而衍生数据系统是加速常见读取操作的索引和缓存,特别是对于记录系统无法有效回答的查询。
|
||||
|
||||
大多数数据库、存储引擎和查询语言本质上不是记录系统或衍生系统。数据库只是一个工具:如何使用它取决于你。记录系统和衍生数据系统之间的区别不在于工具,而在于你如何在应用程序中使用它。通过明确哪些数据是从哪些其他数据衍生的,你可以为一个否则可能混乱的系统架构带来清晰度。
|
||||
|
||||
当一个系统中的数据是从另一个系统的数据衍生的时候,你需要一个过程来更新衍生数据,当记录系统中的原始数据发生变化时。不幸的是,许多数据库的设计基于这样的假设:你的应用程序只需要使用那一个数据库,它们并不容易整合多个系统以传播这些更新。在[即将到来的链接]中,我们将讨论*数据集成*的方法,这些方法允许我们组合多个数据系统来实现一个系统无法单独做到的事情。
|
||||
|
||||
这标志着我们对分析和交易处理的比较的结束。在下一节中,我们将探讨一个你可能已经看到多次争论的另一个折衷方案。
|
||||
|
||||
Related to the distinction between operational and analytical systems, this book also distinguishes between *systems of record* and *derived data systems*. These terms are useful because they can help you clarify the flow of data through a system:
|
||||
|
||||
- Systems of record
|
||||
|
||||
A system of record, also known as *source of truth*, holds the authoritative or *canonical* version of some data. When new data comes in, e.g., as user input, it is first written here. Each fact is represented exactly once (the representation is typically *normalized*; see [“Normalization, Denormalization, and Joins”](ch03.html#sec_datamodels_normalization)). If there is any discrepancy between another system and the system of record, then the value in the system of record is (by definition) the correct one.
|
||||
|
||||
- Derived data systems
|
||||
|
||||
Data in a derived system is the result of taking some existing data from another system and transforming or processing it in some way. If you lose derived data, you can recreate it from the original source. A classic example is a cache: data can be served from the cache if present, but if the cache doesn’t contain what you need, you can fall back to the underlying database. Denormalized values, indexes, materialized views, transformed data representations, and models trained on a dataset also fall into this category.
|
||||
|
||||
Technically speaking, derived data is *redundant*, in the sense that it duplicates existing information. However, it is often essential for getting good performance on read queries. You can derive several different datasets from a single source, enabling you to look at the data from different “points of view.”
|
||||
|
||||
Analytical systems are usually derived data systems, because they are consumers of data created elsewhere. Operational services may contain a mixture of systems of record and derived data systems. The systems of record are the primary databases to which data is first written, whereas the derived data systems are the indexes and caches that speed up common read operations, especially for queries that the system of record cannot answer efficiently.
|
||||
|
||||
Most databases, storage engines, and query languages are not inherently a system of record or a derived system. A database is just a tool: how you use it is up to you. The distinction between system of record and derived data system depends not on the tool, but on how you use it in your application. By being clear about which data is derived from which other data, you can bring clarity to an otherwise confusing system architecture.
|
||||
|
||||
When the data in one system is derived from the data in another, you need a process for updating the derived data when the original in the system of record changes. Unfortunately, many databases are designed based on the assumption that your application only ever needs to use that one database, and they do not make it easy to integrate multiple systems in order to propagate such updates. In [Link to Come] we will discuss approaches to *data integration*, which allow us to compose multiple data systems to achieve things that one system alone cannot do.
|
||||
|
||||
That brings us to the end of our comparison of analytics and transaction processing. In the next section, we will examine another trade-off that you might have already seen debated multiple times.
|
||||
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 云服务与自托管
|
||||
|
||||
对于组织需要执行的任何事务,首先要问的问题之一是:应该在内部完成还是外包?您应该自行构建还是购买?
|
||||
|
||||
这最终是一个关于业务优先级的问题。管理学的普遍观点是,作为组织的核心能力或竞争优势的事物应该在内部完成,而非核心、常规或普通的事务则应交给供应商处理 [[19](ch01.html#Fournier2021)]。举一个极端的例子,大多数公司不会自己发电(除非它们是能源公司,且不考虑紧急备用电力),因为从电网购买电力更便宜。
|
||||
|
||||
在软件方面,需要做出的两个重要决策是谁来构建软件以及谁来部署它。有一个将每个决策外包出去的可能性的范围,如[图 1-2](ch01.html#fig_cloud_spectrum)所示。一个极端是你编写并在内部运行的定制软件;另一个极端是广泛使用的云服务或软件即服务(SaaS)产品,由外部供应商实施和操作,你只能通过Web界面或API访问。
|
||||
|
||||
With anything that an organization needs to do, one of the first questions is: should it be done in-house, or should it be outsourced? Should you build or should you buy?
|
||||
|
||||
Ultimately, this is a question about business priorities. The received management wisdom is that things that are a core competency or a competitive advantage of your organization should be done in-house, whereas things that are non-core, routine, or commonplace should be left to a vendor [[19](ch01.html#Fournier2021)]. To give an extreme example, most companies do not generate their own electricity (unless they are an energy company, and leaving aside emergency backup power), since it is cheaper to buy electricity from the grid.
|
||||
|
||||
With software, two important decisions to be made are who builds the software and who deploys it. There is a spectrum of possibilities that outsource each decision to various degrees, as illustrated in [Figure 1-2](ch01.html#fig_cloud_spectrum). At one extreme is bespoke software that you write and run in-house; at the other extreme are widely-used cloud services or Software as a Service (SaaS) products that are implemented and operated by an external vendor, and which you only access through a web interface or API.
|
||||
|
||||

|
||||
|
||||
###### 图 1-2. 软件及其运营的类型范围。 A spectrum of types of software and its operations.
|
||||
|
||||
中间地带是你自行托管的现成软件(开源或商业的),即自己部署的软件——例如,如果你下载MySQL并将其安装在你控制的服务器上。这可能是在你自己的硬件上(通常称为*本地部署*,即使服务器实际上位于租用的数据中心机架中,也不一定真的在你自己的场所内),或者在云中的虚拟机上(即*基础设施即服务*或IaaS)。在这个范围中还有更多点,例如,运行修改过的开源软件。
|
||||
|
||||
与此范围分开的还有一个问题,即你是如何部署服务的,无论是在云中还是本地——例如,你是否使用像Kubernetes这样的编排框架。然而,部署工具的选择超出了本书的范围,因为其他因素对数据系统的架构有更大的影响。
|
||||
|
||||
The middle ground is off-the-shelf software (open source or commercial) that you *self-host*, i.e., deploy yourself—for example, if you download MySQL and install it on a server you control. This could be on your own hardware (often called *on-premises*, even if the server is actually in a rented datacenter rack and not literally on your own premises), or on a virtual machine in the cloud (*Infrastructure as a Service* or IaaS). There are still more points along this spectrum, e.g., taking open source software and running a modified version of it.
|
||||
|
||||
Seperately from this spectrum there is also the question of *how* you deploy services, either in the cloud or on-premises—for example, whether you use an orchestration framework such as Kubernetes. However, choice of deployment tooling is out of scope of this book, since other factors have a greater influence on the architecture of data systems.
|
||||
|
||||
|
||||
### 云服务的优缺点
|
||||
|
||||
使用云服务,而不是自己运行可比软件,本质上是将该软件的运营外包给云提供商。支持和反对使用云服务的理由都很充分。云提供商声称使用他们的服务可以节省时间和金钱,并允许你比建立自己的基础设施更快地行动。
|
||||
|
||||
云服务是否实际上比自托管更便宜和更容易,很大程度上取决于你的技能和系统的工作负载。如果你已经有设置和操作所需系统的经验,并且你的负载相当可预测(即,你需要的机器数量不会剧烈波动),那么通常购买自己的机器并自己运行软件会更便宜 [[20](ch01.html#HeinemeierHansson2022), [21](ch01.html#Badizadegan2022)]。
|
||||
|
||||
另一方面,如果你需要一个你不知道如何部署和操作的系统,那么采用云服务通常比自己学习管理系统更容易且更快。如果你必须雇佣并培训专门的员工来维护和业务系统,这可能非常昂贵。当你使用云时,仍然需要一个运营团队(见[“云时代的运营”](ch01.html#sec_introduction_operations)),但将基本的系统管理外包可以释放你的团队,专注于更高层次的问题。
|
||||
|
||||
当你将系统的运营外包给专门运营该服务的公司时,这可能会带来更好的服务,因为提供商从为许多客户提供服务中获得运营专长。另一方面,如果你自己运行服务,你可以配置并调整它以在你特定的工作负载上表现良好;云服务不太可能愿意代表你进行此类定制。
|
||||
|
||||
如果你的系统负载随时间变化很大,云服务特别有价值。如果你配置你的机器能够处理高峰负载,但这些计算资源大部分时间都处于空闲状态,系统的成本效益就会降低。在这种情况下,云服务的优势在于它们可以更容易地根据需求变化扩展或缩减你的计算资源。
|
||||
|
||||
例如,分析系统的负载通常变化极大:快速运行大型分析查询需要大量并行的计算资源,但一旦查询完成,这些资源就会闲置,直到用户发出下一个查询。预定义的查询(例如,用于日常报告的查询)可以排队并安排以平滑负载,但对于交互式查询,你希望它们完成得越快,工作负载就越变化无常。如果你的数据集非常大,以至于快速查询需要大量计算资源,使用云可以节省金钱,因为你可以将未使用的资源返回给提供商,而不是让它们闲置。对于较小的数据集,这种差异不那么显著。
|
||||
|
||||
云服务最大的缺点是你对它没有控制权:
|
||||
|
||||
- 如果它缺少你需要的功能,你唯一能做的就是礼貌地询问供应商是否会添加它;你通常无法自己实现它。
|
||||
- 如果服务出现故障,你只能等待它恢复。
|
||||
- 如果你以某种方式使用服务,触发了一个错误或导致性能问题,你很难诊断问题。对于你自己运行的软件,你可以从业务系统获取性能指标和调试信息来帮助你了解其行为,你可以查看服务器日志,但使用供应商托管的服务时,你通常无法访问这些内部信息。
|
||||
- 此外,如果服务关闭或变得无法接受地昂贵,或者如果供应商决定以你不喜欢的方式更改其产品,你将受制于他们——继续运行软件的旧版本通常不是一个选项,因此你将被迫迁移到另一个服务 [[22](ch01.html#Yegge2020)]。如果有提供兼容API的替代服务,这种风险可以缓解,但对于许多云服务,没有标准的API,这增加了切换的成本,使供应商锁定成为一个问题。
|
||||
|
||||
尽管存在这些风险,组织构建基于云服务的新应用变得越来越流行。然而,云服务并不能取代所有的内部数据系统:许多旧系统早于云技术,且对于那些现有云服务无法满足的特殊需求,内部系统仍然是必需的。例如,像高频交易这样对延迟极其敏感的应用需要完全控制硬件。
|
||||
|
||||
Using a cloud service, rather than running comparable software yourself, essentially outsources the operation of that software to the cloud provider. There are good arguments for and against cloud services. Cloud providers claim that using their services saves you time and money, and allows you to move faster compared to setting up your own infrastructure.
|
||||
|
||||
Whether a cloud service is actually cheaper and easier than self-hosting depends very much on your skills and the workload on your systems. If you already have experience setting up and operating the systems you need, and if your load is quite predictable (i.e., the number of machines you need does not fluctuate wildly), then it’s often cheaper to buy your own machines and run the software on them yourself [[20](ch01.html#HeinemeierHansson2022), [21](ch01.html#Badizadegan2022)].
|
||||
|
||||
On the other hand, if you need a system that you don’t already know how to deploy and operate, then adopting a cloud service is often easier and quicker than learning to manage the system yourself. If you have to hire and train staff specifically to maintain and operate the system, that can get very expensive. You still need an operations team when you’re using the cloud (see [“Operations in the Cloud Era”](ch01.html#sec_introduction_operations)), but outsourcing the basic system administration can free up your team to focus on higher-level concerns.
|
||||
|
||||
When you outsource the operation of a system to a company that specializes in running that service, that can potentially result in a better service, since the provider gains operational expertise from providing the service to many customers. On the other hand, if you run the service yourself, you can configure and tune it to perform well on your particular workload; it is unlikely that a cloud service would be willing to make such customizations on your behalf.
|
||||
|
||||
Cloud services are particularly valuable if the load on your systems varies a lot over time. If you provision your machines to be able to handle peak load, but those computing resources are idle most of the time, the system becomes less cost-effective. In this situation, cloud services have the advantage that they can make it easier to scale your computing resources up or down in response to changes in demand.
|
||||
|
||||
For example, analytics systems often have extremely variable load: running a large analytical query quickly requires a lot of computing resources in parallel, but once the query completes, those resources sit idle until the user makes the next query. Predefined queries (e.g., for daily reports) can be enqueued and scheduled to smooth out the load, but for interactive queries, the faster you want them to complete, the more variable the workload becomes. If your dataset is so large that querying it quickly requires significant computing resources, using the cloud can save money, since you can return unused resources to the provider rather than leaving them idle. For smaller datasets, this difference is less significant.
|
||||
|
||||
The biggest downside of a cloud service is that you have no control over it:
|
||||
|
||||
- If it is lacking a feature you need, all you can do is to politely ask the vendor whether they will add it; you generally cannot implement it yourself.
|
||||
- If the service goes down, all you can do is to wait for it to recover.
|
||||
- If you are using the service in a way that triggers a bug or causes performance problems, it will be difficult for you to diagnose the issue. With software that you run yourself, you can get performance metrics and debugging information from the operating system to help you understand its behavior, and you can look at the server logs, but with a service hosted by a vendor you usually do not have access to these internals.
|
||||
- Moreover, if the service shuts down or becomes unacceptably expensive, or if the vendor decides to change their product in a way you don’t like, you are at their mercy—continuing to run an old version of the software is usually not an option, so you will be forced to migrate to an alternative service [[22](ch01.html#Yegge2020)]. This risk is mitigated if there are alternative services that expose a compatible API, but for many cloud services there are no standard APIs, which raises the cost of switching, making vendor lock-in a problem.
|
||||
|
||||
Despite all these risks, it has become more and more popular for organizations to build new applications on top of cloud services. However, cloud services will not subsume all in-house data systems: many older systems predate the cloud, and for any services that have specialist requirements that existing cloud services cannot meet, in-house systems remain necessary. For example, very latency-sensitive applications such as high-frequency trading require full control of the hardware.
|
||||
|
||||
|
||||
--------
|
||||
|
||||
### 云原生系统架构
|
||||
|
||||
除了经济模式的不同(订阅服务而非购买硬件并在其上运行许可软件),云计算的兴起还在技术层面深刻影响了数据系统的实施方式。*云原生* 一词用来描述一种旨在利用云服务优势的架构。
|
||||
|
||||
原则上,几乎任何你可以自行托管的软件也可以作为云服务提供,实际上,许多流行的数据系统现在已经有了这样的托管服务。然而,从底层设计为云原生的系统显示出多项优势:在相同硬件上有更好的性能,从失败中更快恢复,能迅速扩展计算资源以匹配负载,并支持更大的数据集[[23](ch01.html#Verbitski2017), [24](ch01.html#Antonopoulos2019_ch1), [25](ch01.html#Vuppalapati2020)]。[表 1-2](ch01.html#tab_cloud_native_dbs)列出了这两类系统的一些例子。
|
||||
|
||||
| 类别 | 自托管系统 | 云原生系统 |
|
||||
|----------|-----------------------------|---------------------------------------------------------------------|
|
||||
| 事务型/OLTP | MySQL, PostgreSQL, MongoDB | AWS Aurora 【23】, Azure SQL DB Hyperscale 【24】, Google Cloud Spanner |
|
||||
| 分析型/OLAP | Teradata, ClickHouse, Spark | Snowflake 【25】, Google BigQuery, Azure Synapse Analytics |
|
||||
|
||||
Besides having a different economic model (subscribing to a service instead of buying hardware and licensing software to run on it), the rise of the cloud has also had a profound effect on how data systems are implemented on a technical level. The term *cloud-native* is used to describe an architecture that is designed to take advantage of cloud services.
|
||||
|
||||
In principle, almost any software that you can self-host could also be provided as a cloud service, and indeed such managed services are now available for many popular data systems. However, systems that have been designed from the ground up to be cloud-native have been shown to have several advantages: better performance on the same hardware, faster recovery from failures, being able to quickly scale computing resources to match the load, and supporting larger datasets [[23](ch01.html#Verbitski2017), [24](ch01.html#Antonopoulos2019_ch1), [25](ch01.html#Vuppalapati2020)]. [Table 1-2](ch01.html#tab_cloud_native_dbs) lists some examples of both types of systems.
|
||||
|
||||
|
||||
|
||||
#### 云服务的分层
|
||||
|
||||
许多自托管的数据系统具有非常简单的系统要求:它们运行在常规业务系统如 Linux 或 Windows 上,它们将数据存储为文件系统上的文件,并通过标准网络协议如 TCP/IP 进行通信。一些系统依赖于特殊硬件,如用于机器学习的 GPU 或 RDMA 网络接口,但总体来说,自托管软件倾向于使用非常通用的计算资源:CPU、RAM、文件系统和 IP 网络。
|
||||
|
||||
在云中,这类软件可以在基础设施即服务(IaaS)环境中运行,使用一个或多个具有一定CPU、内存、磁盘和网络带宽配额的虚拟机(或*实例*)。与物理机相比,云实例可以更快地配置,并且大小种类更多,但在其他方面它们类似于传统计算机:你可以在其上运行任何软件,但你需要自己负责管理。
|
||||
|
||||
相比之下,云原生服务的关键思想是不仅使用由业务系统管理的计算资源,还要构建在更低层级的云服务之上,创建更高层级的服务。例如:
|
||||
|
||||
- *对象存储*服务,如亚马逊 S3、Azure Blob 存储和 Cloudflare R2 存储大文件。它们提供的 API 比典型文件系统的 API 更有限(基本的文件读写),但它们的优势在于隐藏了底层的物理机器:服务自动将数据分布在许多机器上,因此你无需担心任何一台机器上的磁盘空间耗尽。即使某些机器或其磁盘完全失败,也不会丢失数据。
|
||||
- 许多其他服务又是建立在对象存储和其他云服务之上的:例如,Snowflake 是一种基于云的分析数据库(数据仓库),依赖于 S3 进行数据存储 [[25](ch01.html#Vuppalapati2020)],还有一些服务又建立在 Snowflake 之上。
|
||||
|
||||
正如计算中的抽象总是一样,关于你应该使用什么,没有一个正确的答案。一般规则是,更高层次的抽象往往更针对特定用例。如果你的需求与更高层系统设计的情况匹配,使用现有的更高层系统可能会比从更低层系统自行构建省去许多麻烦。另一方面,如果没有高层系统满足你的需求,那么自己从更低层组件构建是唯一的选择。
|
||||
|
||||
Many self-hosted data systems have very simple system requirements: they run on a conventional operating system such as Linux or Windows, they store their data as files on the filesystem, and they communicate via standard network protocols such as TCP/IP. A few systems depend on special hardware such as GPUs (for machine learning) or RDMA network interfaces, but on the whole, self-hosted software tends to use very generic computing resources: CPU, RAM, a filesystem, and an IP network.
|
||||
|
||||
In a cloud, this type of software can be run on an Infrastructure-as-a-Service environment, using one or more virtual machines (or *instances*) with a certain allocation of CPUs, memory, disk, and network bandwidth. Compared to physical machines, cloud instances can be provisioned faster and they come in a greater variety of sizes, but otherwise they are similar to a traditional computer: you can run any software you like on it, but you are responsible for administering it yourself.
|
||||
|
||||
In contrast, the key idea of cloud-native services is to use not only the computing resources managed by your operating system, but also to build upon lower-level cloud services to create higher-level services. For example:
|
||||
|
||||
- *Object storage* services such as Amazon S3, Azure Blob Storage, and Cloudflare R2 store large files. They provide more limited APIs than a typical filesystem (basic file reads and writes), but they have the advantage that they hide the underlying physical machines: the service automatically distributes the data across many machines, so that you don’t have to worry about running out of disk space on any one machine. Even if some machines or their disks fail entirely, no data is lost.
|
||||
- Many other services are in turn built upon object storage and other cloud services: for example, Snowflake is a cloud-based analytic database (data warehouse) that relies on S3 for data storage [[25](ch01.html#Vuppalapati2020)], and some other services in turn build upon Snowflake.
|
||||
|
||||
As always with abstractions in computing, there is no one right answer to what you should use. As a general rule, higher-level abstractions tend to be more oriented towards particular use cases. If your needs match the situations for which a higher-level system is designed, using the existing higher-level system will probably provide what you need with much less hassle than building it yourself from lower-level systems. On the other hand, if there is no high-level system that meets your needs, then building it yourself from lower-level components is the only option.
|
||||
|
||||
|
||||
|
||||
#### 存储与计算分离
|
||||
|
||||
在传统计算中,磁盘存储被视为持久的(我们假设一旦某些内容被写入磁盘,它就不会丢失);为了容忍单个硬盘的失败,经常使用 RAID 来在几个磁盘上维护数据的副本。在云中,计算实例(虚拟机)也可能有本地磁盘附加,但云原生系统通常将这些磁盘更像是临时缓存,而不是长期存储。这是因为如果关联实例失败,或者为了适应负载变化而用更大或更小的实例替换实例(在不同的物理机上),本地磁盘将变得无法访问。
|
||||
|
||||
作为本地磁盘的替代,云服务还提供了可以从一个实例分离并连接到另一个实例的虚拟磁盘存储(Amazon EBS、Azure 管理磁盘和 Google Cloud 中的持久磁盘)。这种虚拟磁盘实际上不是物理磁盘,而是由一组独立机器提供的云服务,模拟磁盘(块设备)的行为(每个块通常为 4 KiB 大小)。这项技术使得在云中运行传统基于磁盘的软件成为可能,但它通常表现出较差的性能和可扩展性 [[23](ch01.html#Verbitski2017)]。
|
||||
|
||||
为解决这个问题,云原生服务通常避免使用虚拟磁盘,而是建立在专门为特定工作负载优化的专用存储服务之上。如 S3 等对象存储服务旨在长期存储相对较大的文件,大小从数百千字节到几个千兆字节不等。存储在数据库中的单独行或值通常比这小得多;因此云数据库通常在单独的服务中管理更小的值,并在对象存储中存储更大的数据块(包含许多单独的值) [[24](ch01.html#Antonopoulos2019_ch1)]。
|
||||
|
||||
在传统的系统架构中,同一台计算机负责存储(磁盘)和计算(CPU 和 RAM),但在云原生系统中,这两种责任已经有所分离或*解耦* [[8](ch01.html#Prout2022), [25](ch01.html#Vuppalapati2020), [26](https://learning.oreilly.com/library/view/designing-data-intensive-applications/)]。例如,S3仅存储文件,如果你想分析那些数据,你将不得不在 S3 外部的某处运行分析代码。这意味着需要通过网络传输数据,我们将在[“分布式与单节点系统”](ch01.html#sec_introduction_distributed)中进一步讨论这一点。
|
||||
|
||||
此外,云原生系统通常是*多租户*的,这意味着它们不是为每个客户配置单独的机器,而是在同一共享硬件上由同一服务处理来自几个不同客户的数据和计算 [[28](ch01.html#Vanlightly2023)]。多租户可以实现更好的硬件利用率、更容易的可扩展性和云提供商更容易的管理,但它也需要精心的工程设计,以确保一个客户的活动不影响系统对其他客户的性能或安全性 [[29](ch01.html#Jonas2019)]。
|
||||
|
||||
In traditional computing, disk storage is regarded as durable (we assume that once something is written to disk, it will not be lost); to tolerate the failure of an individual hard disk, RAID is often used to maintain copies of the data on several disks. In the cloud, compute instances (virtual machines) may also have local disks attached, but cloud-native systems typically treat these disks more like an ephemeral cache, and less like long-term storage. This is because the local disk becomes inaccessible if the associated instance fails, or if the instance is replaced with a bigger or a smaller one (on a different physical machine) in order to adapt to changes in load.
|
||||
|
||||
As an alternative to local disks, cloud services also offer virtual disk storage that can be detached from one instance and attached to a different one (Amazon EBS, Azure managed disks, and persistent disks in Google Cloud). Such a virtual disk is not actually a physical disk, but rather a cloud service provided by a separate set of machines, which emulates the behavior of a disk (a *block device*, where each block is typically 4 KiB in size). This technology makes it possible to run traditional disk-based software in the cloud, but it often suffers from poor performance and poor scalability [[23](ch01.html#Verbitski2017)].
|
||||
|
||||
To address this problem, cloud-native services generally avoid using virtual disks, and instead build on dedicated storage services that are optimized for particular workloads. Object storage services such as S3 are designed for long-term storage of fairly large files, ranging from hundreds of kilobytes to several gigabytes in size. The individual rows or values stored in a database are typically much smaller than this; cloud databases therefore typically manage smaller values in a separate service, and store larger data blocks (containing many individual values) in an object store [[24](ch01.html#Antonopoulos2019_ch1)].
|
||||
|
||||
In a traditional systems architecture, the same computer is responsible for both storage (disk) and computation (CPU and RAM), but in cloud-native systems, these two responsibilities have become somewhat separated or *disaggregated* [[8](ch01.html#Prout2022), [25](ch01.html#Vuppalapati2020), [26](ch01.html#Shapira2023), [27](ch01.html#Murthy2022)]: for example, S3 only stores files, and if you want to analyze that data, you will have to run the analysis code somewhere outside of S3. This implies transferring the data over the network, which we will discuss further in [“Distributed versus Single-Node Systems”](ch01.html#sec_introduction_distributed).
|
||||
|
||||
Moreover, cloud-native systems are often *multitenant*, which means that rather than having a separate machine for each customer, data and computation from several different customers are handled on the same shared hardware by the same service [[28](ch01.html#Vanlightly2023)]. Multitenancy can enable better hardware utilization, easier scalability, and easier management by the cloud provider, but it also requires careful engineering to ensure that one customer’s activity does not affect the performance or security of the system for other customers [[29](ch01.html#Jonas2019)].
|
||||
|
||||
|
||||
--------
|
||||
|
||||
### 在云时代的运营
|
||||
|
||||
传统上,管理组织服务器端数据基础设施的人被称为*数据库管理员*(DBAs)或*系统管理员*(sysadmins)。近年来,许多组织试图将软件开发和运营的角色整合到一个团队中,共同负责后端服务和数据基础设施;*DevOps*哲学指导了这一趋势。*站点可靠性工程师*(SREs)是谷歌实施这一理念的方式 [[30](ch01.html#Beyer2016)]。
|
||||
|
||||
运营的角色是确保服务可靠地交付给用户(包括配置基础设施和部署应用程序),并确保稳定的生产环境(包括监控和诊断可能影响可靠性的问题)。对于自托管系统,运营传统上涉及大量单机层面的工作,如容量规划(例如,监控可用磁盘空间并在空间用尽前添加更多磁盘)、配置新机器、将服务从一台机器移至另一台以及安装业务系统补丁。
|
||||
|
||||
许多云服务提供了一个API,隐藏了实际实现服务的单个机器。例如,云存储用*计量计费*取代了固定大小的磁盘,您可以在不提前规划容量需求的情况下存储数据,并根据实际使用的空间收费。此外,许多云服务即使单个机器失败也能保持高可用性(见[“可靠性和容错”](ch02.html#sec_introduction_reliability))。
|
||||
|
||||
从单个机器到服务的这种重点转变伴随着运营角色的变化。提供可靠服务的高级目标仍然相同,但过程和工具已经演变。DevOps/SRE哲学更加强调:
|
||||
|
||||
- 自动化——偏好可重复的过程而不是一次性的手工作业,
|
||||
- 偏好短暂的虚拟机和服务而不是长时间运行的服务器,
|
||||
- 促进频繁的应用更新,
|
||||
- 从事件中学习,
|
||||
- 即使个别人员来去,也要保留组织对系统的知识 [[31](ch01.html#Limoncelli2020)]。
|
||||
|
||||
随着云服务的兴起,角色出现了分化:基础设施公司的运营团队专注于向大量客户提供可靠服务的细节,而服务的客户尽可能少地花时间和精力在基础设施上 [[32](ch01.html#Majors2020)]。
|
||||
|
||||
云服务的客户仍然需要运营,但他们关注的方面不同,如选择最适合特定任务的服务、将不同服务相互集成以及从一个服务迁移到另一个服务。尽管计量计费消除了传统意义上的容量规划的需要,但仍然重要的是了解您正在使用哪些资源以及用途,以免在不需要的云资源上浪费金钱:容量规划变成了财务规划,性能优化变成了成本优化 [[33](ch01.html#Cherkasky2021)]。此外,云服务确实有资源限制或*配额*(如您可以同时运行的最大进程数),您需要了解并计划这些限制,以免遇到问题 [[34](ch01.html#Kushchi2023)]。
|
||||
|
||||
采用云服务可能比运行自己的基础设施更容易且更快,尽管即使在这里,学习如何使用它和可能绕过其限制也有成本。随着越来越多的供应商提供针对不同用例的更广泛的云服务,不同服务之间的集成成为特别的挑战 [[35](ch01.html#Bernhardsson2021), [36](ch01.html#Stancil2021)]。ETL(见[“数据仓库”](ch01.html#sec_introduction_dwh))只是故事的一部分;运营云服务也需要相互集成。目前缺乏促进此类集成的标准,因此它通常涉及大量的手动努力。
|
||||
|
||||
其他不能完全外包给云服务的运营方面包括维护应用程序及其使用的库的安全性、管理自己的服务之间的互动、监控服务的负载以及追踪性能下降或中断等问题的原因。虽然云正在改变运营的角色,但运营的需求依旧迫切。
|
||||
|
||||
|
||||
Traditionally, the people managing an organization’s server-side data infrastructure were known as *database administrators* (DBAs) or *system administrators* (sysadmins). More recently, many organizations have tried to integrate the roles of software development and operations into teams with a shared responsibility for both backend services and data infrastructure; the *DevOps* philosophy has guided this trend. *Site Reliability Engineers* (SREs) are Google’s implementation of this idea [[30](ch01.html#Beyer2016)].
|
||||
|
||||
The role of operations is to ensure services are reliably delivered to users (including configuring infrastructure and deploying applications), and to ensure a stable production environment (including monitoring and diagnosing any problems that may affect reliability). For self-hosted systems, operations traditionally involves a significant amount of work at the level of individual machines, such as capacity planning (e.g., monitoring available disk space and adding more disks before you run out of space), provisioning new machines, moving services from one machine to another, and installing operating system patches.
|
||||
|
||||
Many cloud services present an API that hides the individual machines that actually implement the service. For example, cloud storage replaces fixed-size disks with *metered billing*, where you can store data without planning your capacity needs in advance, and you are then charged based on the space actually used. Moreover, many cloud services remain highly available, even when individual machines have failed (see [“Reliability and Fault Tolerance”](ch02.html#sec_introduction_reliability)).
|
||||
|
||||
This shift in emphasis from individual machines to services has been accompanied by a change in the role of operations. The high-level goal of providing a reliable service remains the same, but the processes and tools have evolved. The DevOps/SRE philosophy places greater emphasis on:
|
||||
|
||||
- automation—preferring repeatable processes over manual one-off jobs,
|
||||
- preferring ephemeral virtual machines and services over long running servers,
|
||||
- enabling frequent application updates,
|
||||
- learning from incidents, and
|
||||
- preserving the organization’s knowledge about the system, even as individual people come and go [[31](ch01.html#Limoncelli2020)].
|
||||
|
||||
With the rise of cloud services, there has been a bifurcation of roles: operations teams at infrastructure companies specialize in the details of providing a reliable service to a large number of customers, while the customers of the service spend as little time and effort as possible on infrastructure [[32](ch01.html#Majors2020)].
|
||||
|
||||
Customers of cloud services still require operations, but they focus on different aspects, such as choosing the most appropriate service for a given task, integrating different services with each other, and migrating from one service to another. Even though metered billing removes the need for capacity planning in the traditional sense, it’s still important to know what resources you are using for which purpose, so that you don’t waste money on cloud resources that are not needed: capacity planning becomes financial planning, and performance optimization becomes cost optimization [[33](ch01.html#Cherkasky2021)]. Moreover, cloud services do have resource limits or *quotas* (such as the maximum number of processes you can run concurrently), which you need to know about and plan for before you run into them [[34](ch01.html#Kushchi2023)].
|
||||
|
||||
Adopting a cloud service can be easier and quicker than running your own infrastructure, although even here there is a cost in learning how to use it, and perhaps working around its limitations. Integration between different services becomes a particular challenge as a growing number of vendors offers an ever broader range of cloud services targeting different use cases [[35](ch01.html#Bernhardsson2021), [36](ch01.html#Stancil2021)]. ETL (see [“Data Warehousing”](ch01.html#sec_introduction_dwh)) is only part of the story; operational cloud services also need to be integrated with each other. At present, there is a lack of standards that would facilitate this sort of integration, so it often involves significant manual effort.
|
||||
|
||||
Other operational aspects that cannot fully be outsourced to cloud services include maintaining the security of an application and the libraries it uses, managing the interactions between your own services, monitoring the load on your services, and tracking down the cause of problems such as performance degradations or outages. While the cloud is changing the role of operations, the need for operations is as great as ever.
|
||||
|
||||
|
||||
|
||||
|
||||
-------
|
||||
|
||||
## 分布式与单节点系统
|
||||
|
||||
一个涉及通过网络进行通信的多台机器的系统被称为*分布式系统*。参与分布式系统的每个进程被称为*节点*。您可能希望系统分布式的原因有多种:
|
||||
|
||||
- 固有的分布式系统
|
||||
|
||||
如果一个应用程序涉及两个或更多互动的用户,每个用户都使用自己的设备,那么该系统不可避免地是分布式的:设备之间的通信必须通过网络进行。
|
||||
|
||||
- 云服务间的请求
|
||||
|
||||
如果数据存储在一个服务中但在另一个服务中处理,则必须通过网络从一个服务传输到另一个服务。
|
||||
|
||||
- 容错/高可用性
|
||||
|
||||
如果您的应用程序需要在一台机器(或多台机器、网络或整个数据中心)宕机时仍然继续工作,您可以使用多台机器来提供冗余。当一台机器失败时,另一台可以接管。见[“可靠性和容错”](ch02.html#sec_introduction_reliability)。
|
||||
|
||||
- 可扩展性
|
||||
|
||||
如果您的数据量或计算需求超过单台机器的处理能力,您可以将负载分散到多台机器上。见[“可扩展性”](ch02.html#sec_introduction_scalability)。
|
||||
|
||||
- 延迟
|
||||
|
||||
如果您的用户遍布全球,您可能希望在全球各地设置服务器,以便每个用户都可以从地理位置靠近他们的数据中心获得服务。这避免了用户必须等待网络包绕地球半圈来响应他们的请求。见[“描述性能”](ch02.html#sec_introduction_percentiles)。
|
||||
|
||||
- 弹性
|
||||
|
||||
如果您的应用程序在某些时候忙碌而在其他时候空闲,云部署可以根据需求扩展或缩减,因此您只需为您实际使用的资源付费。这在单台机器上更难实现,因为它需要预先配置好以应对最大负载,即使在很少使用时也是如此。
|
||||
|
||||
- 使用专用硬件
|
||||
|
||||
系统的不同部分可以利用不同类型的硬件来匹配它们的工作负载。例如,对象存储可能使用多硬盘但CPU较少的机器,而数据分析系统可能使用CPU和内存多但没有硬盘的机器,机器学习系统可能使用GPU(对于训练深度神经网络和其他机器学习任务比CPU更高效)的机器。
|
||||
|
||||
- 法律合规
|
||||
|
||||
一些国家有数据居留法律,要求在其管辖区内的人的数据必须在该国地理范围内存储和处理 [[37](ch01.html#Korolov2022)]。这些规则的范围各不相同——例如,在某些情况下,它仅适用于医疗或财务数据,而其他情况则更广泛。因此,一个在几个这样的司法管辖区有用户的服务将不得不将其数据分布在几个位置的服务器上。
|
||||
|
||||
这些原因适用于您自己编写的服务(应用程序代码)和由现成软件组成的服务(例如数据库)。
|
||||
|
||||
|
||||
A system that involves several machines communicating via a network is called a *distributed system*. Each of the processes participating in a distributed system is called a *node*. There are various reasons why you might want a system to be distributed:
|
||||
|
||||
- Inherently distributed systems
|
||||
|
||||
If an application involves two or more interacting users, each using their own device, then the system is unavoidably distributed: the communication between the devices will have to go via a network.
|
||||
|
||||
- Requests between cloud services
|
||||
|
||||
If data is stored in one service but processed in another, it must be transferred over the network from one service to the other.
|
||||
|
||||
- Fault tolerance/high availability
|
||||
|
||||
If your application needs to continue working even if one machine (or several machines, or the network, or an entire datacenter) goes down, you can use multiple machines to give you redundancy. When one fails, another one can take over. See [“Reliability and Fault Tolerance”](ch02.html#sec_introduction_reliability).
|
||||
|
||||
- Scalability
|
||||
|
||||
If your data volume or computing requirements grow bigger than a single machine can handle, you can potentially spread the load across multiple machines. See [“Scalability”](ch02.html#sec_introduction_scalability).
|
||||
|
||||
- Latency
|
||||
|
||||
If you have users around the world, you might want to have servers at various locations worldwide so that each user can be served from a datacenter that is geographically close to them. That avoids the users having to wait for network packets to travel halfway around the world to answer their requests. See [“Describing Performance”](ch02.html#sec_introduction_percentiles).
|
||||
|
||||
- Elasticity
|
||||
|
||||
If your application is busy at some times and idle at other times, a cloud deployment can scale up or down to meet the demand, so that you pay only for resources you are actively using. This more difficult on a single machine, which needs to be provisioned to handle the maximum load, even at times when it is barely used.
|
||||
|
||||
- Using specialized hardware
|
||||
|
||||
Different parts of the system can take advantage of different types of hardware to match their workload. For example, an object store may use machines with many disks but few CPUs, whereas a data analysis system may use machines with lots of CPU and memory but no disks, and a machine learning system may use machines with GPUs (which are much more efficient than CPUs for training deep neural networks and other machine learning tasks).
|
||||
|
||||
- Legal compliance
|
||||
|
||||
Some countries have data residency laws that require data about people in their jurisdiction to be stored and processed geographically within that country [[37](ch01.html#Korolov2022)]. The scope of these rules varies—for example, in some cases it applies only to medical or financial data, while other cases are broader. A service with users in several such jurisdictions will therefore have to distribute their data across servers in several locations.
|
||||
|
||||
These reasons apply both to services that you write yourself (application code) and services consisting of off-the-shelf software (such as databases).
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
### 分布式系统的问题
|
||||
|
||||
分布式系统也有其不利之处。通过网络传输的每个请求和API调用都需要处理可能发生的故障:网络可能中断,服务可能过载或崩溃,因此任何请求都可能在未收到响应的情况下超时。在这种情况下,我们不知道服务是否收到了请求,简单地重试可能不安全。我们将在[链接待补充]中详细讨论这些问题。
|
||||
|
||||
尽管数据中心网络速度很快,但调用另一个服务的速度仍然比在同一进程中调用函数要慢得多 [38]。在处理大量数据时,与其将数据从存储传输到另一台处理它的机器,不如将计算带到已经拥有数据的机器上,这样可能更快 [39]。更多的节点并不总是更快:在某些情况下,一台计算机上的简单单线程程序可能比拥有超过100个CPU核心的集群表现得更好 [40]。
|
||||
|
||||
调试分布式系统通常很困难:如果系统响应缓慢,您如何确定问题所在?在可观测性的标题下开发了分布式系统问题诊断技术 [41, 42],这涉及收集关于系统执行的数据,并允许以可以分析高级指标和个别事件的方式查询这些数据。追踪工具如OpenTelemetry允许您跟踪哪个客户端为哪个操作调用了哪个服务器,以及每个调用花费了多长时间 [43]。
|
||||
|
||||
数据库提供了各种机制来确保数据一致性,我们将在[链接待补充]和[链接待补充]中看到。然而,当每个服务都有自己的数据库时,跨这些不同服务维护数据一致性成为应用程序的问题。我们将在[链接待补充]中探讨的分布式事务是确保一致性的一种可能技术,但它们在微服务环境中很少使用,因为它们与使服务相互独立的目标相悖 [44]。
|
||||
|
||||
基于所有这些原因,如果您可以在单台机器上完成某项任务,这通常比建立分布式系统简单得多 [21]。CPU、内存和硬盘已变得更大、更快和更可靠。结合单节点数据库,如DuckDB、SQLite和KùzuDB,现在许多工作负载都可以在单个节点上运行。我们将在[链接待补充]中进一步探讨这个话题。
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
### 微服务与无服务
|
||||
|
||||
分布式系统通常将系统分布在多台机器上,最常见的方式是将它们分为客户端和服务器,并让客户端向服务器发出请求。如我们将在[链接待补充]中讨论的,这种通信最常使用HTTP。同一个过程可能既是服务器(处理传入请求)也是客户端(向其他服务发出传出请求)。
|
||||
|
||||
这种构建应用程序的方式传统上被称为*面向服务的架构*(SOA);最近这个想法被细化为*微服务*架构 [[45](ch01.html#Newman2021_ch1), [46](ch01.html#Richardson2014)]。在这种架构中,每个服务都有一个明确定义的目的(例如,在S3的情况下,这将是文件存储);每个服务都暴露一个可以通过网络由客户端调用的API,并且每个服务都有一个负责其维护的团队。因此,一个复杂的应用程序可以被分解为多个互动的服务,每个服务由一个单独的团队管理。
|
||||
|
||||
将复杂的软件分解为多个服务有几个优点:每个服务都可以独立更新,减少团队间的协调工作;每个服务可以被分配其所需的硬件资源;通过在API后面隐藏实现细节,服务所有者可以自由更改实现,而不影响客户端。在数据存储方面,通常每个服务都有自己的数据库,并且服务之间不共享数据库:共享数据库将有效地使整个数据库结构成为服务API的一部分,然后更改该结构将会很困难。共享的数据库还可能导致一个服务的查询负面影响其他服务的性能。
|
||||
|
||||
另一方面,拥有许多服务本身可能产生复杂性:每个服务都需要基础设施来部署新版本,调整分配的硬件资源以匹配负载,收集日志,监控服务健康,并在出现问题时通知值班工程师。*编排*框架如Kubernetes已成为部署服务的流行方式,因为它们为这些基础设施提供了基础。在开发过程中测试服务可能很复杂,因为您还需要运行它所依赖的所有其他服务。
|
||||
|
||||
微服务API的演进可能具有挑战性。调用API的客户端希望API具有某些字段。开发人员可能希望根据业务需求的变化添加或删除API中的字段,但这样做可能导致客户端失败。更糟糕的是,这种失败通常直到开发周期后期,当更新的服务API部署到暂存或生产环境时才被发现。API描述标准如OpenAPI和gRPC有助于管理客户端和服务器API之间的关系;我们将在[链接待补充]中进一步讨论这些内容。
|
||||
|
||||
微服务主要是对人的问题的技术解决方案:允许不同团队独立进展,无需彼此协调。这在大公司中很有价值,但在小公司中,如果没有许多团队,使用微服务可能是不必要的开销,更倾向于以最简单的方式实现应用程序 [[45](ch01.html#Newman2021_ch1)]。
|
||||
|
||||
*无服务器*,或*功能即服务*(FaaS),是部署服务的另一种方法,其中基础设施的管理被外包给云供应商 [[29](ch01.html#Jonas2019)]。使用虚拟机时,您必须明确选择何时启动或关闭实例;相比之下,在无服务器模型中,云提供商根据对您服务的传入请求,自动分配和释放硬件资源 [[47](ch01.html#Shahrad2020)]。“无服务器”的术语可能会产生误导:每个无服务器功能执行仍然在服务器上运行,但后续执行可能在不同的服务器上进行。
|
||||
|
||||
就像云存储用计量计费模式取代了容量规划(提前决定购买多少硬盘)一样,无服务器方法正在将计量计费带到代码执行:您只需为应用程序代码实际运行的时间付费,而不必提前预配资源。
|
||||
|
||||
The most common way of distributing a system across multiple machines is to divide them into clients and servers, and let the clients make requests to the servers. Most commonly HTTP is used for this communication, as we will discuss in [Link to Come]. The same process may be both a server (handling incoming requests) and a client (making outbound requests to other services).
|
||||
|
||||
This way of building applications has traditionally been called a *service-oriented architecture* (SOA); more recently the idea has been refined into a *microservices* architecture [[45](ch01.html#Newman2021_ch1), [46](ch01.html#Richardson2014)]. In this architecture, a service has one well-defined purpose (for example, in the case of S3, this would be file storage); each service exposes an API that can be called by clients via the network, and each service has one team that is responsible for its maintenance. A complex application can thus be decomposed into multiple interacting services, each managed by a separate team.
|
||||
|
||||
There are several advantages to breaking down a complex piece of software into multiple services: each service can be updated independently, reducing coordination effort among teams; each service can be assigned the hardware resources it needs; and by hiding the implementation details behind an API, the service owners are free to change the implementation without affecting clients. In terms of data storage, it is common for each service to have its own databases, and not to share databases between services: sharing a database would effectively make the entire database structure a part of the service’s API, and then that structure would be difficult to change. Shared databases could also cause one service’s queries to negatively impact the performance of other services.
|
||||
|
||||
On the other hand, having many services can itself breed complexity: each service requires infrastructure for deploying new releases, adjusting the allocated hardware resources to match the load, collecting logs, monitoring service health, and alerting an on-call engineer in the case of a problem. *Orchestration* frameworks such as Kubernetes have become a popular way of deploying services, since they provide a foundation for this infrastructure. Testing a service during development can be complicated, since you also need to run all the other services that it depends on.
|
||||
|
||||
Microservice APIs can be challenging to evolve. Clients that call an API expect the API to have certain fields. Developers might wish to add or remove fields to an API as business needs change, but doing so can cause clients to fail. Worse still, such failures are often not discovered until late in the development cycle when the updated service API is deployed to a staging or production environment. API description standards such as OpenAPI and gRPC help manage the relationship between client and server APIs; we discuss these further in [Link to Come].
|
||||
|
||||
Microservices are primarily a technical solution to a people problem: allowing different teams to make progress independently without having to coordinate with each other. This is valuable in a large company, but in a small company where there are not many teams, using microservices is likely to be unnecessary overhead, and it is preferable to implement the application in the simplest way possible [[45](ch01.html#Newman2021_ch1)].
|
||||
|
||||
*Serverless*, or *function-as-a-service* (FaaS), is another approach to deploying services, in which the management of the infrastructure is outsourced to a cloud vendor [[29](ch01.html#Jonas2019)]. When using virtual machines, you have to explicitly choose when to start up or shut down an instance; in contrast, with the serverless model, the cloud provider automatically allocates and frees hardware resources as needed, based on the incoming requests to your service [[47](ch01.html#Shahrad2020)]. The term “serverless” can misleading: each serverless function execution still runs on a server, but subsequent executions might run on a different one.
|
||||
|
||||
Just like cloud storage replaced capacity planning (deciding in advance how many disks to buy) with a metered billing model, the serverless approach is bringing metered billing to code execution: you only pay for the time that your application code is actually running, rather than having to provision resources in advance.
|
||||
|
||||
|
||||
--------
|
||||
|
||||
### 云计算与超算
|
||||
|
||||
云计算并非构建大规模计算系统的唯一方式;另一种选择是*高性能计算*(HPC),也称为*超级计算*。虽然有一些重叠,但HPC通常有不同的优先级并采用与云计算和企业数据中心系统不同的技术。其中一些差异包括:
|
||||
|
||||
- 超级计算机通常用于计算密集型的科学计算任务,如天气预报、分子动力学(模拟原子和分子的运动)、复杂的优化问题和求解偏微分方程。另一方面,云计算倾向于用于在线服务、商业数据系统和需要高可用性服务用户请求的类似系统。
|
||||
- 超级计算机通常运行大型批处理作业,这些作业会不时地将计算状态检查点保存到磁盘。如果节点失败,一个常见的解决方案是简单地停止整个集群工作,修复故障节点,然后从最后一个检查点重新开始计算 [[48](ch01.html#Barroso2018), [49](ch01.html#Fiala2012)]。在云服务中,通常不希望停止整个集群,因为服务需要持续地以最小的中断服务于用户。
|
||||
- 超级计算机通常由专用硬件构建,每个节点都相当可靠。云服务中的节点通常由商品机构建,这些商品机由于规模经济可以以较低成本提供等效性能,但也具有更高的故障率(见[“硬件和软件故障”](ch02.html#sec_introduction_hardware_faults))。
|
||||
- 超级计算机节点通常通过共享内存和远程直接内存访问(RDMA)进行通信,这支持高带宽和低延迟,但假设系统用户之间有高度的信任 [[50](ch01.html#KornfeldSimpson2020)]。在云计算中,网络和机器经常由互不信任的组织共享,需要更强的安全机制,如资源隔离(例如,虚拟机)、加密和认证。
|
||||
- 云数据中心网络通常基于IP和以太网,按Clos拓扑排列,以提供高切面带宽——这是衡量网络整体性能的常用指标 [[48](ch01.html#Barroso2018), [51](ch01.html#Singh2015)]。超级计算机通常使用专用的网络拓扑,如多维网格和环面 [[52](ch01.html#Lockwood2014)],这为具有已知通信模式的HPC工作负载提供了更好的性能。
|
||||
- 云计算允许节点分布在多个地理位置,而超级计算机通常假设其所有节点都靠近在一起。
|
||||
|
||||
大规模分析系统有时与超级计算共享一些特征,这就是为什么如果您在这一领域工作,了解这些技术可能是值得的。然而,本书主要关注需要持续可用的服务,如[“可靠性和容错”](ch02.html#sec_introduction_reliability)中所讨论的。
|
||||
|
||||
Cloud computing is not the only way of building large-scale computing systems; an alternative is *high-performance computing* (HPC), also known as *supercomputing*. Although there are overlaps, HPC often has different priorities and uses different techniques compared to cloud computing and enterprise datacenter systems. Some of those differences are:
|
||||
|
||||
- Supercomputers are typically used for computationally intensive scientific computing tasks, such as weather forecasting, molecular dynamics (simulating the movement of atoms and molecules), complex optimization problems, and solving partial differential equations. On the other hand, cloud computing tends to be used for online services, business data systems, and similar systems that need to serve user requests with high availability.
|
||||
- A supercomputer typically runs large batch jobs that checkpoint the state of their computation to disk from time to time. If a node fails, a common solution is to simply stop the entire cluster workload, repair the faulty node, and then restart the computation from the last checkpoint [[48](ch01.html#Barroso2018), [49](ch01.html#Fiala2012)]. With cloud services, it is usually not desirable to stop the entire cluster, since the services need to continually serve users with minimal interruptions.
|
||||
- Supercomputers are typically built from specialized hardware, where each node is quite reliable. Nodes in cloud services are usually built from commodity machines, which can provide equivalent performance at lower cost due to economies of scale, but which also have higher failure rates (see [“Hardware and Software Faults”](ch02.html#sec_introduction_hardware_faults)).
|
||||
- Supercomputer nodes typically communicate through shared memory and remote direct memory access (RDMA), which support high bandwidth and low latency, but assume a high level of trust among the users of the system [[50](ch01.html#KornfeldSimpson2020)]. In cloud computing, the network and the machines are often shared by mutually untrusting organizations, requiring stronger security mechanisms such as resource isolation (e.g., virtual machines), encryption and authentication.
|
||||
- Cloud datacenter networks are often based on IP and Ethernet, arranged in Clos topologies to provide high bisection bandwidth—a commonly used measure of a network’s overall performance [[48](ch01.html#Barroso2018), [51](ch01.html#Singh2015)]. Supercomputers often use specialized network topologies, such as multi-dimensional meshes and toruses [[52](ch01.html#Lockwood2014)], which yield better performance for HPC workloads with known communication patterns.
|
||||
- Cloud computing allows nodes to be distributed across multiple geographic locations, whereas supercomputers generally assume that all of their nodes are close together.
|
||||
|
||||
Large-scale analytics systems sometimes share some characteristics with supercomputing, which is why it can be worth knowing about these techniques if you are working in this area. However, this book is mostly concerned with services that need to be continually available, as discussed in [“Reliability and Fault Tolerance”](ch02.html#sec_introduction_reliability).
|
||||
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 数据系统,法律与社会
|
||||
|
||||
到目前为止,您已经看到本章中数据系统的架构不仅受到技术目标和需求的影响,还受到它们支持的组织的人类需求的影响。越来越多的数据系统工程师意识到,仅仅满足自己业务的需求是不够的:我们还对整个社会负有责任。
|
||||
|
||||
特别需要关注的是存储关于人们及其行为的数据的系统。自2018年以来,*通用数据保护条例*(GDPR)为许多欧洲国家的居民提供了更大的控制权和法律权利,用以管理他们的个人数据,类似的隐私法规也在世界各地的不同国家和地区得到采纳,例如加利福尼亚消费者隐私法案(CCPA)。围绕人工智能的法规,如*欧盟人工智能法案*,对个人数据的使用施加了进一步的限制。
|
||||
|
||||
此外,即使在不直接受法规约束的领域,也越来越多地认识到计算机系统对人和社会的影响。社交媒体改变了个人获取新闻的方式,这影响了他们的政治观点,从而可能影响选举结果。自动化系统越来越多地做出对个人有深远影响的决定,例如决定谁应获得贷款或保险,谁应被邀请参加工作面试,或者谁应被怀疑犯有罪行 [[53](ch01.html#ONeil2016_ch1)]。
|
||||
|
||||
从事这些系统的每个人都负有考虑其伦理影响并确保遵守相关法律的责任。并不是每个人都必须成为法律和伦理的专家,但基本的法律和伦理原则意识与分布式系统的一些基础知识同样重要。
|
||||
|
||||
法律考量正在影响数据系统设计的基础 [[54](ch01.html#Shastri2020)]。例如,GDPR授予个人在请求时删除其数据的权利(有时称为*被遗忘权*)。然而,正如我们在本书中将看到的,许多数据系统依赖于不可变构造,如作为设计一部分的仅追加日志;我们如何确保在一个本应不可变的文件中删除某些数据?我们如何处理已并入派生数据集的数据的删除问题(见[“记录系统与派生数据”](ch01.html#sec_introduction_derived)),如机器学习模型的训练数据?回答这些问题创造了新的工程挑战。
|
||||
|
||||
目前我们没有明确的指南来判断哪些特定技术或系统架构应被视为“符合GDPR”的。法规故意没有规定特定的技术,因为这些可能随着技术的进步而迅速变化。相反,法律文本提出了需要解释的高级原则。这意味着关于如何遵守隐私法规的问题没有简单的答案,但我们将通过这个视角审视本书中的一些技术。
|
||||
|
||||
一般来说,我们存储数据是因为我们认为其价值大于存储它的成本。然而,值得记住的是,存储成本不仅仅是您为亚马逊 S3 或其他服务支付的账单:成本效益计算还应考虑数据泄露或被敌对方妥协时的责任和声誉损害风险,以及如果数据的存储和处理被发现不符合法律的风险,还有法律费用和罚款的风险。
|
||||
|
||||
政府或警察部门也可能强制公司交出数据。当存在数据可能揭示被刑事化行为的风险时(例如,在几个中东和非洲国家的同性恋行为,或在几个美国州寻求堕胎),存储该数据为用户创造了真正的安全风险。例如,通过位置数据很容易揭露到堕胎诊所的旅行,甚至可能通过一段时间内用户 IP 地址的日志(表明大致位置)揭露。
|
||||
|
||||
一旦考虑到所有风险,可能会合理地决定某些数据根本不值得存储,因此应该将其删除。*数据最小化*原则(有时称为德语术语*Datensparsamkeit*)与存储大量数据的“大数据”哲学相悖,以防它在未来证明有用 [[55](ch01.html#Datensparsamkeit)]。但这与 GDPR 相符,后者规定只能为特定的、明确的目的收集个人数据,这些数据以后不能用于任何其他目的,且为了收集目的,保存的数据不得超过必要的时间 [[56](ch01.html#GDPR)]。
|
||||
|
||||
企业也注意到了隐私和安全问题。信用卡公司要求支付处理业务遵守严格的支付卡行业(PCI)标准。处理者经常接受独立审计师的评估,以验证持续合规。软件供应商也看到了增加的审查。现在许多买家要求其供应商符合服务组织控制(SOC)类型 2 标准。与 PCI 合规一样,供应商接受第三方审计以验证遵守情况。
|
||||
|
||||
总的来说,平衡您的业务需求与您收集和处理的数据的人的需求很重要。这个话题还有更多内容;在[链接待补充]中,我们将更深入地探讨伦理和法律合规问题,包括偏见和歧视的问题。
|
||||
|
||||
|
||||
So far you’ve seen in this chapter that the architecture of data systems is influenced not only by technical goals and requirements, but also by the human needs of the organizations that they support. Increasingly, data systems engineers are realizing that serving the needs of their own business is not enough: we also have a responsibility towards society at large.
|
||||
|
||||
One particular concern are systems that store data about people and their behavior. Since 2018 the *General Data Protection Regulation* (GDPR) has given residents of many European countries greater control and legal rights over their personal data, and similar privacy regulation has been adopted in various other countries and states around the world, including for example the California Consumer Privacy Act (CCPA). Regulations around AI, such as the *EU AI Act*, place further restrictions on how personal data can be used.
|
||||
|
||||
Moreover, even in areas that are not directly subject to regulation, there is increasing recognition of the effects that computer systems have on people and society. Social media has changed how individuals consume news, which influences their political opinions and hence may affect the outcome of elections. Automated systems increasingly make decisions that have profound consequences for individuals, such as deciding who should be given a loan or insurance coverage, who should be invited to a job interview, or who should be suspected of a crime [[53](ch01.html#ONeil2016_ch1)].
|
||||
|
||||
Everyone who works on such systems shares a responsibility for considering the ethical impact and ensuring that they comply with relevant law. It is not necessary for everybody to become an expert in law and ethics, but a basic awareness of legal and ethical principles is just as important as, say, some foundational knowledge in distributed systems.
|
||||
|
||||
Legal considerations are influencing the very foundations of how data systems are being designed [[54](ch01.html#Shastri2020)]. For example, the GDPR grants individuals the right to have their data erased on request (sometimes known as the *right to be forgotten*). However, as we shall see in this book, many data systems rely on immutable constructs such as append-only logs as part of their design; how can we ensure deletion of some data in the middle of a file that is supposed to be immutable? How do we handle deletion of data that has been incorporated into derived datasets (see [“Systems of Record and Derived Data”](ch01.html#sec_introduction_derived)), such as training data for machine learning models? Answering these questions creates new engineering challenges.
|
||||
|
||||
At present we don’t have clear guidelines on which particular technologies or system architectures should be considered “GDPR-compliant” or not. The regulation deliberately does not mandate particular technologies, because these may quickly change as technology progresses. Instead, the legal texts set out high-level principles that are subject to interpretation. This means that there are no simple answers to the question of how to comply with privacy regulation, but we will look at some of the technologies in this book through this lens.
|
||||
|
||||
In general, we store data because we think that its value is greater than the costs of storing it. However, it is worth remembering that the costs of storage are not just the bill you pay for Amazon S3 or another service: the cost-benefit calculation should also take into account the risks of liability and reputational damage if the data were to be leaked or compromised by adversaries, and the risk of legal costs and fines if the storage and processing of the data is found not to be compliant with the law.
|
||||
|
||||
Governments or police forces might also compel companies to hand over data. When there is a risk that the data may reveal criminalized behaviors (for example, homosexuality in several Middle Eastern and African countries, or seeking an abortion in several US states), storing that data creates real safety risks for users. Travel to an abortion clinic, for example, could easily be revealed by location data, perhaps even by a log of the user’s IP addresses over time (which indicate approximate location).
|
||||
|
||||
Once all the risks are taken into account, it might be reasonable to decide that some data is simply not worth storing, and that it should therefore be deleted. This principle of *data minimization* (sometimes known by the German term *Datensparsamkeit*) runs counter to the “big data” philosophy of storing lots of data speculatively in case it turns out to be useful in the future [[55](ch01.html#Datensparsamkeit)]. But it fits with the GDPR, which mandates that personal data many only be collected for a specified, explicit purpose, that this data may not later be used for any other purpose, and that the data must not be kept for longer than necessary for the purposes for which it was collected [[56](ch01.html#GDPR)].
|
||||
|
||||
Businesses have also taken notice of privacy and safety concerns. Credit card companies require payment processing businesses to adhere to strict payment card industry (PCI) standards. Processors undergo frequent evaluations from independent auditors to verify continued compliance. Software vendors have also seen increased scrutiny. Many buyers now require their vendors to comply with Service Organization Control (SOC) Type 2 standards. As with PCI compliance, vendors undergo third party audits to verify adherence.
|
||||
|
||||
Generally, it is important to balance the needs of your business against the needs of the people whose data you are collecting and processing. There is much more to this topic; in [Link to Come] we will go deeper into the topics of ethics and legal compliance, including the problems of bias and discrimination.
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 本章小结
|
||||
|
||||
本章的主题是理解权衡:即,认识到对于许多问题并没有唯一的正确答案,而是有几种不同的方法,每种方法都有各自的优缺点。我们探讨了影响数据系统架构的一些重要选择,并介绍了在本书余下部分将需要用到的术语。
|
||||
|
||||
我们首先区分了操作型(事务处理,OLTP)和分析型(OLAP)系统,并看到了它们的不同特点:不仅管理不同类型的数据,访问模式也不同,而且服务于不同的受众。我们遇到了数据仓库和数据湖的概念,这些系统通过 ETL 从业务系统接收数据。在[链接待补充]中,我们将看到,由于需要服务的查询类型不同,操作型和分析型系统通常使用非常不同的内部数据布局。
|
||||
|
||||
然后,我们比较了云服务(一种相对较新的发展)和之前主导数据系统架构的传统自托管软件范式。这两种方法哪种更具成本效益很大程度上取决于您的具体情况,但不可否认的是,云原生方法正在改变数据系统的架构方式,例如它们如何分离存储和计算。
|
||||
|
||||
云系统本质上是分布式的,我们简要考察了与使用单一机器相比,分布式系统的一些权衡。在某些情况下,您无法避免采用分布式,但如果有可能保持在单一机器上,建议不要急于使系统分布式化。在[链接待补充]和[链接待补充]中,我们将更详细地介绍分布式系统的挑战。
|
||||
|
||||
最后,我们看到,数据系统架构不仅由部署系统的业务需求决定,还由保护被处理数据人员权利的隐私法规决定——这是许多工程师容易忽视的一个方面。如何将法律要求转化为技术实现尚未被充分理解,但在我们翻阅本书的其余部分时,保持对这个问题的关注是很重要的。
|
||||
|
||||
The theme of this chapter has been to understand trade-offs: that is, to recognize that for many questions there is not one right answer, but several different approaches that each have various pros and cons. We explored some of the most important choices that affect the architecture of data systems, and introduced terminology that will be needed throughout the rest of this book.
|
||||
|
||||
We started by making a distinction between operational (transaction-processing, OLTP) and analytical (OLAP) systems, and saw their different characteristics: not only managing different types of data with different access patterns, but also serving different audiences. We encountered the concept of a data warehouse and data lake, which receive data feeds from operational systems via ETL. In [Link to Come] we will see that operational and analytical systems often use very different internal data layouts because of the different types of queries they need to serve.
|
||||
|
||||
We then compared cloud services, a comparatively recent development, to the traditional paradigm of self-hosted software that has previously dominated data systems architecture. Which of these approaches is more cost-effective depends a lot on your particular situation, but it’s undeniable that cloud-native approaches are bringing big changes to the way data systems are architected, for example in the way they separate storage and compute.
|
||||
|
||||
Cloud systems are intrinsically distributed, and we briefly examined some of the trade-offs of distributed systems compared to using a single machine. There are situations in which you can’t avoid going distributed, but it’s advisable not to rush into making a system distributed if it’s possible to keep it on a single machine. In [Link to Come] and [Link to Come] we will cover the challenges with distributed systems in more detail.
|
||||
|
||||
Finally, we saw that data systems architecture is determined not only by the needs of the business deploying the system, but also by privacy regulation that protects the rights of the people whose data is being processed—an aspect that many engineers are prone to ignoring. How we translate legal requirements into technical implementations is not yet well understood, but it’s important to keep this question in mind as we move through the rest of this book.
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 参考文献
|
||||
|
||||
[[1](ch01.html#Kouzes2009-marker)] Richard T. Kouzes, Gordon A. Anderson, Stephen T. Elbert, Ian Gorton, and Deborah K. Gracio. [The Changing Paradigm of Data-Intensive Computing](http://www2.ic.uff.br/~boeres/slides_AP/papers/TheChanginParadigmDataIntensiveComputing_2009.pdf). *IEEE Computer*, volume 42, issue 1, January 2009. [doi:10.1109/MC.2009.26](https://doi.org/10.1109/MC.2009.26)
|
||||
|
||||
[[2](ch01.html#Kleppmann2019-marker)] Martin Kleppmann, Adam Wiggins, Peter van Hardenberg, and Mark McGranaghan. [Local-first software: you own your data, in spite of the cloud](https://www.inkandswitch.com/local-first/). At *2019 ACM SIGPLAN International Symposium on New Ideas, New Paradigms, and Reflections on Programming and Software* (Onward!), October 2019. [doi:10.1145/3359591.3359737](https://doi.org/10.1145/3359591.3359737)
|
||||
|
||||
[[3](ch01.html#Reis2022-marker)] Joe Reis and Matt Housley. [*Fundamentals of Data Engineering*](https://www.oreilly.com/library/view/fundamentals-of-data/9781098108298/). O’Reilly Media, 2022. ISBN: 9781098108304
|
||||
|
||||
[[4](ch01.html#Machado2023-marker)] Rui Pedro Machado and Helder Russa. [*Analytics Engineering with SQL and dbt*](https://www.oreilly.com/library/view/analytics-engineering-with/9781098142377/). O’Reilly Media, 2023. ISBN: 9781098142384
|
||||
|
||||
[[5](ch01.html#Codd1993-marker)] Edgar F. Codd, S. B. Codd, and C. T. Salley. [Providing OLAP to User-Analysts: An IT Mandate](http://www.estgv.ipv.pt/PaginasPessoais/jloureiro/ESI_AID2007_2008/fichas/codd.pdf). E. F. Codd Associates, 1993. Archived at [perma.cc/RKX8-2GEE](https://perma.cc/RKX8-2GEE)
|
||||
|
||||
[[6](ch01.html#Chaudhuri1997-marker)] Surajit Chaudhuri and Umeshwar Dayal. [An Overview of Data Warehousing and OLAP Technology](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/sigrecord.pdf). *ACM SIGMOD Record*, volume 26, issue 1, pages 65–74, March 1997. [doi:10.1145/248603.248616](https://doi.org/10.1145/248603.248616)
|
||||
|
||||
[[7](ch01.html#Ozcan2017-marker)] Fatma Özcan, Yuanyuan Tian, and Pinar Tözün. [Hybrid Transactional/Analytical Processing: A Survey](https://humming80.github.io/papers/sigmod-htaptut.pdf). At *ACM International Conference on Management of Data* (SIGMOD), May 2017. [doi:10.1145/3035918.3054784](https://doi.org/10.1145/3035918.3054784)
|
||||
|
||||
[[8](ch01.html#Prout2022-marker)] Adam Prout, Szu-Po Wang, Joseph Victor, Zhou Sun, Yongzhu Li, Jack Chen, Evan Bergeron, Eric Hanson, Robert Walzer, Rodrigo Gomes, and Nikita Shamgunov. [Cloud-Native Transactions and Analytics in SingleStore](https://dl.acm.org/doi/abs/10.1145/3514221.3526055). At *International Conference on Management of Data* (SIGMOD), June 2022. [doi:10.1145/3514221.3526055](https://doi.org/10.1145/3514221.3526055)
|
||||
|
||||
[[9](ch01.html#Stonebraker2005fitsall-marker)] Michael Stonebraker and Uğur Çetintemel. [‘One Size Fits All’: An Idea Whose Time Has Come and Gone](https://pages.cs.wisc.edu/~shivaram/cs744-readings/fits_all.pdf). At *21st International Conference on Data Engineering* (ICDE), April 2005. [doi:10.1109/ICDE.2005.1](https://doi.org/10.1109/ICDE.2005.1)
|
||||
|
||||
[[10](ch01.html#Cohen2009-marker)] Jeffrey Cohen, Brian Dolan, Mark Dunlap, Joseph M Hellerstein, and Caleb Welton. [MAD Skills: New Analysis Practices for Big Data](http://www.vldb.org/pvldb/vol2/vldb09-219.pdf). *Proceedings of the VLDB Endowment*, volume 2, issue 2, pages 1481–1492, August 2009. [doi:10.14778/1687553.1687576](https://doi.org/10.14778/1687553.1687576)
|
||||
|
||||
[[11](ch01.html#Olteanu2020-marker)] Dan Olteanu. [The Relational Data Borg is Learning](http://www.vldb.org/pvldb/vol13/p3502-olteanu.pdf). *Proceedings of the VLDB Endowment*, volume 13, issue 12, August 2020. [doi:10.14778/3415478.3415572](https://doi.org/10.14778/3415478.3415572)
|
||||
|
||||
[[12](ch01.html#Bornstein2020-marker)] Matt Bornstein, Martin Casado, and Jennifer Li. [Emerging Architectures for Modern Data Infrastructure: 2020](https://future.a16z.com/emerging-architectures-for-modern-data-infrastructure-2020/). *future.a16z.com*, October 2020. Archived at [perma.cc/LF8W-KDCC](https://perma.cc/LF8W-KDCC)
|
||||
|
||||
[[13](ch01.html#Fowler2015-marker)] Martin Fowler. [DataLake](https://www.martinfowler.com/bliki/DataLake.html). *martinfowler.com*, February 2015. Archived at [perma.cc/4WKN-CZUK](https://perma.cc/4WKN-CZUK)
|
||||
|
||||
[[14](ch01.html#Johnson2015-marker)] Bobby Johnson and Joseph Adler. [The Sushi Principle: Raw Data Is Better](https://learning.oreilly.com/videos/strata-hadoop/9781491924143/9781491924143-video210840/). At *Strata+Hadoop World*, February 2015.
|
||||
|
||||
[[15](ch01.html#Armbrust2021-marker)] Michael Armbrust, Ali Ghodsi, Reynold Xin, and Matei Zaharia. [Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics](https://www.cidrdb.org/cidr2021/papers/cidr2021_paper17.pdf). At *11th Annual Conference on Innovative Data Systems Research* (CIDR), January 2021.
|
||||
|
||||
[[16](ch01.html#DataOps-marker)] DataKitchen, Inc. [The DataOps Manifesto](https://dataopsmanifesto.org/en/). *dataopsmanifesto.org*, 2017. Archived at [perma.cc/3F5N-FUQ4](https://perma.cc/3F5N-FUQ4)
|
||||
|
||||
[[17](ch01.html#Manohar2021-marker)] Tejas Manohar. [What is Reverse ETL: A Definition & Why It’s Taking Off](https://hightouch.io/blog/reverse-etl/). *hightouch.io*, November 2021. Archived at [perma.cc/A7TN-GLYJ](https://perma.cc/A7TN-GLYJ)
|
||||
|
||||
[[18](ch01.html#ORegan2018-marker)] Simon O’Regan. [Designing Data Products](https://towardsdatascience.com/designing-data-products-b6b93edf3d23). *towardsdatascience.com*, August 2018. Archived at [perma.cc/HU67-3RV8](https://perma.cc/HU67-3RV8)
|
||||
|
||||
[[19](ch01.html#Fournier2021-marker)] Camille Fournier. [Why is it so hard to decide to buy?](https://skamille.medium.com/why-is-it-so-hard-to-decide-to-buy-d86fee98e88e) *skamille.medium.com*, July 2021. Archived at [perma.cc/6VSG-HQ5X](https://perma.cc/6VSG-HQ5X)
|
||||
|
||||
[[20](ch01.html#HeinemeierHansson2022-marker)] David Heinemeier Hansson. [Why we’re leaving the cloud](https://world.hey.com/dhh/why-we-re-leaving-the-cloud-654b47e0). *world.hey.com*, October 2022. Archived at [perma.cc/82E6-UJ65](https://perma.cc/82E6-UJ65)
|
||||
|
||||
[[21](ch01.html#Badizadegan2022-marker)] Nima Badizadegan. [Use One Big Server](https://specbranch.com/posts/one-big-server/). *specbranch.com*, August 2022. Archived at [perma.cc/M8NB-95UK](https://perma.cc/M8NB-95UK)
|
||||
|
||||
[[22](ch01.html#Yegge2020-marker)] Steve Yegge. [Dear Google Cloud: Your Deprecation Policy is Killing You](https://steve-yegge.medium.com/dear-google-cloud-your-deprecation-policy-is-killing-you-ee7525dc05dc). *steve-yegge.medium.com*, August 2020. Archived at [perma.cc/KQP9-SPGU](https://perma.cc/KQP9-SPGU)
|
||||
|
||||
[[23](ch01.html#Verbitski2017-marker)] Alexandre Verbitski, Anurag Gupta, Debanjan Saha, Murali Brahmadesam, Kamal Gupta, Raman Mittal, Sailesh Krishnamurthy, Sandor Maurice, Tengiz Kharatishvili, and Xiaofeng Bao. [Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases](https://media.amazonwebservices.com/blog/2017/aurora-design-considerations-paper.pdf). At *ACM International Conference on Management of Data* (SIGMOD), pages 1041–1052, May 2017. [doi:10.1145/3035918.3056101](https://doi.org/10.1145/3035918.3056101)
|
||||
|
||||
[[24](ch01.html#Antonopoulos2019_ch1-marker)] Panagiotis Antonopoulos, Alex Budovski, Cristian Diaconu, Alejandro Hernandez Saenz, Jack Hu, Hanuma Kodavalla, Donald Kossmann, Sandeep Lingam, Umar Farooq Minhas, Naveen Prakash, Vijendra Purohit, Hugh Qu, Chaitanya Sreenivas Ravella, Krystyna Reisteter, Sheetal Shrotri, Dixin Tang, and Vikram Wakade. [Socrates: The New SQL Server in the Cloud](https://www.microsoft.com/en-us/research/uploads/prod/2019/05/socrates.pdf). At *ACM International Conference on Management of Data* (SIGMOD), pages 1743–1756, June 2019. [doi:10.1145/3299869.3314047](https://doi.org/10.1145/3299869.3314047)
|
||||
|
||||
[[25](ch01.html#Vuppalapati2020-marker)] Midhul Vuppalapati, Justin Miron, Rachit Agarwal, Dan Truong, Ashish Motivala, and Thierry Cruanes. [Building An Elastic Query Engine on Disaggregated Storage](https://www.usenix.org/system/files/nsdi20-paper-vuppalapati.pdf). At *17th USENIX Symposium on Networked Systems Design and Implementation* (NSDI), February 2020.
|
||||
|
||||
[[26](ch01.html#Shapira2023-marker)] Gwen Shapira. [Compute-Storage Separation Explained](https://www.thenile.dev/blog/storage-compute). *thenile.dev*, January 2023. Archived at [perma.cc/QCV3-XJNZ](https://perma.cc/QCV3-XJNZ)
|
||||
|
||||
[[27](ch01.html#Murthy2022-marker)] Ravi Murthy and Gurmeet Goindi. [AlloyDB for PostgreSQL under the hood: Intelligent, database-aware storage](https://cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storage). *cloud.google.com*, May 2022. Archived at [archive.org](https://web.archive.org/web/20220514021120/https://cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storage)
|
||||
|
||||
[[28](ch01.html#Vanlightly2023-marker)] Jack Vanlightly. [The Architecture of Serverless Data Systems](https://jack-vanlightly.com/blog/2023/11/14/the-architecture-of-serverless-data-systems). *jack-vanlightly.com*, November 2023. Archived at [perma.cc/UDV4-TNJ5](https://perma.cc/UDV4-TNJ5)
|
||||
|
||||
[[29](ch01.html#Jonas2019-marker)] Eric Jonas, Johann Schleier-Smith, Vikram Sreekanti, Chia-Che Tsai, Anurag Khandelwal, Qifan Pu, Vaishaal Shankar, Joao Carreira, Karl Krauth, Neeraja Yadwadkar, Joseph E Gonzalez, Raluca Ada Popa, Ion Stoica, David A Patterson. [Cloud Programming Simplified: A Berkeley View on Serverless Computing](https://arxiv.org/abs/1902.03383). *arxiv.org*, February 2019.
|
||||
|
||||
[[30](ch01.html#Beyer2016-marker)] Betsy Beyer, Jennifer Petoff, Chris Jones, and Niall Richard Murphy. [*Site Reliability Engineering: How Google Runs Production Systems*](https://www.oreilly.com/library/view/site-reliability-engineering/9781491929117/). O’Reilly Media, 2016. ISBN: 9781491929124
|
||||
|
||||
[[31](ch01.html#Limoncelli2020-marker)] Thomas Limoncelli. [The Time I Stole $10,000 from Bell Labs](https://queue.acm.org/detail.cfm?id=3434773). *ACM Queue*, volume 18, issue 5, November 2020. [doi:10.1145/3434571.3434773](https://doi.org/10.1145/3434571.3434773)
|
||||
|
||||
[[32](ch01.html#Majors2020-marker)] Charity Majors. [The Future of Ops Jobs](https://acloudguru.com/blog/engineering/the-future-of-ops-jobs). *acloudguru.com*, August 2020. Archived at [perma.cc/GRU2-CZG3](https://perma.cc/GRU2-CZG3)
|
||||
|
||||
[[33](ch01.html#Cherkasky2021-marker)] Boris Cherkasky. [(Over)Pay As You Go for Your Datastore](https://medium.com/riskified-technology/over-pay-as-you-go-for-your-datastore-11a29ae49a8b). *medium.com*, September 2021. Archived at [perma.cc/Q8TV-2AM2](https://perma.cc/Q8TV-2AM2)
|
||||
|
||||
[[34](ch01.html#Kushchi2023-marker)] Shlomi Kushchi. [Serverless Doesn’t Mean DevOpsLess or NoOps](https://thenewstack.io/serverless-doesnt-mean-devopsless-or-noops/). *thenewstack.io*, February 2023. Archived at [perma.cc/3NJR-AYYU](https://perma.cc/3NJR-AYYU)
|
||||
|
||||
[[35](ch01.html#Bernhardsson2021-marker)] Erik Bernhardsson. [Storm in the stratosphere: how the cloud will be reshuffled](https://erikbern.com/2021/11/30/storm-in-the-stratosphere-how-the-cloud-will-be-reshuffled.html). *erikbern.com*, November 2021. Archived at [perma.cc/SYB2-99P3](https://perma.cc/SYB2-99P3)
|
||||
|
||||
[[36](ch01.html#Stancil2021-marker)] Benn Stancil. [The data OS](https://benn.substack.com/p/the-data-os). *benn.substack.com*, September 2021. Archived at [perma.cc/WQ43-FHS6](https://perma.cc/WQ43-FHS6)
|
||||
|
||||
[[37](ch01.html#Korolov2022-marker)] Maria Korolov. [Data residency laws pushing companies toward residency as a service](https://www.csoonline.com/article/3647761/data-residency-laws-pushing-companies-toward-residency-as-a-service.html). *csoonline.com*, January 2022. Archived at [perma.cc/CHE4-XZZ2](https://perma.cc/CHE4-XZZ2)
|
||||
|
||||
[[38](ch01.html#Nath2019-marker)] Kousik Nath. [These are the numbers every computer engineer should know](https://www.freecodecamp.org/news/must-know-numbers-for-every-computer-engineer/). *freecodecamp.org*, September 2019. Archived at [perma.cc/RW73-36RL](https://perma.cc/RW73-36RL)
|
||||
|
||||
[[39](ch01.html#Hellerstein2019-marker)] Joseph M Hellerstein, Jose Faleiro, Joseph E Gonzalez, Johann Schleier-Smith, Vikram Sreekanti, Alexey Tumanov, and Chenggang Wu. [Serverless Computing: One Step Forward, Two Steps Back](https://arxiv.org/abs/1812.03651). At *Conference on Innovative Data Systems Research* (CIDR), January 2019.
|
||||
|
||||
[[40](ch01.html#McSherry2015_ch1-marker)] Frank McSherry, Michael Isard, and Derek G. Murray. [Scalability! But at What COST?](https://www.usenix.org/system/files/conference/hotos15/hotos15-paper-mcsherry.pdf) At *15th USENIX Workshop on Hot Topics in Operating Systems* (HotOS), May 2015.
|
||||
|
||||
[[41](ch01.html#Sridharan2018-marker)] Cindy Sridharan. *[Distributed Systems Observability: A Guide to Building Robust Systems](https://unlimited.humio.com/rs/756-LMY-106/images/Distributed-Systems-Observability-eBook.pdf)*. Report, O’Reilly Media, May 2018. Archived at [perma.cc/M6JL-XKCM](https://perma.cc/M6JL-XKCM)
|
||||
|
||||
[[42](ch01.html#Majors2019-marker)] Charity Majors. [Observability — A 3-Year Retrospective](https://thenewstack.io/observability-a-3-year-retrospective/). *thenewstack.io*, August 2019. Archived at [perma.cc/CG62-TJWL](https://perma.cc/CG62-TJWL)
|
||||
|
||||
[[43](ch01.html#Sigelman2010-marker)] Benjamin H. Sigelman, Luiz André Barroso, Mike Burrows, Pat Stephenson, Manoj Plakal, Donald Beaver, Saul Jaspan, and Chandan Shanbhag. [Dapper, a Large-Scale Distributed Systems Tracing Infrastructure](https://research.google/pubs/pub36356/). Google Technical Report dapper-2010-1, April 2010. Archived at [perma.cc/K7KU-2TMH](https://perma.cc/K7KU-2TMH)
|
||||
|
||||
[[44](ch01.html#Laigner2021-marker)] Rodrigo Laigner, Yongluan Zhou, Marcos Antonio Vaz Salles, Yijian Liu, and Marcos Kalinowski. [Data management in microservices: State of the practice, challenges, and research directions](http://www.vldb.org/pvldb/vol14/p3348-laigner.pdf). *Proceedings of the VLDB Endowment*, volume 14, issue 13, pages 3348–3361, September 2021. [doi:10.14778/3484224.3484232](https://doi.org/10.14778/3484224.3484232)
|
||||
|
||||
[[45](ch01.html#Newman2021_ch1-marker)] Sam Newman. [*Building Microservices*, second edition](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/). O’Reilly Media, 2021. ISBN: 9781492034025
|
||||
|
||||
[[46](ch01.html#Richardson2014-marker)] Chris Richardson. [Microservices: Decomposing Applications for Deployability and Scalability](http://www.infoq.com/articles/microservices-intro). *infoq.com*, May 2014. Archived at [perma.cc/CKN4-YEQ2](https://perma.cc/CKN4-YEQ2)
|
||||
|
||||
[[47](ch01.html#Shahrad2020-marker)] Mohammad Shahrad, Rodrigo Fonseca, Íñigo Goiri, Gohar Chaudhry, Paul Batum, Jason Cooke, Eduardo Laureano, Colby Tresness, Mark Russinovich, Ricardo Bianchini. [Serverless in the Wild: Characterizing and Optimizing the Serverless Workload at a Large Cloud Provider](https://www.usenix.org/system/files/atc20-shahrad.pdf). At *USENIX Annual Technical Conference* (ATC), July 2020.
|
||||
|
||||
[[48](ch01.html#Barroso2018-marker)] Luiz André Barroso, Urs Hölzle, and Parthasarathy Ranganathan. [The Datacenter as a Computer: Designing Warehouse-Scale Machines](https://www.morganclaypool.com/doi/10.2200/S00874ED3V01Y201809CAC046), third edition. Morgan & Claypool Synthesis Lectures on Computer Architecture, October 2018. [doi:10.2200/S00874ED3V01Y201809CAC046](https://doi.org/10.2200/S00874ED3V01Y201809CAC046)
|
||||
|
||||
[[49](ch01.html#Fiala2012-marker)] David Fiala, Frank Mueller, Christian Engelmann, Rolf Riesen, Kurt Ferreira, and Ron Brightwell. [Detection and Correction of Silent Data Corruption for Large-Scale High-Performance Computing](http://moss.csc.ncsu.edu/~mueller/ftp/pub/mueller/papers/sc12.pdf),” at *International Conference for High Performance Computing, Networking, Storage and Analysis* (SC), November 2012. [doi:10.1109/SC.2012.49](https://doi.org/10.1109/SC.2012.49)
|
||||
|
||||
[[50](ch01.html#KornfeldSimpson2020-marker)] Anna Kornfeld Simpson, Adriana Szekeres, Jacob Nelson, and Irene Zhang. [Securing RDMA for High-Performance Datacenter Storage Systems](https://www.usenix.org/conference/hotcloud20/presentation/kornfeld-simpson). At *12th USENIX Workshop on Hot Topics in Cloud Computing* (HotCloud), July 2020.
|
||||
|
||||
[[51](ch01.html#Singh2015-marker)] Arjun Singh, Joon Ong, Amit Agarwal, Glen Anderson, Ashby Armistead, Roy Bannon, Seb Boving, Gaurav Desai, Bob Felderman, Paulie Germano, Anand Kanagala, Jeff Provost, Jason Simmons, Eiichi Tanda, Jim Wanderer, Urs Hölzle, Stephen Stuart, and Amin Vahdat. [Jupiter Rising: A Decade of Clos Topologies and Centralized Control in Google’s Datacenter Network](http://conferences.sigcomm.org/sigcomm/2015/pdf/papers/p183.pdf). At *Annual Conference of the ACM Special Interest Group on Data Communication* (SIGCOMM), August 2015. [doi:10.1145/2785956.2787508](https://doi.org/10.1145/2785956.2787508)
|
||||
|
||||
[[52](ch01.html#Lockwood2014-marker)] Glenn K. Lockwood. [Hadoop’s Uncomfortable Fit in HPC](http://glennklockwood.blogspot.co.uk/2014/05/hadoops-uncomfortable-fit-in-hpc.html). *glennklockwood.blogspot.co.uk*, May 2014. Archived at [perma.cc/S8XX-Y67B](https://perma.cc/S8XX-Y67B)
|
||||
|
||||
[[53](ch01.html#ONeil2016_ch1-marker)] Cathy O’Neil: *Weapons of Math Destruction: How Big Data Increases Inequality and Threatens Democracy*. Crown Publishing, 2016. ISBN: 9780553418811
|
||||
|
||||
[[54](ch01.html#Shastri2020-marker)] Supreeth Shastri, Vinay Banakar, Melissa Wasserman, Arun Kumar, and Vijay Chidambaram. [Understanding and Benchmarking the Impact of GDPR on Database Systems](http://www.vldb.org/pvldb/vol13/p1064-shastri.pdf). *Proceedings of the VLDB Endowment*, volume 13, issue 7, pages 1064–1077, March 2020. [doi:10.14778/3384345.3384354](https://doi.org/10.14778/3384345.3384354)
|
||||
|
||||
[[55](ch01.html#Datensparsamkeit-marker)] Martin Fowler. [Datensparsamkeit](https://www.martinfowler.com/bliki/Datensparsamkeit.html). *martinfowler.com*, December 2013. Archived at [perma.cc/R9QX-CME6](https://perma.cc/R9QX-CME6)
|
||||
|
||||
[[56](ch01.html#GDPR-marker)] [Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 (General Data Protection Regulation)](https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:32016R0679&from=EN). *Official Journal of the European Union* L 119/1, May 2016.
|
||||
|
|
@ -1,913 +0,0 @@
|
|||
---
|
||||
title: "第二章:定义非功能性要求"
|
||||
linkTitle: "2. 定义非功能性要求"
|
||||
weight: 102
|
||||
breadcrumbs: false
|
||||
---
|
||||
|
||||
|
||||
> 互联网做得太棒了,以至于大多数人将它看作像太平洋这样的自然资源,而不是什么人工产物。上一次出现这种大规模且无差错的技术,你还记得是什么时候吗?
|
||||
>
|
||||
> —— [艾伦・凯](http://www.drdobbs.com/architecture-and-design/interview-with-alan-kay/240003442) 在接受 Dobb 博士杂志采访时说(2012 年)
|
||||
|
||||
--------
|
||||
|
||||
如果您正在构建应用程序,您将由一系列需求所驱动。在您需求列表的最顶端,很可能是应用程序必须提供的功能:需要哪些屏幕和按钮,以及每个操作应如何执行以满足软件的目的。这些是您的*功能性需求*。
|
||||
|
||||
此外,您可能还有一些*非功能性需求*:例如,应用应该快速、可靠、安全、合法合规,并且易于维护。这些需求可能没有明确书写下来,因为它们似乎有些显而易见,但它们和应用的功能一样重要:一个异常缓慢或不可靠的应用可能根本无法存在。
|
||||
|
||||
并非所有非功能性需求都属于本书的讨论范围,但有几个是如此。在本章中,我们将介绍几个技术概念,这将帮助您明确自己系统的非功能性需求:
|
||||
|
||||
- 如何定义和衡量系统的*性能*(见[“描述性能”](#描述性能));
|
||||
- 服务*可靠*的含义——即使在出现问题时,也能继续正确工作(见[“可靠性与容错”](#可靠性与容错));
|
||||
- 允许系统通过有效地增加计算能力来*可扩展*,随着系统负载的增长(见[“可伸缩性”](#可伸缩性));以及
|
||||
- 长期易于维护系统(见[“可维护性”](#可维护性))。
|
||||
|
||||
本章引入的术语在后续章节中也将非常有用,当我们详细探讨数据密集型系统的实现方式时。然而,抽象的定义可能相当枯燥;为了使这些概念更具体,我们将从社交网络服务的案例研究开始本章,这将提供性能和可扩展性的实际示例。
|
||||
|
||||
If you are building an application, you will be driven by a list of requirements. At the top of your list is most likely the functionality that the application must offer: what screens and what buttons you need, and what each operation is supposed to do in order to fulfill the purpose of your software. These are your *functional requirements*.
|
||||
|
||||
In addition, you probably also have some *nonfunctional requirements*: for example, the app should be fast, reliable, secure, legally compliant, and easy to maintain. These requirements might not be explicitly written down, because they may seem somewhat obvious, but they are just as important as the app’s functionality: an app that is unbearably slow or unreliable might as well not exist.
|
||||
|
||||
Not all nonfunctional requirements fall within the scope of this book, but several do. In this chapter we will introduce several technical concepts that will help you articulate the nonfunctional requirements for your own systems:
|
||||
|
||||
- How to define and measure the *performance* of a system (see [“Describing Performance”](ch02.html#sec_introduction_percentiles));
|
||||
- What it means for a service to be *reliable*—namely, continuing to work correctly, even when things go wrong (see [“Reliability and Fault Tolerance”](ch02.html#sec_introduction_reliability));
|
||||
- Allowing a system to be *scalable* by having efficient ways of adding computing capacity as the load on the system grows (see [“Scalability”](ch02.html#sec_introduction_scalability)); and
|
||||
- Making it easier to maintain a system in the long term (see [“Maintainability”](ch02.html#sec_introduction_maintainability)).
|
||||
|
||||
The terminology introduced in this chapter will also be useful in the following chapters, when we go into the details of how data-intensive systems are implemented. However, abstract definitions can be quite dry; to make the ideas more concrete, we will start this chapter with a case study of how a social networking service might work, which will provide practical examples of performance and scalability.
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 案例学习:社交网络主页时间线
|
||||
|
||||
假设你被分配了一个任务,要实现一个类似X(前身为Twitter)的社交网络,在这个网络中,用户可以发布消息并关注其他用户。这将是对这种服务实际工作方式的极大简化 [[1](ch02.html#Cvet2016), [2](ch02.html#Krikorian2012_ch2), [3](ch02.html#Twitter2023)],但它将有助于说明大规模系统中出现的一些问题。
|
||||
|
||||
假设用户每天发布 5 亿条消息,平均每秒 5700 条消息。偶尔,这个速率可能会激增至每秒 150,000 条消息 [[4](ch02.html#Krikorian2013)]。我们还假设平均每个用户关注 200 人,拥有 200 名粉丝(尽管这个范围非常广泛:大多数人只有少数几个粉丝,而像巴拉克·奥巴马这样的名人粉丝超过 1 亿)。
|
||||
|
||||
|
||||
Imagine you are given the task of implementing a social network in the style of X (formerly Twitter), in which users can post messages and follow other users. This will be a huge simplification of how such a service actually works [[1](ch02.html#Cvet2016), [2](ch02.html#Krikorian2012_ch2), [3](ch02.html#Twitter2023)], but it will help illustrate some of the issues that arise in large-scale systems.
|
||||
|
||||
Let’s assume that users make 500 million posts per day, or 5,700 posts per second on average. Occasionally, the rate can spike as high as 150,000 posts/second [[4](ch02.html#Krikorian2013)]. Let’s also assume that the average user follows 200 people and has 200 followers (although there is a very wide range: most people have only a handful of followers, and a few celebrities such as Barack Obama have over 100 million followers).
|
||||
|
||||
### 用户、帖子和关注关系的表示
|
||||
|
||||
|
||||
设想我们将所有数据保存在关系数据库中,如 [图 2-1](ch02.html#fig_twitter_relational) 所示。我们有一个用户表、一个帖子表和一个关注关系表。
|
||||
|
||||
Imagine we keep all of the data in a relational database as shown in [Figure 2-1](ch02.html#fig_twitter_relational). We have one table for users, one table for posts, and one table for follow relationships.
|
||||
|
||||

|
||||
|
||||
> 图 2-1. 社交网络的简单关系模式,其中用户可以相互关注。
|
||||
|
||||
假设我们的社交网络需要支持的主要读操作是*首页时间线*,它显示你所关注的人最近的帖子(为简单起见,我们将忽略广告、来自你未关注的人的建议帖子以及其他扩展)。我们可以编写以下 SQL 查询来获取特定用户的首页时间线:
|
||||
|
||||
> Figure 2-1. Simple relational schema for a social network in which users can follow each other.
|
||||
|
||||
Let’s say the main read operation that our social network must support is the *home timeline*, which displays recent posts by people you are following (for simplicity we will ignore ads, suggested posts from people you are not following, and other extensions). We could write the following SQL query to get the home timeline for a particular user:
|
||||
|
||||
```sql
|
||||
SELECT posts.*, users.* FROM posts
|
||||
JOIN follows ON posts.sender_id = follows.followee_id
|
||||
JOIN users ON posts.sender_id = users.id
|
||||
WHERE follows.follower_id = current_user
|
||||
ORDER BY posts.timestamp DESC
|
||||
LIMIT 1000
|
||||
```
|
||||
|
||||
为了执行这个查询,数据库将使用 `follows` 表来查找 `current_user` 正在关注的所有人,查找这些用户的最近帖子,并按时间戳排序以获得被关注用户的最新 1000 条帖子。
|
||||
|
||||
帖子应当是及时的,因此假设某人发帖后,我们希望他们的关注者在 5 秒内能看到。一种实现这一目标的方法是,当用户在线时,其客户端每 5 秒重复上述查询一次(这被称为*轮询*)。如果我们假设有 1000 万用户同时在线并登录,这意味着每秒需要运行 200 万次查询。即使你增加轮询间隔,这也是一个庞大的数字。
|
||||
|
||||
此外,上述查询相当昂贵:如果你关注了 200 人,它需要获取这 200 人的最近帖子列表,并合并这些列表。每秒 200 万次时间线查询意味着数据库需要每秒查找某些发送者的最近帖子 4 亿次——这是一个巨大的数字。而这只是平均情况。有些用户关注了成千上万的账户;对他们而言,这个查询非常昂贵,难以快速执行。
|
||||
|
||||
To execute this query, the database will use the `follows` table to find everybody who `current_user` is following, look up recent posts by those users, and sort them by timestamp to get the most recent 1,000 posts by any of the followed users.
|
||||
|
||||
Posts are supposed to be timely, so let’s assume that after somebody makes a post, we want their followers to be able to see it within 5 seconds. One way of doing that would be for the user’s client to repeat the query above every 5 seconds while the user is online (this is known as *polling*). If we assume that 10 million users are online and logged in at the same time, that would mean running the query 2 million times per second. Even if you increase the polling interval, this is a lot.
|
||||
|
||||
Moreover, the query above is quite expensive: if you are following 200 people, it needs to fetch a list of recent posts by each of those 200 people, and merge those lists. 2 million timeline queries per second then means that the database needs to look up the recent posts from some sender 400 million times per second—a huge number. And that is the average case. Some users follow tens of thousands of accounts; for them, this query is very expensive to execute, and difficult to make fast.
|
||||
|
||||
### 物化与更新时间线
|
||||
|
||||
我们怎样才能做得更好?首先,与其使用轮询,不如让服务器主动将新帖推送给当前在线的任何关注者。其次,我们应该预计算上述查询的结果,以便用户请求他们的首页时间线时可以从缓存中获取。
|
||||
|
||||
想象一下,对于每个用户,我们存储一个包含他们首页时间线的数据结构,即他们所关注的人的最近帖子。每当用户发表帖子时,我们查找他们所有的关注者,并将该帖子插入到每个关注者的首页时间线中——就像将信息送达邮箱一样。现在,当用户登录时,我们可以简单地提供我们预计算的这个首页时间线。此外,为了接收其时间线上任何新帖子的通知,用户的客户端只需订阅被添加到他们首页时间线的帖子流。
|
||||
|
||||
这种方法的缺点是,每当用户发帖时,我们都需要做更多的工作,因为首页时间线是派生数据,需要更新。这一过程在 [图 2-2](ch02.html#fig_twitter_timelines) 中有所示。当一个初始请求导致执行多个下游请求时,我们使用*扩散*一词来描述请求数量的增加因素。
|
||||
|
||||
How can we do better? Firstly, instead of polling, it would be better if the server actively pushed new posts to any followers who are currently online. Secondly, we should precompute the results of the query above so that a user’s request for their home timeline can be served from a cache.
|
||||
|
||||
Imagine that for each user we store a data structure containing their home timeline, i.e., the recent posts by people they are following. Every time a user makes a post, we look up all of their followers, and insert that post into the home timeline of each follower—like delivering a message to a mailbox. Now when a user logs in, we can simply give them this home timeline that we precomputed. Moreover, to receive a notification about any new posts on their timeline, the user’s client simply needs to subscribe to the stream of posts being added to their home timeline.
|
||||
|
||||
The downside of this approach is that we now need to do more work every time a user makes a post, because the home timelines are derived data that needs to be updated. The process is illustrated in [Figure 2-2](ch02.html#fig_twitter_timelines). When one initial request results in several downstream requests being carried out, we use the term *fan-out* to describe the factor by which the number of requests increases.
|
||||
|
||||

|
||||
|
||||
> 图 2-2. 扇出: 将新推文传达给发帖用户的每个关注者
|
||||
|
||||
以每秒 5700 帖的速率,如果平均每个帖子达到 200 个关注者(即扩散因子为 200),我们将需要每秒执行超过 100 万次首页时间线写入。这个数字虽然大,但与我们原本需要执行的每秒 4 亿次按发送者查找帖子相比,仍然是一个显著的节省。
|
||||
|
||||
如果由于某些特殊事件导致帖子发布率激增,我们不必立即执行时间线传递——我们可以将它们排队,并接受帖子在关注者时间线上显示出来可能会暂时延迟一些。即使在此类负载激增期间,时间线的加载仍然很快,因为我们只需从缓存中提供它们。
|
||||
|
||||
这种预计算和更新查询结果的过程被称为*实体化*,而时间线缓存则是一个*实体化视图*的例子(这是我们将进一步讨论的一个概念)。实体化的缺点是,每当一位名人发帖时,我们现在必须做大量的工作,将那篇帖子插入他们数百万关注者的首页时间线中。
|
||||
|
||||
解决这个问题的一种方法是将名人的帖子与其他人的帖子分开处理:我们可以通过将名人的帖子单独存储并在读取时与实体化时间线合并,从而避免将它们添加到数百万时间线上的努力。尽管有此类优化,处理社交网络上的名人可能需要大量的基础设施 [[5](ch02.html#Axon2010_ch2)]。
|
||||
|
||||
At a rate of 5,700 posts posted per second, if the average post reaches 200 followers (i.e., a fan-out factor of 200), we will need to do just over 1 million home timeline writes per second. This is a lot, but it’s still a significant saving compared to the 400 million per-sender post lookups per second that we would otherwise have to do.
|
||||
|
||||
If the rate of posts spikes due to some special event, we don’t have to do the timeline deliveries immediately—we can enqueue them and accept that it will temporarily take a bit longer for posts to show up in followers’ timelines. Even during such load spikes, timelines remain fast to load, since we simply serve them from a cache.
|
||||
|
||||
This process of precomputing and updating the results of a query is called *materialization*, and the timeline cache is an example of a *materialized view* (a concept we will discuss further in [Link to Come]). The downside of materialization is that every time a celebrity makes a post, we now have to do a large amount of work to insert that post into the home timelines of each of their millions of followers.
|
||||
|
||||
One way of solving this problem is to handle celebrity posts separately from everyone else’s posts: we can save ourselves the effort of adding them to millions of timelines by storing the celebrity posts separately and merging them with the materialized timeline when it is read. Despite such optimizations, handling celebrities on a social network can require a lot of infrastructure [[5](ch02.html#Axon2010_ch2)].
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 描述性能
|
||||
|
||||
在软件性能的讨论中,通常考虑两种主要的度量指标:
|
||||
|
||||
- **响应时间**(Response Time)
|
||||
|
||||
从用户发出请求的那一刻到他们接收到请求的答案所经历的时间。测量单位是秒。
|
||||
|
||||
- **吞吐量**(Throughput)
|
||||
|
||||
系统每秒处理的请求数量或每秒处理的数据量。对于给定的硬件资源配置,存在一个*最大吞吐量*。测量单位是“每秒某事物数”。
|
||||
|
||||
在社交网络案例研究中,“每秒帖子数”和“每秒时间线写入数”是吞吐量指标,而“加载首页时间线所需的时间”或“帖子传递给关注者的时间”是响应时间指标。
|
||||
|
||||
吞吐量与响应时间之间通常存在联系;在线服务中这种关系的一个示例在 [图 2-3](ch02.html#fig_throughput) 中进行了描述。当请求吞吐量低时,服务具有低响应时间,但随着负载增加,响应时间会增长。这是因为*排队*:当请求到达一个负载较高的系统时,很可能 CPU 正在处理先前的请求,因此新来的请求需要等待直到先前的请求完成。当吞吐量接近硬件能够处理的最大值时,排队延迟会急剧增加。
|
||||
|
||||
|
||||
Most discussions of software performance consider two main types of metric:
|
||||
|
||||
- Response Time
|
||||
|
||||
The elapsed time from the moment when a user makes a request until they receive the requested answer. The unit of measurement is seconds.
|
||||
|
||||
- Throughput
|
||||
|
||||
The number of requests per second, or the data volume per second, that the system is processing. For a given a particular allocation of hardware resources, there is a *maximum throughput* that can be handled. The unit of measurement is “somethings per second”.
|
||||
|
||||
In the social network case study, “posts per second” and “timeline writes per second” are throughput metrics, whereas the “time it takes to load the home timeline” or the “time until a post is delivered to followers” are response time metrics.
|
||||
|
||||
There is often a connection between throughput and response time; an example of such a relationship for an online service is sketched in [Figure 2-3](ch02.html#fig_throughput). The service has a low response time when request throughput is low, but response time increases as load increases. This is because of *queueing*: when a request arrives on a highly loaded system, it’s likely that the CPU is already in the process of handling an earlier request, and therefore the incoming request needs to wait until the earlier request has been completed. As throughput approaches the maximum that the hardware can handle, queueing delays increase sharply.
|
||||
|
||||

|
||||
|
||||
> 图2-3. 当服务吞吐量接近容量时,响应时间会由于排队而急剧增加
|
||||
|
||||
|
||||
#### 当过载系统无法恢复时
|
||||
|
||||
如果系统接近过载,吞吐量接近极限,有时会进入一个恶性循环,使得系统变得效率更低,从而更加过载。例如,如果有大量请求在排队等待处理,响应时间可能会增加到客户端超时并重新发送请求的程度。这会导致请求率进一步增加,使问题更加严重——这就是所谓的*重试风暴*。即使负载再次减少,这样的系统也可能仍处于过载状态,直到重新启动或以其他方式重置。这种现象称为*亚稳定故障*,可能会导致生产系统中严重的中断[[6](ch02.html#Bronson2021), [7](ch02.html#Brooker2021)]。
|
||||
|
||||
为了避免重试过度加载服务,你可以增加并随机化客户端连续重试之间的时间(*指数退避*[[8](ch02.html#Brooker2015), [9](ch02.html#Brooker2022backoff)]),并暂时停止向最近返回错误或超时的服务发送请求(使用*断路器*[[10](ch02.html#Nygard2018)]或*令牌桶*算法[[11](ch02.html#Brooker2022retries)])。服务器也可以检测到它即将过载,并开始主动拒绝请求(*减载*[[12](ch02.html#YanacekLoadShedding)]),并发送回响应要求客户端减慢速度(*反压力*[[1](ch02.html#Cvet2016), [13](ch02.html#Sackman2016_ch2)])。队列和负载平衡算法的选择也可以有所不同[[14](ch02.html#Kopytkov2018)]。
|
||||
|
||||
在性能指标方面,响应时间通常是用户最关心的,而吞吐量决定了所需的计算资源(例如,你需要多少服务器),从而决定了服务特定工作负载的成本。如果吞吐量可能超过当前硬件能够处理的范围,就需要扩展容量;如果一个系统能够通过增加计算资源显著提高其最大吞吐量,则称该系统具有*可扩展性*。
|
||||
|
||||
在本节中,我们将主要关注响应时间,并将在[“可扩展性”](ch02.html#sec_introduction_scalability)一节中回归讨论吞吐量和可扩展性。
|
||||
|
||||
|
||||
If a system is close to overload, with throughput pushed close to the limit, it can sometimes enter a vicious cycle where it becomes less efficient and hence even more overloaded. For example, if there is a long queue of requests waiting to be handled, response times may increase so much that clients time out and resend their request. This causes the rate of requests to increase even further, making the problem worse—a *retry storm*. Even when the load is reduced again, such a system may remain in an overloaded state until it is rebooted or otherwise reset. This phenomenon is called a *metastable failure*, and it can cause serious outages in production systems [[6](ch02.html#Bronson2021), [7](ch02.html#Brooker2021)].
|
||||
|
||||
To avoid retries overloading a service, you can increase and randomize the time between successive retries on the client side (*exponential backoff* [[8](ch02.html#Brooker2015), [9](ch02.html#Brooker2022backoff)]), and temporarily stop sending requests to a service that has returned errors or timed out recently (using a *circuit breaker* [[10](ch02.html#Nygard2018)] or *token bucket* algorithm [[11](ch02.html#Brooker2022retries)]). The server can also detect when it is approaching overload and start proactively rejecting requests (*load shedding* [[12](ch02.html#YanacekLoadShedding)]), and send back responses asking clients to slow down (*backpressure* [[1](ch02.html#Cvet2016), [13](ch02.html#Sackman2016_ch2)]). The choice of queueing and load-balancing algorithms can also make a difference [[14](ch02.html#Kopytkov2018)].
|
||||
|
||||
In terms of performance metrics, the response time is usually what users care about the most, whereas the throughput determines the required computing resources (e.g., how many servers you need), and hence the cost of serving a particular workload. If throughput is likely to increase beyond what the current hardware can handle, the capacity needs to be expanded; a system is said to be *scalable* if its maximum throughput can be significantly increased by adding computing resources.
|
||||
|
||||
In this section we will focus primarily on response times, and we will return to throughput and scalability in [“Scalability”](ch02.html#sec_introduction_scalability).
|
||||
|
||||
### 延迟与响应时间
|
||||
|
||||
“Latency”和“response time”有时被交替使用,但在本书中,我们将以特定的方式使用这些术语(如[图2-4](ch02.html#fig_response_time)所示):
|
||||
|
||||
- *响应时间*是客户端所看到的;它包括系统中任何地方产生的所有延迟。
|
||||
- *服务时间*是服务实际处理用户请求的持续时间。
|
||||
- *排队延迟*可以在流程的几个点出现:例如,接收到请求后
|
||||
- *延迟* 是一个包罗万象的术语,用于描述请求未被积极处理的时间,即处于 *潜伏状态* 的时间。特别是,*网络延迟* 或 *网络延迟* 指的是请求和响应在网络中传输的时间。
|
||||
|
||||
“Latency” and “response time” are sometimes used interchangeably, but in this book we will use the terms in a specific way (illustrated in [Figure 2-4](ch02.html#fig_response_time)):
|
||||
|
||||
- The *response time* is what the client sees; it includes all delays incurred anywhere in the system.
|
||||
- The *service time* is the duration for which the service is actively processing the user request.
|
||||
- *Queueing delays* can occur at several points in the flow: for example, after a request is received, it might need to wait until a CPU is available before it can be processed; a response packet might need to be buffered before it is sent over the network if other tasks on the same machine are sending a lot of data via the outbound network interface.
|
||||
- *Latency* is a catch-all term for time during which a request is not being actively processed, i.e., during which it is *latent*. In particular, *network latency* or *network delay* refers to the time that request and response spend traveling through the network.
|
||||
|
||||

|
||||
|
||||
> 图2-4. 响应时间、服务时间、网络延迟和排队延迟
|
||||
|
||||
即使反复发出同一请求,响应时间也可能因请求而异,差异显著。许多因素可能会导致随机延迟:例如,切换到后台进程的上下文切换,网络数据包丢失和 TCP 重传,垃圾收集暂停,页面错误强制从磁盘读取,服务器架的机械振动[[15](ch02.html#Gunawi2018)],或许多其他原因。我们将在 [未来链接] 中更详细地讨论这个话题。
|
||||
|
||||
排队延迟通常是响应时间变化性的一个重要部分。由于服务器同时只能处理少量事务(例如,受其 CPU 核心数量的限制),只需少数几个慢请求就足以阻塞后续请求的处理——这种效应被称为 *队首阻塞*。即使那些后续请求的服务时间很快,客户端也会因为等待先前请求完成而感觉到整体响应时间的缓慢。排队延迟不属于服务时间的一部分,因此在客户端测量响应时间十分重要。
|
||||
|
||||
The response time can vary significantly from one request to the next, even if you keep making the same request over and over again. Many factors can add random delays: for example, a context switch to a background process, the loss of a network packet and TCP retransmission, a garbage collection pause, a page fault forcing a read from disk, mechanical vibrations in the server rack [[15](ch02.html#Gunawi2018)], or many other causes. We will discuss this topic in more detail in [Link to Come].
|
||||
|
||||
Queueing delays often account for a large part of the variability in response times. As a server can only process a small number of things in parallel (limited, for example, by its number of CPU cores), it only takes a small number of slow requests to hold up the processing of subsequent requests—an effect known as *head-of-line blocking*. Even if those subsequent requests have fast service times, the client will see a slow overall response time due to the time waiting for the prior request to complete. The queueing delay is not part of the service time, and for this reason it is important to measure response times on the client side.
|
||||
|
||||
### 平均数,中位数与百分位点
|
||||
|
||||
因为响应时间从一个请求到另一个请求都在变化,我们需要把它视为一个你可以测量的值的 *分布*,而不是一个单一的数字。在 [图 2-5](ch02.html#fig_lognormal),每个灰色条代表对一个服务的请求,其高度显示了该请求所需的时间。大多数请求相当快,但偶尔也有 *异常值* 花费的时间要长得多。网络延迟的变化也被称为 *抖动*。
|
||||
|
||||
Because the response time varies from one request to the next, we need to think of it not as a single number, but as a *distribution* of values that you can measure. In [Figure 2-5](ch02.html#fig_lognormal), each gray bar represents a request to a service, and its height shows how long that request took. Most requests are reasonably fast, but there are occasional *outliers* that take much longer. Variation in network delay is also known as *jitter*.
|
||||
|
||||

|
||||
|
||||
> 图 2-5. 描述平均值和百分位数:对某服务100次请求的响应时间。
|
||||
>
|
||||
> Figure 2-5. Illustrating mean and percentiles: response times for a sample of 100 requests to a service.
|
||||
|
||||
通常我们会报告服务的*平均*响应时间(技术上说是*算术平均值*:即总和所有的响应时间,然后除以请求的数量)。然而,如果你想了解你的“典型”响应时间,平均值并不是一个很好的度量,因为它不能告诉你有多少用户实际经历了那种延迟。
|
||||
|
||||
通常使用*百分位数*会更好。如果你将响应时间列表从最快到最慢排序,那么*中位数*是中间点:例如,如果你的中位响应时间是200毫秒,这意味着你一半的请求在200毫秒内返回,另一半请求需要超过这个时间。这使得中位数成为一个好的度量,如果你想知道用户通常需要等待多久。中位数也被称为*第50百分位*,有时缩写为*p50*。
|
||||
|
||||
为了弄清楚你的异常值有多严重,你可以查看更高的百分位数:*第95、第99和第99.9百分位*是常见的(缩写为*p95、p99和p999*)。它们是响应时间的阈值,即95%、99%或99.9%的请求比该特定阈值快。例如,如果第95百分位的响应时间是1.5秒,这意味着100次请求中有95次不到1.5秒,有5次需要1.5秒或更多时间。这在[图 2-5](ch02.html#fig_lognormal)中有所示。
|
||||
|
||||
响应时间的高百分位数,也称为*尾部延迟*,很重要,因为它们直接影响用户对服务的体验。例如,亚马逊描述其内部服务的响应时间要求是以第99.9百分位来衡量,尽管它只影响1/1000的请求。这是因为请求最慢的客户往往是那些在他们的账户上有最多数据的客户,因为他们进行了许多购买——即,他们是最有价值的客户[[16](ch02.html#DeCandia2007_ch1)]。保证网站对他们来说快速是很重要的,以保持这些客户的满意。
|
||||
|
||||
另一方面,优化第99.99百分位(最慢的1/10,000的请求)被认为过于昂贵且对亚马逊的目的来说收益不足。在非常高的百分位数上减少响应时间是困难的,因为它们容易受到你无法控制的随机事件的影响,而且收益递减。
|
||||
|
||||
|
||||
It’s common to report the *average* response time of a service (technically, the *arithmetic mean*: that is, sum all the response times, and divide by the number of requests). However, the mean is not a very good metric if you want to know your “typical” response time, because it doesn’t tell you how many users actually experienced that delay.
|
||||
|
||||
Usually it is better to use *percentiles*. If you take your list of response times and sort it from fastest to slowest, then the *median* is the halfway point: for example, if your median response time is 200 ms, that means half your requests return in less than 200 ms, and half your requests take longer than that. This makes the median a good metric if you want to know how long users typically have to wait. The median is also known as the *50th percentile*, and sometimes abbreviated as *p50*.
|
||||
|
||||
In order to figure out how bad your outliers are, you can look at higher percentiles: the *95th*, *99th*, and *99.9th* percentiles are common (abbreviated *p95*, *p99*, and *p999*). They are the response time thresholds at which 95%, 99%, or 99.9% of requests are faster than that particular threshold. For example, if the 95th percentile response time is 1.5 seconds, that means 95 out of 100 requests take less than 1.5 seconds, and 5 out of 100 requests take 1.5 seconds or more. This is illustrated in [Figure 2-5](ch02.html#fig_lognormal).
|
||||
|
||||
High percentiles of response times, also known as *tail latencies*, are important because they directly affect users’ experience of the service. For example, Amazon describes response time requirements for internal services in terms of the 99.9th percentile, even though it only affects 1 in 1,000 requests. This is because the customers with the slowest requests are often those who have the most data on their accounts because they have made many purchases—that is, they’re the most valuable customers [[16](ch02.html#DeCandia2007_ch1)]. It’s important to keep those customers happy by ensuring the website is fast for them.
|
||||
|
||||
On the other hand, optimizing the 99.99th percentile (the slowest 1 in 10,000 requests) was deemed too expensive and to not yield enough benefit for Amazon’s purposes. Reducing response times at very high percentiles is difficult because they are easily affected by random events outside of your control, and the benefits are diminishing.
|
||||
|
||||
### 响应时间对用户的影响
|
||||
|
||||
直觉上看,快速服务比慢服务更有利于用户似乎是显而易见的[[17](ch02.html#Whitenton2020)]。然而,要获取可靠数据来量化延迟对用户行为的影响却出奇地困难。
|
||||
|
||||
一些经常被引用的统计数据是不可靠的。2006年谷歌报告称,搜索结果从400毫秒减慢到900毫秒,导致流量和收入下降20%[[18](ch02.html#Linden2006)]。然而,谷歌在2009年的另一项研究报告称,延迟增加400毫秒仅导致每天的搜索量减少0.6%[[19](ch02.html#Brutlag2009)],同年必应发现加载时间增加两秒钟,广告收入减少了4.3%[[20](ch02.html#Schurman2009)]。这些公司的更新数据似乎没有公开。
|
||||
|
||||
Akamai的一项较新研究[[21](ch02.html#Akamai2017)]声称响应时间增加100毫秒,会使电子商务网站的转化率降低多达7%;然而,仔细检查同一研究发现,非常*快*的页面加载时间也与较低的转化率相关!这种看似矛盾的结果是由于最快加载的页面往往是那些没有有用内容的页面(例如,404错误页面)。然而,由于该研究没有努力区分页面内容和加载时间的影响,其结果可能没有意义。
|
||||
|
||||
雅虎的一项研究[[22](ch02.html#Bai2017)]比较了快速加载与慢速加载搜索结果的点击率,控制搜索结果的质量。研究发现,当快速和慢速响应之间的差异在1.25秒或更多时,快速搜索的点击率增加了20-30%。
|
||||
|
||||
It seems intuitively obvious that a fast service is better for users than a slow service [[17](ch02.html#Whitenton2020)]. However, it is surprisingly difficult to get hold of reliable data to quantify the effect that latency has on user behavior.
|
||||
|
||||
Some often-cited statistics are unreliable. In 2006 Google reported that a slowdown in search results from 400 ms to 900 ms was associated with a 20% drop in traffic and revenue [[18](ch02.html#Linden2006)]. However, another Google study from 2009 reported that a 400 ms increase in latency resulted in only 0.6% fewer searches per day [[19](ch02.html#Brutlag2009)], and in the same year Bing found that a two-second increase in load time reduced ad revenue by 4.3% [[20](ch02.html#Schurman2009)]. Newer data from these companies appears not to be publicly available.
|
||||
|
||||
A more recent Akamai study [[21](ch02.html#Akamai2017)] claims that a 100 ms increase in response time reduced the conversion rate of e-commerce sites by up to 7%; however, on closer inspection, the same study reveals that very *fast* page load times are also correlated with lower conversion rates! This seemingly paradoxical result is explained by the fact that the pages that load fastest are often those that have no useful content (e.g., 404 error pages). However, since the study makes no effort to separate the effects of page content from the effects of load time, its results are probably not meaningful.
|
||||
|
||||
A study by Yahoo [[22](ch02.html#Bai2017)] compares click-through rates on fast-loading versus slow-loading search results, controlling for quality of search results. It finds 20–30% more clicks on fast searches when the difference between fast and slow responses is 1.25 seconds or more.
|
||||
|
||||
#### 使用响应时间指标
|
||||
|
||||
高百分位数在后端服务中尤其重要,这些服务在处理单个最终用户请求时会被多次调用。即使你并行进行调用,最终用户请求仍然需要等待并行调用中最慢的一个完成。正如[图 2-6](ch02.html#fig_tail_amplification)所示,只需一个慢调用就能使整个最终用户请求变慢。即使只有少数后端调用较慢,如果最终用户请求需要多次后端调用,获得慢调用的几率就会增加,因此更高比例的最终用户请求最终变慢(这种效应被称为*尾延迟放大*[[23](ch02.html#Dean2013)])。
|
||||
|
||||
High percentiles are especially important in backend services that are called multiple times as part of serving a single end-user request. Even if you make the calls in parallel, the end-user request still needs to wait for the slowest of the parallel calls to complete. It takes just one slow call to make the entire end-user request slow, as illustrated in [Figure 2-6](ch02.html#fig_tail_amplification). Even if only a small percentage of backend calls are slow, the chance of getting a slow call increases if an end-user request requires multiple backend calls, and so a higher proportion of end-user requests end up being slow (an effect known as *tail latency amplification* [[23](ch02.html#Dean2013)]).
|
||||
|
||||

|
||||
|
||||
> 图 2-6. 当一个请求需要多次后端调用时,只需要一个缓慢的后端请求,就能拖慢整个终端用户的请求
|
||||
|
||||
百分位数通常用于*服务级别目标*(SLOs)和*服务级别协议*(SLAs),作为定义服务预期性能和可用性的方式[[24](ch02.html#Hidalgo2020)]。例如,SLO可能设定一个目标,要求服务的中位响应时间少于200毫秒,第99百分位在1秒以下,并且至少99.9%的有效请求结果为非错误响应。SLA是一份合同,规定如果未达到SLO将发生什么(例如,客户可能有权获得退款)。至少基本思想是这样的;实际上,为SLOs和SLAs定义良好的可用性指标并不简单[[25](ch02.html#Mogul2019), 26]。
|
||||
|
||||
Percentiles are often used in *service level objectives* (SLOs) and *service level agreements* (SLAs) as ways of defining the expected performance and availability of a service [[24](ch02.html#Hidalgo2020)]. For example, an SLO may set a target for a service to have a median response time of less than 200 ms and a 99th percentile under 1 s, and a target that at least 99.9% of valid requests result in non-error responses. An SLA is a contract that specifies what happens if the SLO is not met (for example, customers may be entitled to a refund). That is the basic idea, at least; in practice, defining good availability metrics for SLOs and SLAs is not straightforward [[25](ch02.html#Mogul2019), [26](ch02.html#Hauer2020)].
|
||||
|
||||
#### 计算百分位点
|
||||
|
||||
如果你想在服务的监控仪表板上添加响应时间百分位数,你需要持续有效地计算它们。例如,你可能希望保持一个最近10分钟内请求响应时间的滚动窗口。每分钟,你都会计算该窗口中的中位数和各种百分位数,并将这些指标绘制在图表上。
|
||||
|
||||
最简单的实现方式是保留时间窗口内所有请求的响应时间列表,并每分钟对该列表进行排序。如果这对你来说效率太低,有些算法可以以最小的CPU和内存成本计算出百分位数的良好近似值。开源的百分位数估计库包括 HdrHistogram、t-digest [[27](ch02.html#Dunning2021), [28](ch02.html#Kohn2021)]、OpenHistogram [[29](ch02.html#Hartmann2020)] 和 DDSketch [[30](ch02.html#Masson2019)]。
|
||||
|
||||
注意,对百分位数进行平均化,例如为了降低时间分辨率或将来自几台机器的数据结合在一起,从数学上讲是没有意义的——聚合响应时间数据的正确方法是添加直方图[[31](ch02.html#Schwartz2015)]。
|
||||
|
||||
If you want to add response time percentiles to the monitoring dashboards for your services, you need to efficiently calculate them on an ongoing basis. For example, you may want to keep a rolling window of response times of requests in the last 10 minutes. Every minute, you calculate the median and various percentiles over the values in that window and plot those metrics on a graph.
|
||||
|
||||
The simplest implementation is to keep a list of response times for all requests within the time window and to sort that list every minute. If that is too inefficient for you, there are algorithms that can calculate a good approximation of percentiles at minimal CPU and memory cost. Open source percentile estimation libraries include HdrHistogram, t-digest [[27](ch02.html#Dunning2021), [28](ch02.html#Kohn2021)], OpenHistogram [[29](ch02.html#Hartmann2020)], and DDSketch [[30](ch02.html#Masson2019)].
|
||||
|
||||
Beware that averaging percentiles, e.g., to reduce the time resolution or to combine data from several machines, is mathematically meaningless—the right way of aggregating response time data is to add the histograms [[31](ch02.html#Schwartz2015)].
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 可靠性与容错
|
||||
|
||||
每个人对于一个东西可靠不可靠都有自己的直观想法。对于软件来说,典型的期望包括:
|
||||
|
||||
* 应用程序表现出用户所期望的功能。
|
||||
* 软件允许用户犯错,或以意料之外的方式来使用软件。
|
||||
* 在预期的负载和数据量下,性能可以满足要求。
|
||||
* 系统能够阻止未经授权的访问和滥用。
|
||||
|
||||
如果把所有这些要求放一块儿意味着 “正确工作”,那么我们可以把 *可靠性* 粗略理解为:“即使出现问题,也能继续正常工作”。为了更准确地描述问题的发生,我们将区分*故障*和*失败*[[32](ch02.html#Heimerdinger1992), [33](ch02.html#Gaertner1999)]:
|
||||
|
||||
- **故障**(fault)
|
||||
|
||||
故障是指系统的某个部分停止正常工作:例如,单个硬盘故障,或者单台机器崩溃,或者系统依赖的外部服务出现中断。
|
||||
A fault is when a particular *part* of a system stops working correctly: for example, if a single hard drive malfunctions, or a single machine crashes, or an external service (that the system depends on) has an outage.
|
||||
|
||||
- **失效**(Failure)
|
||||
|
||||
失效是指系统整体停止向用户提供所需服务;换句话说,就是未达到服务级别目标(SLO)。
|
||||
A failure is when the system *as a whole* stops providing the required service to the user; in other words, when it does not meet the service level objective (SLO).
|
||||
|
||||
故障与失败之间的区别可能会引起混淆,因为它们是同一件事,只是在不同的层级上。例如,如果一个硬盘停止工作,我们说硬盘发生了失败:如果系统只由那一个硬盘组成,它就停止提供所需的服务。然而,如果你所说的系统包含多个硬盘,那么单个硬盘的失败只是从更大系统的角度看是一个故障,并且更大的系统可能能够通过在另一个硬盘上有数据的副本来容忍这个故障。
|
||||
|
||||
The distinction between fault and failure can be confusing because they are the same thing, just at different levels. For example, if a hard drive stops working, we say that the hard drive has failed: if the system consists only of that one hard drive, it has stopped providing the required service. However, if the system you’re talking about contains many hard drives, then the failure of a single hard drive is only a fault from the point of view of the bigger system, and the bigger system might be able to tolerate that fault by having a copy of the data on another hard drive.
|
||||
|
||||
|
||||
### 容错
|
||||
|
||||
如果系统在某些故障发生时仍继续向用户提供所需服务,我们称该系统为*容错*系统。如果系统不能容忍某部分出现故障,我们称该部分为*单点故障*(SPOF),因为该部分的故障会升级为导致整个系统的失败。
|
||||
|
||||
例如,在社交网络案例研究中,可能发生的故障是在广播过程中,参与更新物化时间线的机器崩溃或变得不可用。为了使这个过程具有容错性,我们需要确保另一台机器能够接管这个任务,不遗漏任何本应传送的帖子,也不重复任何帖子。(这个概念被称为*精确一次语义*,我们将在[未来链接]中详细讨论)
|
||||
|
||||
We call a system *fault-tolerant* if it continues providing the required service to the user in spite of certain faults occurring. If a system cannot tolerate a certain part becoming faulty, we call that part a *single point of failure* (SPOF), because a fault in that part escalates to cause the failure of the whole system.
|
||||
|
||||
For example, in the social network case study, a fault that might happen is that during the fan-out process, a machine involved in updating the materialized timelines crashes or become unavailable. To make this process fault-tolerant, we would need to ensure that another machine can take over this task without missing any posts that should have been delivered, and without duplicating any posts. (This idea is known as *exactly-once semantics*, and we will examine it in detail in [Link to Come].)
|
||||
|
||||
容错性始终仅限于一定数量的特定类型的故障。例如,一个系统可能能够同时容忍最多两个硬盘故障,或者三个节点中最多有一个崩溃。容忍任意数量的故障是没有意义的:如果所有节点都崩溃了,那就无计可施。如果整个地球(及其上的所有服务器)被黑洞吞噬,那么要容忍这种故障就需要在太空中进行网络托管——祝你好运,让这个预算项目获批。
|
||||
|
||||
违反直觉的是,在这样的容错系统中,通过故意触发故障来*增加*故障率是有意义的——例如,随机无预警地终止个别进程。许多关键性的错误实际上是由于错误处理不当引起的[[34](ch02.html#Yuan2014)];通过故意诱发故障,你确保了容错机制不断地得到运用和测试,这可以增强你的信心,相信在自然发生故障时能够得到正确处理。*混沌工程*是一门旨在通过诸如故意注入故障的实验来提高对容错机制信心的学科[[35](ch02.html#Rosenthal2020)]。
|
||||
|
||||
虽然我们通常倾向于容忍故障而非预防故障,但在某些情况下,预防比治疗更好(例如,因为没有治疗方法)。在安全问题上就是这样,例如:如果攻击者已经侵入系统并获取了敏感数据,那个事件是无法撤销的。然而,本书主要讨论的是可以治愈的故障类型,如下文所述。
|
||||
|
||||
Fault tolerance is always limited to a certain number of certain types of faults. For example, a system might be able to tolerate a maximum of two hard drives failing at the same time, or a maximum of one out of three nodes crashing. It would not make sense to tolerate any number of faults: if all nodes crash, there is nothing that can be done. If the entire planet Earth (and all servers on it) were swallowed by a black hole, tolerance of that fault would require web hosting in space—good luck getting that budget item approved.
|
||||
|
||||
Counter-intuitively, in such fault-tolerant systems, it can make sense to *increase* the rate of faults by triggering them deliberately—for example, by randomly killing individual processes without warning. Many critical bugs are actually due to poor error handling [[34](ch02.html#Yuan2014)]; by deliberately inducing faults, you ensure that the fault-tolerance machinery is continually exercised and tested, which can increase your confidence that faults will be handled correctly when they occur naturally. *Chaos engineering* is a discipline that aims to improve confidence in fault-tolerance mechanisms through experiments such as deliberately injecting faults [[35](ch02.html#Rosenthal2020)].
|
||||
|
||||
Although we generally prefer tolerating faults over preventing faults, there are cases where prevention is better than cure (e.g., because no cure exists). This is the case with security matters, for example: if an attacker has compromised a system and gained access to sensitive data, that event cannot be undone. However, this book mostly deals with the kinds of faults that can be cured, as described in the following sections.
|
||||
|
||||
### 硬件与软件缺陷
|
||||
|
||||
当我们思考系统故障的原因时,硬件故障很快浮现脑海:
|
||||
|
||||
- 每年大约有 2-5% 的磁盘硬盘出现故障[[36](ch02.html#Pinheiro2007), [37](ch02.html#Schroeder2007)];在一个拥有 10,000 块硬盘的存储集群中,我们因此可以预计平均每天会有一块硬盘故障。最近的数据表明硬盘越来越可靠,但故障率仍然显著[[38](ch02.html#Klein2021)]。
|
||||
- 每年大约有 0.5-1% 的固态硬盘(SSD)故障[[39](ch02.html#Narayanan2016)]。少量的位错误可以自动纠正[[40](ch02.html#Alibaba2019_ch2)],但不可纠正的错误大约每年每块硬盘发生一次,即使是相当新的硬盘(即,磨损较少的硬盘);这种错误率高于磁盘硬盘[[41](ch02.html#Schroeder2016), [42](ch02.html#Alter2019)]。
|
||||
- 其他硬件组件如电源供应器、RAID 控制器和内存模块也会发生故障,尽管频率低于硬盘[[43](ch02.html#Ford2010), [44](ch02.html#Vishwanath2010)]。
|
||||
- 大约每 1,000 台机器中就有一台的 CPU 核心偶尔计算出错误的结果,这很可能是由制造缺陷引起的[[45](ch02.html#Hochschild2021), [46](ch02.html#Dixit2021), [47](ch02.html#Behrens2015)]。在某些情况下,错误的计算会导致崩溃,但在其他情况下,它会导致程序简单地返回错误的结果。
|
||||
- RAM 中的数据也可能被破坏,原因可能是宇宙射线等随机事件,或是永久性物理缺陷。即使使用了具有纠错码(ECC)的内存,超过 1% 的机器在给定年份遇到不可纠正的错误,这通常会导致机器和受影响的内存模块崩溃并需要更换[[48](ch02.html#Schroeder2009)]。此外,某些病态的内存访问模式可以高概率地翻转位[[49](ch02.html#Kim2014)]。
|
||||
- 整个数据中心可能变得不可用(例如,由于停电或网络配置错误)或甚至被永久性破坏(例如火灾或洪水)。尽管这种大规模故障很少见,但如果一项服务不能容忍数据中心的丢失,其影响可能是灾难性的[[50](ch02.html#Cockcroft2019)]。
|
||||
|
||||
这些事件足够罕见,以至于在处理小型系统时你通常不需要担心它们,只要你可以轻松替换变得有故障的硬件。然而,在大规模系统中,硬件故障发生得足够频繁,以至于它们成为正常系统运作的一部分。
|
||||
|
||||
When we think of causes of system failure, hardware faults quickly come to mind:
|
||||
|
||||
- Approximately 2–5% of magnetic hard drives fail per year [[36](ch02.html#Pinheiro2007), [37](ch02.html#Schroeder2007)]; in a storage cluster with 10,000 disks, we should therefore expect on average one disk failure per day. Recent data suggests that disks are getting more reliable, but failure rates remain significant [[38](ch02.html#Klein2021)].
|
||||
- Approximately 0.5–1% of solid state drives (SSDs) fail per year [[39](ch02.html#Narayanan2016)]. Small numbers of bit errors are corrected automatically [[40](ch02.html#Alibaba2019_ch2)], but uncorrectable errors occur approximately once per year per drive, even in drives that are fairly new (i.e., that have experienced little wear); this error rate is higher than that of magnetic hard drives [[41](ch02.html#Schroeder2016), [42](ch02.html#Alter2019)].
|
||||
- Other hardware components such as power supplies, RAID controllers, and memory modules also fail, although less frequently than hard drives [[43](ch02.html#Ford2010), [44](ch02.html#Vishwanath2010)].
|
||||
- Approximately one in 1,000 machines has a CPU core that occasionally computes the wrong result, likely due to manufacturing defects [[45](ch02.html#Hochschild2021), [46](ch02.html#Dixit2021), [47](ch02.html#Behrens2015)]. In some cases, an erroneous computation leads to a crash, but in other cases it leads to a program simply returning the wrong result.
|
||||
- Data in RAM can also be corrupted, either due to random events such as cosmic rays, or due to permanent physical defects. Even when memory with error-correcting codes (ECC) is used, more than 1% of machines encounter an uncorrectable error in a given year, which typically leads to a crash of the machine and the affected memory module needing to be replaced [[48](ch02.html#Schroeder2009)]. Moreover, certain pathological memory access patterns can flip bits with high probability [[49](ch02.html#Kim2014)].
|
||||
- An entire datacenter might become unavailable (for example, due to power outage or network misconfiguration) or even be permanently destroyed (for example by fire or flood). Although such large-scale failures are rare, their impact can be catastrophic if a service cannot tolerate the loss of a datacenter [[50](ch02.html#Cockcroft2019)].
|
||||
|
||||
These events are rare enough that you often don’t need to worry about them when working on a small system, as long as you can easily replace hardware that becomes faulty. However, in a large-scale system, hardware faults happen often enough that they become part of the normal system operation.
|
||||
|
||||
#### 通过冗余容忍硬件缺陷
|
||||
|
||||
Our first response to unreliable hardware is usually to add redundancy to the individual hardware components in order to reduce the failure rate of the system. Disks may be set up in a RAID configuration (spreading data across multiple disks in the same machine so that a failed disk does not cause data loss), servers may have dual power supplies and hot-swappable CPUs, and datacenters may have batteries and diesel generators for backup power. Such redundancy can often keep a machine running uninterrupted for years.
|
||||
|
||||
Redundancy is most effective when component faults are independent, that is, the occurrence of one fault does not change how likely it is that another fault will occur. However, experience has shown that there are often significant correlations between component failures [[37](ch02.html#Schroeder2007), [51](ch02.html#Han2021), [52](ch02.html#Nightingale2011)]; unavailability of an entire server rack or an entire datacenter still happens more often than we would like.
|
||||
|
||||
Hardware redundancy increases the uptime of a single machine; however, as discussed in [“Distributed versus Single-Node Systems”](ch01.html#sec_introduction_distributed), there are advantages to using a distributed system, such as being able to tolerate a complete outage of one datacenter. For this reason, cloud systems tend to focus less on the reliability of individual machines, and instead aim to make services highly available by tolerating faulty nodes at the software level. Cloud providers use *availability zones* to identify which resources are physically co-located; resources in the same place are more likely to fail at the same time than geographically separated resources.
|
||||
|
||||
The fault-tolerance techniques we discuss in this book are designed to tolerate the loss of entire machines, racks, or availability zones. They generally work by allowing a machine in one datacenter to take over when a machine in another datacenter fails or becomes unreachable. We will discuss such techniques for fault tolerance in [Link to Come], [Link to Come], and at various other points in this book.
|
||||
|
||||
Systems that can tolerate the loss of entire machines also have operational advantages: a single-server system requires planned downtime if you need to reboot the machine (to apply operating system security patches, for example), whereas a multi-node fault-tolerant system can be patched by restarting one node at a time, without affecting the service for users. This is called a *rolling upgrade*, and we will discuss it further in [Link to Come].
|
||||
|
||||
#### 软件缺陷
|
||||
|
||||
我们通常认为硬件故障是随机的、相互独立的:一台机器的磁盘失效并不意味着另一台机器的磁盘也会失效。虽然大量硬件组件之间可能存在微弱的相关性(例如服务器机架的温度等共同的原因),但同时发生故障也是极为罕见的。
|
||||
|
||||
另一类错误是内部的 **系统性错误(systematic error)**【8】。这类错误难以预料,而且因为是跨节点相关的,所以比起不相关的硬件故障往往可能造成更多的 **系统失效**【5】。例子包括:
|
||||
|
||||
* 接受特定的错误输入,便导致所有应用服务器实例崩溃的 BUG。例如 2012 年 6 月 30 日的闰秒,由于 Linux 内核中的一个错误【9】,许多应用同时挂掉了。
|
||||
* 失控进程会用尽一些共享资源,包括 CPU 时间、内存、磁盘空间或网络带宽。
|
||||
* 系统依赖的服务变慢,没有响应,或者开始返回错误的响应。
|
||||
* 级联故障,一个组件中的小故障触发另一个组件中的故障,进而触发更多的故障【10】。
|
||||
|
||||
导致这类软件故障的 BUG 通常会潜伏很长时间,直到被异常情况触发为止。这种情况意味着软件对其环境做出了某种假设 —— 虽然这种假设通常来说是正确的,但由于某种原因最后不再成立了【11】。
|
||||
|
||||
虽然软件中的系统性故障没有速效药,但我们还是有很多小办法,例如:仔细考虑系统中的假设和交互;彻底的测试;进程隔离;允许进程崩溃并重启;测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证(例如在一个消息队列中,进入与发出的消息数量相等),那么系统就可以在运行时不断自检,并在出现 **差异(discrepancy)** 时报警【12】。
|
||||
|
||||
|
||||
Although hardware failures can be weakly correlated, they are still mostly independent: for example, if one disk fails, it’s likely that other disks in the same machine will be fine for another while. On the other hand, software faults are often very highly correlated, because it is common for many nodes to run the same software and thus have the same bugs [[53](ch02.html#Gunawi2014), [54](ch02.html#Kreps2012_ch1)]. Such faults are harder to anticipate, and they tend to cause many more system failures than uncorrelated hardware faults [[43](ch02.html#Ford2010)]. For example:
|
||||
|
||||
- A software bug that causes every node to fail at the same time in particular circumstances. For example, on June 30, 2012, a leap second caused many Java applications to hang simultaneously due to a bug in the Linux kernel, bringing down many Internet services [[55](ch02.html#Minar2012_ch1)]. Due to a firmware bug, all SSDs of certain models suddenly fail after precisely 32,768 hours of operation (less than 4 years), rendering the data on them unrecoverable [[56](ch02.html#HPE2019)].
|
||||
- A runaway process that uses up some shared, limited resource, such as CPU time, memory, disk space, network bandwidth, or threads [[57](ch02.html#Hochstein2020)]. For example, a process that consumes too much memory while processing a large request may be killed by the operating system.
|
||||
- A service that the system depends on slows down, becomes unresponsive, or starts returning corrupted responses.
|
||||
- An interaction between different systems results in emergent behavior that does not occur when each system was tested in isolation [[58](ch02.html#Tang2023)].
|
||||
- Cascading failures, where a problem in one component causes another component to become overloaded and slow down, which in turn brings down another component [[59](ch02.html#Ulrich2016), [60](ch02.html#Fassbender2022)].
|
||||
|
||||
The bugs that cause these kinds of software faults often lie dormant for a long time until they are triggered by an unusual set of circumstances. In those circumstances, it is revealed that the software is making some kind of assumption about its environment—and while that assumption is usually true, it eventually stops being true for some reason [[61](ch02.html#Cook2000), [62](ch02.html#Woods2017)].
|
||||
|
||||
There is no quick solution to the problem of systematic faults in software. Lots of small things can help: carefully thinking about assumptions and interactions in the system; thorough testing; process isolation; allowing processes to crash and restart; avoiding feedback loops such as retry storms (see [“When an overloaded system won’t recover”](ch02.html#sidebar_metastable)); measuring, monitoring, and analyzing system behavior in production.
|
||||
|
||||
### 人类与可靠性
|
||||
|
||||
设计并构建了软件系统的工程师是人类,维持系统运行的运维也是人类。即使他们怀有最大的善意,人类也是不可靠的。举个例子,一项关于大型互联网服务的研究发现,运维配置错误是导致服务中断的首要原因,而硬件故障(服务器或网络)仅导致了 10-25% 的服务中断【13】。
|
||||
|
||||
尽管人类不可靠,但怎么做才能让系统变得可靠?最好的系统会组合使用以下几种办法:
|
||||
|
||||
* 以最小化犯错机会的方式设计系统。例如,精心设计的抽象、API 和管理后台使做对事情更容易,搞砸事情更困难。但如果接口限制太多,人们就会忽略它们的好处而想办法绕开。很难正确把握这种微妙的平衡。
|
||||
* 将人们最容易犯错的地方与可能导致失效的地方 **解耦(decouple)**。特别是提供一个功能齐全的非生产环境 **沙箱(sandbox)**,使人们可以在不影响真实用户的情况下,使用真实数据安全地探索和实验。
|
||||
* 在各个层次进行彻底的测试【3】,从单元测试、全系统集成测试到手动测试。自动化测试易于理解,已经被广泛使用,特别适合用来覆盖正常情况中少见的 **边缘场景(corner case)**。
|
||||
* 允许从人为错误中简单快速地恢复,以最大限度地减少失效情况带来的影响。例如,快速回滚配置变更,分批发布新代码(以便任何意外错误只影响一小部分用户),并提供数据重算工具(以备旧的计算出错)。
|
||||
* 配置详细和明确的监控,比如性能指标和错误率。在其他工程学科中这指的是 **遥测(telemetry)**(一旦火箭离开了地面,遥测技术对于跟踪发生的事情和理解失败是至关重要的)。监控可以向我们发出预警信号,并允许我们检查是否有任何地方违反了假设和约束。当出现问题时,指标数据对于问题诊断是非常宝贵的。
|
||||
* 良好的管理实践与充分的培训 —— 一个复杂而重要的方面,但超出了本书的范围。
|
||||
|
||||
|
||||
Humans design and build software systems, and the operators who keep the systems running are also human. Unlike machines, humans don’t just follow rules; their strength is being creative and adaptive in getting their job done. However, this characteristic also leads to unpredictability, and sometimes mistakes that can lead to failures, despite best intentions. For example, one study of large internet services found that configuration changes by operators were the leading cause of outages, whereas hardware faults (servers or network) played a role in only 10–25% of outages [[63](ch02.html#Oppenheimer2003)].
|
||||
|
||||
It is tempting to label such problems as “human error” and to wish that they could be solved by better controlling human behavior through tighter procedures and compliance with rules. However, blaming people for mistakes is counterproductive. What we call “human error” is not really the cause of an incident, but rather a symptom of a problem with the sociotechnical system in which people are trying their best to do their jobs [[64](ch02.html#Dekker2017)].
|
||||
|
||||
Various technical measures can help minimize the impact of human mistakes, including thorough testing [[34](ch02.html#Yuan2014)], rollback mechanisms for quickly reverting configuration changes, gradual roll-outs of new code, detailed and clear monitoring, observability tools for diagnosing production issues (see [“Problems with Distributed Systems”](ch01.html#sec_introduction_dist_sys_problems)), and well-designed interfaces that encourage “the right thing” and discourage “the wrong thing”.
|
||||
|
||||
However, these things require an investment of time and money, and in the pragmatic reality of everyday business, organizations often prioritize revenue-generating activities over measures that increase their resilience against mistakes. If there is a choice between more features and more testing, many organizations understandably choose features. Given this choice, when a preventable mistake inevitably occurs, it does not make sense to blame the person who made the mistake—the problem is the organization’s priorities.
|
||||
|
||||
Increasingly, organizations are adopting a culture of *blameless postmortems*: after an incident, the people involved are encouraged to share full details about what happened, without fear of punishment, since this allows others in the organization to learn how to prevent similar problems in the future [[65](ch02.html#Allspaw2012)]. This process may uncover a need to change business priorities, a need to invest in areas that have been neglected, a need to change the incentives for the people involved, or some other systemic issue that needs to be brought to the management’s attention.
|
||||
|
||||
As a general principle, when investigating an incident, you should be suspicious of simplistic answers. “Bob should have been more careful when deploying that change” is not productive, but neither is “We must rewrite the backend in Haskell.” Instead, management should take the opportunity to learn the details of how the sociotechnical system works from the point of view of the people who work with it every day, and take steps to improve it based on this feedback [[64](ch02.html#Dekker2017)].
|
||||
|
||||
### 可靠性到底有多重要?
|
||||
|
||||
可靠性不仅仅是针对核电站和空中交通管制软件而言,我们也期望更多平凡的应用能可靠地运行。商务应用中的错误会导致生产力损失(也许数据报告不完整还会有法律风险),而电商网站的中断则可能会导致收入和声誉的巨大损失。
|
||||
|
||||
即使在 “非关键” 应用中,我们也对用户负有责任。试想一位家长把所有的照片和孩子的视频储存在你的照片应用里【15】。如果数据库突然损坏,他们会感觉如何?他们可能会知道如何从备份恢复吗?
|
||||
|
||||
在某些情况下,我们可能会选择牺牲可靠性来降低开发成本(例如为未经证实的市场开发产品原型)或运营成本(例如利润率极低的服务),但我们偷工减料时,应该清楚意识到自己在做什么。
|
||||
|
||||
|
||||
Reliability is not just for nuclear power stations and air traffic control—more mundane applications are also expected to work reliably. Bugs in business applications cause lost productivity (and legal risks if figures are reported incorrectly), and outages of e-commerce sites can have huge costs in terms of lost revenue and damage to reputation.
|
||||
|
||||
In many applications, a temporary outage of a few minutes or even a few hours is tolerable [[66](ch02.html#Sabo2023)], but permanent data loss or corruption would be catastrophic. Consider a parent who stores all their pictures and videos of their children in your photo application [[67](ch02.html#Jurewitz2013)]. How would they feel if that database was suddenly corrupted? Would they know how to restore it from a backup?
|
||||
|
||||
As another example of how unreliable software can harm people, consider the Post Office Horizon scandal. Between 1999 and 2019, hundreds of people managing Post Office branches in Britain were convicted of theft or fraud because the accounting software showed a shortfall in their accounts. Eventually it became clear that many of these shortfalls were due to bugs in the software, and many convictions have since been overturned [[68](ch02.html#Siddique2021)]. What led to this, probably the largest miscarriage of justice in British history, is the fact that English law assumes that computers operate correctly (and hence, evidence produced by computers is reliable) unless there is evidence to the contrary [[69](ch02.html#Bohm2022)]. Software engineers may laugh at the idea that software could ever be bug-free, but this is little solace to the people who were wrongfully imprisoned, declared bankrupt, or even committed suicide as a result of a wrongful conviction due to an unreliable computer system.
|
||||
|
||||
There are situations in which we may choose to sacrifice reliability in order to reduce development cost (e.g., when developing a prototype product for an unproven market)—but we should be very conscious of when we are cutting corners and keep in mind the potential consequences.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 可伸缩性
|
||||
|
||||
即使系统今天运行可靠,也不意味着将来一定能保持可靠。退化的一个常见原因是负载增加:可能系统从1万并发用户增长到了10万,并发用户,或从100万增加到了1000万。也许它正在处理比以前更大的数据量。
|
||||
|
||||
可扩展性是我们用来描述系统应对增加负载能力的术语。有时,在讨论可扩展性时,人们会这样评论:“你不是谷歌或亚马逊。不用担心规模,只用关系型数据库就好。”这个格言是否适用于你,取决于你正在构建的应用类型。
|
||||
|
||||
如果你正在为一个刚起步的公司构建一个新产品,目前只有少数用户,通常最重要的工程目标是保持系统尽可能简单和灵活,以便你可以根据对客户需求的了解轻松修改和适应产品功能[70]。在这种环境下,担心未来可能需要的假设性规模是适得其反的:在最好的情况下,投资于可扩展性是浪费努力和过早的优化;在最坏的情况下,它们会让你陷入僵化的设计,使得应用难以进化。
|
||||
|
||||
原因是可扩展性不是一维标签:说“X可扩展”或“Y不可扩展”是没有意义的。相反,讨论可扩展性意味着考虑诸如此类的问题:
|
||||
|
||||
“如果系统以特定方式增长,我们有哪些应对增长的选项?”
|
||||
“我们如何增加计算资源来处理额外的负载?”
|
||||
“基于当前的增长预测,我们何时会达到当前架构的极限?”
|
||||
如果你成功地让你的应用受欢迎,因此处理了越来越多的负载,你将了解你的性能瓶颈在哪里,因此你将知道你需要沿哪些维度进行扩展。到了那个时候,就是开始担心扩展技术的时候了。
|
||||
|
||||
Even if a system is working reliably today, that doesn’t mean it will necessarily work reliably in the future. One common reason for degradation is increased load: perhaps the system has grown from 10,000 concurrent users to 100,000 concurrent users, or from 1 million to 10 million. Perhaps it is processing much larger volumes of data than it did before.
|
||||
|
||||
*Scalability* is the term we use to describe a system’s ability to cope with increased load. Sometimes, when discussing scalability, people make comments along the lines of, “You’re not Google or Amazon. Stop worrying about scale and just use a relational database.” Whether this maxim applies to you depends on the type of application you are building.
|
||||
|
||||
If you are building a new product that currently only has a small number of users, perhaps at a startup, the overriding engineering goal is usually to keep the system as simple and flexible as possible, so that you can easily modify and adapt the features of your product as you learn more about customers’ needs [[70](ch02.html#McKinley2015)]. In such an environment, it is counterproductive to worry about hypothetical scale that might be needed in the future: in the best case, investments in scalability are wasted effort and premature optimization; in the worst case, they lock you into an inflexible design and make it harder to evolve your application.
|
||||
|
||||
The reason is that scalability is not a one-dimensional label: it is meaningless to say “X is scalable” or “Y doesn’t scale.” Rather, discussing scalability means considering questions like:
|
||||
|
||||
- “If the system grows in a particular way, what are our options for coping with the growth?”
|
||||
- “How can we add computing resources to handle the additional load?”
|
||||
- “Based on current growth projections, when will we hit the limits of our current architecture?”
|
||||
|
||||
If you succeed in making your application popular, and therefore handling a growing amount of load, you will learn where your performance bottlenecks lie, and therefore you will know along which dimensions you need to scale. At that point it’s time to start worrying about techniques for scalability.
|
||||
|
||||
### 描述负载
|
||||
|
||||
首先,我们需要简洁地描述系统当前的负载;只有这样,我们才能讨论增长问题(如果我们的负载翻倍会发生什么?)。这通常是通过吞吐量来衡量的:例如,每秒向服务的请求数量、每天新增多少吉字节的数据,或者每小时有多少购物车结账。有时你关心某些变量的峰值,比如同时在线用户的数量,如[“案例研究:社交网络首页时间线”](ch02.html#sec_introduction_twitter)中所述。
|
||||
|
||||
负载的其他统计特性也可能影响访问模式,从而影响可扩展性需求。例如,你可能需要知道数据库中读写的比例、缓存的命中率,或每个用户的数据项数量(例如,社交网络案例研究中的关注者数量)。也许平均情况是你关心的,或许你的瓶颈由少数极端情况主导。这一切都取决于你特定应用的细节。
|
||||
|
||||
一旦你描述了系统的负载,你就可以探究当负载增加时会发生什么。你可以从两个方面考虑这个问题:
|
||||
|
||||
- 当你以某种方式增加负载并保持系统资源(CPU、内存、网络带宽等)不变时,你的系统性能会受到什么影响?
|
||||
- 当你以某种方式增加负载时,如果你想保持性能不变,你需要增加多少资源?
|
||||
|
||||
通常我们的目标是在最小化运行系统的成本的同时,保持系统性能符合SLA的要求(见[“响应时间指标的使用”](ch02.html#sec_introduction_slo_sla))。所需的计算资源越多,成本就越高。可能某些类型的硬件比其他类型更具成本效益,随着新型硬件的出现,这些因素可能会随时间而变化。
|
||||
|
||||
如果你可以通过加倍资源来处理双倍的负载,同时保持性能不变,我们就说你实现了*线性可扩展性*,这被认为是一件好事。偶尔也可能通过不到双倍的资源来处理双倍的负载,这得益于规模经济或更好的高峰负载分配[[71](ch02.html#Warfield2023),[72](ch02.html#Brooker2023)]。更常见的情况是,成本增长超过线性,可能有许多原因导致这种低效。例如,如果你有大量数据,那么处理单个写请求可能涉及的工作量比你的数据量小的时候要多,即使请求的大小相同。
|
||||
|
||||
First, we need to succinctly describe the current load on the system; only then can we discuss growth questions (what happens if our load doubles?). Often this will be a measure of throughput: for example, the number of requests per second to a service, how many gigabytes of new data arrive per day, or the number of shopping cart checkouts per hour. Sometimes you care about the peak of some variable quantity, such as the number of simultaneously online users in [“Case Study: Social Network Home Timelines”](ch02.html#sec_introduction_twitter).
|
||||
|
||||
Often there are other statistical characteristics of the load that also affect the access patterns and hence the scalability requirements. For example, you may need to know the ratio of reads to writes in a database, the hit rate on a cache, or the number of data items per user (for example, the number of followers in the social network case study). Perhaps the average case is what matters for you, or perhaps your bottleneck is dominated by a small number of extreme cases. It all depends on the details of your particular application.
|
||||
|
||||
Once you have described the load on your system, you can investigate what happens when the load increases. You can look at it in two ways:
|
||||
|
||||
- When you increase the load in a certain way and keep the system resources (CPUs, memory, network bandwidth, etc.) unchanged, how is the performance of your system affected?
|
||||
- When you increase the load in a certain way, how much do you need to increase the resources if you want to keep performance unchanged?
|
||||
|
||||
Usually our goal is to keep the performance of the system within the requirements of the SLA (see [“Use of Response Time Metrics”](ch02.html#sec_introduction_slo_sla)) while also minimizing the cost of running the system. The greater the required computing resources, the higher the cost. It might be that some types of hardware are more cost-effective than others, and these factors may change over time as new types of hardware become available.
|
||||
|
||||
If you can double the resources in order to handle twice the load, while keeping performance the same, we say that you have *linear scalability*, and this is considered a good thing. Occasionally it is possible to handle twice the load with less than double the resources, due to economies of scale or a better distribution of peak load [[71](ch02.html#Warfield2023), [72](ch02.html#Brooker2023)]. Much more likely is that the cost grows faster than linearly, and there may be many reasons for the inefficiency. For example, if you have a lot of data, then processing a single write request may involve more work than if you have a small amount of data, even if the size of the request is the same.
|
||||
|
||||
### 共享内存,共享磁盘,无共享架构
|
||||
|
||||
增加服务的硬件资源最简单的方式是将其迁移到更强大的机器上。单个CPU核心的速度不再显著提升,但您可以购买(或租用云实例)一个拥有更多CPU核心、更多RAM和更多磁盘空间的机器。这种方法被称为*垂直扩展*或*向上扩展*。
|
||||
|
||||
在单台机器上,您可以通过使用多个进程或线程来实现并行性。属于同一进程的所有线程可以访问同一RAM,因此这种方法也被称为*共享内存架构*。共享内存方法的问题在于成本增长超过线性:拥有双倍硬件资源的高端机器通常的成本显著高于两倍。而且由于瓶颈,一台规模加倍的机器往往处理的负载不到两倍。
|
||||
|
||||
另一种方法是*共享磁盘架构*,它使用多台拥有独立CPU和RAM的机器,但将数据存储在一个磁盘阵列上,这些磁盘阵列在机器之间通过快速网络共享:*网络附加存储*(NAS)或*存储区域网络*(SAN)。这种架构传统上用于本地数据仓库工作负载,但争用和锁定开销限制了共享磁盘方法的可扩展性[[73](ch02.html#Stopford2009)]。
|
||||
|
||||
相比之下,*无共享架构* [[74](ch02.html#Stonebraker1986)](也称为*水平扩展*或*向外扩展*)获得了很大的流行。在这种方法中,我们使用一个具有多个节点的分布式系统,每个节点都拥有自己的CPU、RAM和磁盘。节点之间的任何协调都在软件层面通过常规网络完成。
|
||||
|
||||
无共享的优势在于它有潜力线性扩展,它可以使用提供最佳价格/性能比的任何硬件(特别是在云中),它可以随着负载的增减更容易地调整其硬件资源,并且通过在多个数据中心和地区分布系统,它可以实现更大的容错性。缺点是它需要显式的数据分区(见[链接即将到来]),并且带来了分布式系统的所有复杂性(见[链接即将到来])。
|
||||
|
||||
一些云原生数据库系统使用独立的服务来执行存储和事务处理(见[“存储与计算的分离”](ch01.html#sec_introduction_storage_compute)),多个计算节点共享访问同一个存储服务。这种模型与共享磁盘架构有些相似,但它避免了旧系统的可扩展性问题:存储服务不提供文件系统(NAS)或块设备(SAN)抽象,而是提供了专门为数据库需求设计的专用API[[75](ch02.html#Antonopoulos2019_ch2)]。
|
||||
|
||||
The simplest way of increasing the hardware resources of a service is to move it to a more powerful machine. Individual CPU cores are no longer getting significantly faster, but you can buy a machine (or rent a cloud instance) with more CPU cores, more RAM, and more disk space. This approach is called *vertical scaling* or *scaling up*.
|
||||
|
||||
You can get parallelism on a single machine by using multiple processes or threads. All the threads belonging to the same process can access the same RAM, and hence this approach is also called a *shared-memory architecture*. The problem with a shared-memory approach is that the cost grows faster than linearly: a high-end machine with twice the hardware resources typically costs significantly more than twice as much. And due to bottlenecks, a machine twice the size can often handle less than twice the load.
|
||||
|
||||
Another approach is the *shared-disk architecture*, which uses several machines with independent CPUs and RAM, but which stores data on an array of disks that is shared between the machines, which are connected via a fast network: *Network-Attached Storage* (NAS) or *Storage Area Network* (SAN). This architecture has traditionally been used for on-premises data warehousing workloads, but contention and the overhead of locking limit the scalability of the shared-disk approach [[73](ch02.html#Stopford2009)].
|
||||
|
||||
By contrast, the *shared-nothing architecture* [[74](ch02.html#Stonebraker1986)] (also called *horizontal scaling* or *scaling out*) has gained a lot of popularity. In this approach, we use a distributed system with multiple nodes, each of which has its own CPUs, RAM, and disks. Any coordination between nodes is done at the software level, via a conventional network.
|
||||
|
||||
The advantages of shared-nothing are that it has the potential to scale linearly, it can use whatever hardware offers the best price/performance ratio (especially in the cloud), it can more easily adjust its hardware resources as load increases or decreases, and it can achieve greater fault tolerance by distributing the system across multiple data centers and regions. The downsides are that it requires explicit data partitioning (see [Link to Come]), and it incurs all the complexity of distributed systems ([Link to Come]).
|
||||
|
||||
Some cloud-native database systems use separate services for storage and transaction execution (see [“Separation of storage and compute”](ch01.html#sec_introduction_storage_compute)), with multiple compute nodes sharing access to the same storage service. This model has some similarity to a shared-disk architecture, but it avoids the scalability problems of older systems: instead of providing a filesystem (NAS) or block device (SAN) abstraction, the storage service offers a specialized API that is designed for the specific needs of the database [[75](ch02.html#Antonopoulos2019_ch2)].
|
||||
|
||||
|
||||
|
||||
### 可伸缩性原则
|
||||
|
||||
在大规模运行的系统架构通常高度特定于应用——没有所谓的通用、一刀切的可扩展架构(非正式称为*魔法扩展酱*)。例如,一个设计为每秒处理100,000个请求,每个请求1 kB大小的系统,与一个设计为每分钟处理3个请求,每个请求2 GB大小的系统看起来完全不同——尽管这两个系统有相同的数据吞吐量(100 MB/秒)。
|
||||
|
||||
此外,适用于某一负载水平的架构不太可能应对10倍的负载。因此,如果您正在处理一个快速增长的服务,很可能您需要在每个数量级负载增加时重新思考您的架构。由于应用的需求可能会发展变化,通常不值得提前超过一个数量级来规划未来的扩展需求。
|
||||
|
||||
一个关于可扩展性的好的一般原则是将系统分解成可以相对独立运行的小组件。这是微服务背后的基本原则(见[“微服务与无服务器”](ch01.html#sec_introduction_microservices))、分区([链接即将到来])、流处理([链接即将到来])和无共享架构。然而,挑战在于知道在应该在一起的事物和应该分开的事物之间划线的位置。关于微服务的设计指南可以在其他书籍中找到[[76](ch02.html#Newman2021_ch2)],我们将在[链接即将到来]中讨论无共享系统的分区。
|
||||
|
||||
另一个好的原则是不要让事情变得比必要的更复杂。如果单机数据库可以完成工作,它可能比复杂的分布式设置更可取。自动扩展系统(根据需求自动增加或减少资源)很酷,但如果您的负载相当可预测,手动扩展的系统可能会有更少的运营惊喜(见[链接即将到来])。一个拥有五个服务的系统比拥有五十个服务的系统简单。好的架构通常涉及到方法的实用混合。
|
||||
|
||||
|
||||
The architecture of systems that operate at large scale is usually highly specific to the application—there is no such thing as a generic, one-size-fits-all scalable architecture (informally known as *magic scaling sauce*). For example, a system that is designed to handle 100,000 requests per second, each 1 kB in size, looks very different from a system that is designed for 3 requests per minute, each 2 GB in size—even though the two systems have the same data throughput (100 MB/sec).
|
||||
|
||||
Moreover, an architecture that is appropriate for one level of load is unlikely to cope with 10 times that load. If you are working on a fast-growing service, it is therefore likely that you will need to rethink your architecture on every order of magnitude load increase. As the needs of the application are likely to evolve, it is usually not worth planning future scaling needs more than one order of magnitude in advance.
|
||||
|
||||
A good general principle for scalability is to break a system down into smaller components that can operate largely independently from each other. This is the underlying principle behind microservices (see [“Microservices and Serverless”](ch01.html#sec_introduction_microservices)), partitioning ([Link to Come]), stream processing ([Link to Come]), and shared-nothing architectures. However, the challenge is in knowing where to draw the line between things that should be together, and things that should be apart. Design guidelines for microservices can be found in other books [[76](ch02.html#Newman2021_ch2)], and we discuss partitioning of shared-nothing systems in [Link to Come].
|
||||
|
||||
Another good principle is not to make things more complicated than necessary. If a single-machine database will do the job, it’s probably preferable to a complicated distributed setup. Auto-scaling systems (which automatically add or remove resources in response to demand) are cool, but if your load is fairly predictable, a manually scaled system may have fewer operational surprises (see [Link to Come]). A system with five services is simpler than one with fifty. Good architectures usually involve a pragmatic mixture of approaches.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 可维护性
|
||||
|
||||
软件不会磨损或遭受材料疲劳,因此它的损坏方式与机械物体不同。但应用程序的需求经常变化,软件运行的环境也在变化(如其依赖关系和底层平台),并且它有需要修复的错误。
|
||||
|
||||
广泛认为,软件的大部分成本不在于初始开发,而在于持续的维护——修复错误、保持系统运行、调查故障、适应新平台、针对新用例修改软件、偿还技术债务以及添加新功能[77,78]。
|
||||
|
||||
然而,维护也很困难。如果一个系统已经成功运行很长时间,它可能会使用一些今天很少有工程师理解的过时技术(如大型机和COBOL代码);随着人员离职,关于系统如何以及为什么以某种方式设计的机构知识可能已经丢失;可能需要修复其他人的错误。此外,计算机系统往往与它支持的人类组织交织在一起,这意味着维护这种遗留系统既是一个人的问题也是一个技术问题[79]。
|
||||
|
||||
如果一个系统足够有价值,能长时间存活,我们今天创建的每个系统终将成为遗留系统。为了最小化未来维护我们软件的后代所承受的痛苦,我们应当在设计时考虑维护问题。虽然我们无法总是预测哪些决策将在未来造成维护难题,但在本书中,我们将关注几个广泛适用的原则:
|
||||
|
||||
Software does not wear out or suffer material fatigue, so it does not break in the same ways as mechanical objects do. But the requirements for an application frequently change, the environment that the software runs in changes (such as its dependencies and the underlying platform), and it has bugs that need fixing.
|
||||
|
||||
It is widely recognized that the majority of the cost of software is not in its initial development, but in its ongoing maintenance—fixing bugs, keeping its systems operational, investigating failures, adapting it to new platforms, modifying it for new use cases, repaying technical debt, and adding new features [[77](ch02.html#Ensmenger2016), [78](ch02.html#Glass2002)].
|
||||
|
||||
However, maintenance is also difficult. If a system has been successfully running for a long time, it may well use outdated technologies that not many engineers understand today (such as mainframes and COBOL code); institutional knowledge of how and why a system was designed in a certain way may have been lost as people have left the organization; it might be necessary to fix other people’s mistakes. Moreover, the computer system is often intertwined with the human organization that it supports, which means that maintenance of such *legacy* systems is as much a people problem as a technical one [[79](ch02.html#Bellotti2021)].
|
||||
|
||||
Every system we create today will one day become a legacy system if it is valuable enough to survive for a long time. In order to minimize the pain for future generations who need to maintain our software, we should design it with maintenance concerns in mind. Although we cannot always predict which decisions might create maintenance headaches in the future, in this book we will pay attention to several principles that are widely applicable:
|
||||
|
||||
* 可操作性(Operability)
|
||||
|
||||
便于运维团队保持系统平稳运行。
|
||||
|
||||
* 简单性(Simplicity)
|
||||
|
||||
让新工程师也能轻松理解系统 —— 通过使用众所周知、协调一致的模式和结构来实现系统,并避免不必要的**复杂性(Complexity)**。
|
||||
|
||||
* 可演化性(Evolvability)
|
||||
|
||||
使工程师能够轻松地对系统进行改造,并在未来出现需求变化时,能使其适应和扩展到新的应用场景中。
|
||||
|
||||
|
||||
|
||||
### 可操作性:人生苦短,关爱运维
|
||||
|
||||
我们先前在[云时代的运营](/v2/ch1#在云时代的运营)中讨论过运维的角色,不难发现在这个过程中人类扮演的角色至少也是与工具一样重要的。 实际上有人认为,“良好的运维经常可以绕开垃圾(或不完整)软件的局限性,而再好的软件摊上垃圾运维也没法可靠运行”。尽管运维的某些方面可以,而且应该是自动化的,但在最初建立正确运作的自动化机制仍然取决于人。
|
||||
|
||||
运维团队对于保持软件系统顺利运行至关重要。一个优秀运维团队的典型职责如下(或者更多)【29】:
|
||||
|
||||
* 监控系统的运行状况,并在服务状态不佳时快速恢复服务。
|
||||
* 跟踪问题的原因,例如系统故障或性能下降。
|
||||
* 及时更新软件和平台,比如安全补丁。
|
||||
* 了解系统间的相互作用,以便在异常变更造成损失前进行规避。
|
||||
* 预测未来的问题,并在问题出现之前加以解决(例如,容量规划)。
|
||||
* 建立部署、配置、管理方面的良好实践,编写相应工具。
|
||||
* 执行复杂的维护任务,例如将应用程序从一个平台迁移到另一个平台。
|
||||
* 当配置变更时,维持系统的安全性。
|
||||
* 定义工作流程,使运维操作可预测,并保持生产环境稳定。
|
||||
* 铁打的营盘流水的兵,维持组织对系统的了解。
|
||||
|
||||
良好的可操作性意味着更轻松的日常工作,进而运维团队能专注于高价值的事情。数据系统可以通过各种方式使日常任务更轻松:
|
||||
|
||||
* 通过良好的监控,提供对系统内部状态和运行时行为的 **可见性(visibility)**。
|
||||
* 为自动化提供良好支持,将系统与标准化工具相集成。
|
||||
* 避免依赖单台机器(在整个系统继续不间断运行的情况下允许机器停机维护)。
|
||||
* 提供良好的文档和易于理解的操作模型(“如果做 X,会发生 Y”)。
|
||||
* 提供良好的默认行为,但需要时也允许管理员自由覆盖默认值。
|
||||
* 有条件时进行自我修复,但需要时也允许管理员手动控制系统状态。
|
||||
* 行为可预测,最大限度减少意外。
|
||||
|
||||
We previously discussed the role of operations in [“Operations in the Cloud Era”](ch01.html#sec_introduction_operations), and we saw that human processes are at least as important for reliable operations as software tools. In fact, it has been suggested that “good operations can often work around the limitations of bad (or incomplete) software, but good software cannot run reliably with bad operations” [[54](ch02.html#Kreps2012_ch1)].
|
||||
|
||||
In large-scale systems consisting of many thousands of machines, manual maintenance would be unreasonably expensive, and automation is essential. However, automation can be a two-edged sword: there will always be edge cases (such as rare failure scenarios) that require manual intervention from the operations team. Since the cases that cannot be handled automatically are the most complex issues, greater automation requires a *more* skilled operations team that can resolve those issues [[80](ch02.html#Bainbridge1983)].
|
||||
|
||||
Moreover, if an automated system goes wrong, it is often harder to troubleshoot than a system that relies on an operator to perform some actions manually. For that reason, it is not the case that more automation is always better for operability. However, some amount of automation is important, and the sweet spot will depend on the specifics of your particular application and organization.
|
||||
|
||||
Good operability means making routine tasks easy, allowing the operations team to focus their efforts on high-value activities. Data systems can do various things to make routine tasks easy, including [[81](ch02.html#Hamilton2007)]:
|
||||
|
||||
- Allowing monitoring tools to check the system’s key metrics, and supporting observability tools (see [“Problems with Distributed Systems”](ch01.html#sec_introduction_dist_sys_problems)) to give insights into the system’s runtime behavior. A variety of commercial and open source tools can help here [[82](ch02.html#Horovits2021)].
|
||||
- Avoiding dependency on individual machines (allowing machines to be taken down for maintenance while the system as a whole continues running uninterrupted)
|
||||
- Providing good documentation and an easy-to-understand operational model (“If I do X, Y will happen”)
|
||||
- Providing good default behavior, but also giving administrators the freedom to override defaults when needed
|
||||
- Self-healing where appropriate, but also giving administrators manual control over the system state when needed
|
||||
- Exhibiting predictable behavior, minimizing surprises
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 简单性:管理复杂度
|
||||
|
||||
小型软件项目可以使用简单讨喜的、富表现力的代码,但随着项目越来越大,代码往往变得非常复杂,难以理解。这种复杂度拖慢了所有系统相关人员,进一步增加了维护成本。一个陷入复杂泥潭的软件项目有时被描述为 **烂泥潭(a big ball of mud)** 【30】。
|
||||
|
||||
**复杂度(complexity)** 有各种可能的症状,例如:状态空间激增、模块间紧密耦合、纠结的依赖关系、不一致的命名和术语、解决性能问题的 Hack、需要绕开的特例等等,现在已经有很多关于这个话题的讨论【31,32,33】。
|
||||
|
||||
因为复杂度导致维护困难时,预算和时间安排通常会超支。在复杂的软件中进行变更,引入错误的风险也更大:当开发人员难以理解系统时,隐藏的假设、无意的后果和意外的交互就更容易被忽略。相反,降低复杂度能极大地提高软件的可维护性,因此简单性应该是构建系统的一个关键目标。
|
||||
|
||||
简化系统并不一定意味着减少功能;它也可以意味着消除 **额外的(accidental)** 的复杂度。Moseley 和 Marks【32】把 **额外复杂度** 定义为:由具体实现中涌现,而非(从用户视角看,系统所解决的)问题本身固有的复杂度。
|
||||
|
||||
用于消除 **额外复杂度** 的最好工具之一是 **抽象(abstraction)**。一个好的抽象可以将大量实现细节隐藏在一个干净,简单易懂的外观下面。一个好的抽象也可以广泛用于各类不同应用。比起重复造很多轮子,重用抽象不仅更有效率,而且有助于开发高质量的软件。抽象组件的质量改进将使所有使用它的应用受益。
|
||||
|
||||
例如,高级编程语言是一种抽象,隐藏了机器码、CPU 寄存器和系统调用。SQL 也是一种抽象,隐藏了复杂的磁盘 / 内存数据结构、来自其他客户端的并发请求、崩溃后的不一致性。当然在用高级语言编程时,我们仍然用到了机器码;只不过没有 **直接(directly)** 使用罢了,正是因为编程语言的抽象,我们才不必去考虑这些实现细节。
|
||||
|
||||
抽象可以帮助我们将系统的复杂度控制在可管理的水平,不过,找到好的抽象是非常困难的。在分布式系统领域虽然有许多好的算法,但我们并不清楚它们应该打包成什么样抽象。
|
||||
|
||||
本书将紧盯那些允许我们将大型系统的部分提取为定义明确的、可重用的组件的优秀抽象。
|
||||
|
||||
Small software projects can have delightfully simple and expressive code, but as projects get larger, they often become very complex and difficult to understand. This complexity slows down everyone who needs to work on the system, further increasing the cost of maintenance. A software project mired in complexity is sometimes described as a *big ball of mud* [[83](ch02.html#Foote1997)].
|
||||
|
||||
When complexity makes maintenance hard, budgets and schedules are often overrun. In complex software, there is also a greater risk of introducing bugs when making a change: when the system is harder for developers to understand and reason about, hidden assumptions, unintended consequences, and unexpected interactions are more easily overlooked [[62](ch02.html#Woods2017)]. Conversely, reducing complexity greatly improves the maintainability of software, and thus simplicity should be a key goal for the systems we build.
|
||||
|
||||
Simple systems are easier to understand, and therefore we should try to solve a given problem in the simplest way possible. Unfortunately, this is easier said than done. Whether something is simple or not is often a subjective matter of taste, as there is no objective standard of simplicity [[84](ch02.html#Brooker2022)]. For example, one system may hide a complex implementation behind a simple interface, whereas another may have a simple implementation that exposes more internal detail to its users—which one is simpler?
|
||||
|
||||
One attempt at reasoning about complexity has been to break it down into two categories, *essential* and *accidental* complexity [[85](ch02.html#Brooks1995)]. The idea is that essential complexity is inherent in the problem domain of the application, while accidental complexity arises only because of limitations of our tooling. Unfortunately, this distinction is also flawed, because boundaries between the essential and the accidental shift as our tooling evolves [[86](ch02.html#Luu2020)].
|
||||
|
||||
One of the best tools we have for managing complexity is *abstraction*. A good abstraction can hide a great deal of implementation detail behind a clean, simple-to-understand façade. A good abstraction can also be used for a wide range of different applications. Not only is this reuse more efficient than reimplementing a similar thing multiple times, but it also leads to higher-quality software, as quality improvements in the abstracted component benefit all applications that use it.
|
||||
|
||||
For example, high-level programming languages are abstractions that hide machine code, CPU registers, and syscalls. SQL is an abstraction that hides complex on-disk and in-memory data structures, concurrent requests from other clients, and inconsistencies after crashes. Of course, when programming in a high-level language, we are still using machine code; we are just not using it *directly*, because the programming language abstraction saves us from having to think about it.
|
||||
|
||||
Abstractions for application code, which aim to reduce its complexity, can be created using methodologies such as *design patterns* [[87](ch02.html#Gamma1994)] and *domain-driven design* (DDD) [[88](ch02.html#Evans2003)]. This book is not about such application-specific abstractions, but rather about general-purpose abstractions on top of which you can build your applications, such as database transactions, indexes, and event logs. If you want to use techniques such as DDD, you can implement them on top of the foundations described in this book.
|
||||
|
||||
### 可演化性:让变更更容易
|
||||
|
||||
系统的需求永远不变,基本是不可能的。更可能的情况是,它们处于常态的变化中,例如:你了解了新的事实、出现意想不到的应用场景、业务优先级发生变化、用户要求新功能、新平台取代旧平台、法律或监管要求发生变化、系统增长迫使架构变化等。
|
||||
|
||||
在组织流程方面,**敏捷(agile)** 工作模式为适应变化提供了一个框架。敏捷社区还开发了对在频繁变化的环境中开发软件很有帮助的技术工具和模式,如 **测试驱动开发(TDD, test-driven development)** 和 **重构(refactoring)** 。
|
||||
|
||||
这些敏捷技术的大部分讨论都集中在相当小的规模(同一个应用中的几个代码文件)。本书将探索在更大数据系统层面上提高敏捷性的方法,可能由几个不同的应用或服务组成。例如,为了将装配主页时间线的方法从方法 1 变为方法 2,你会如何 “重构” 推特的架构 ?
|
||||
|
||||
修改数据系统并使其适应不断变化需求的容易程度,是与 **简单性** 和 **抽象性** 密切相关的:简单易懂的系统通常比复杂系统更容易修改。但由于这是一个非常重要的概念,我们将用一个不同的词来指代数据系统层面的敏捷性: **可演化性(evolvability)** 【34】。
|
||||
|
||||
|
||||
It’s extremely unlikely that your system’s requirements will remain unchanged forever. They are much more likely to be in constant flux: you learn new facts, previously unanticipated use cases emerge, business priorities change, users request new features, new platforms replace old platforms, legal or regulatory requirements change, growth of the system forces architectural changes, etc.
|
||||
|
||||
In terms of organizational processes, *Agile* working patterns provide a framework for adapting to change. The Agile community has also developed technical tools and processes that are helpful when developing software in a frequently changing environment, such as test-driven development (TDD) and refactoring. In this book, we search for ways of increasing agility at the level of a system consisting of several different applications or services with different characteristics.
|
||||
|
||||
The ease with which you can modify a data system, and adapt it to changing requirements, is closely linked to its simplicity and its abstractions: simple and easy-to-understand systems are usually easier to modify than complex ones. Since this is such an important idea, we will use a different word to refer to agility on a data system level: *evolvability* [[89](ch02.html#Breivold2008)].
|
||||
|
||||
One major factor that makes change difficult in large systems is when some action is irreversible, and therefore that action needs to be taken very carefully [[90](ch02.html#Zaninotto2002)]. For example, say you are migrating from one database to another: if you cannot switch back to the old system in case of problems wth the new one, the stakes are much higher than if you can easily go back. Minimizing irreversibility improves flexibility.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 本章小结
|
||||
|
||||
在本章中,我们检查了几个非功能性需求的示例:性能、可靠性、可扩展性和可维护性。通过这些话题,我们还遇到了我们在本书其余部分将需要的原则和术语。我们从一个案例研究开始,探讨了如何在社交网络中实现首页时间线,这展示了在规模扩大时可能出现的一些挑战。
|
||||
|
||||
我们讨论了如何测量性能(例如,使用响应时间百分位数)、系统负载(例如,使用吞吐量指标),以及它们如何在SLA中使用。可扩展性是一个密切相关的概念:即确保在负载增长时性能保持不变。我们看到了一些可扩展性的一般原则,如将任务分解成可以独立操作的小部分,并将在后续章节中深入技术细节探讨可扩展性技术。
|
||||
|
||||
为了实现可靠性,您可以使用容错技术,即使系统的某个组件(例如,磁盘、机器或其他服务)出现故障,也能继续提供服务。我们看到了可能发生的硬件故障示例,并将其与软件故障区分开来,后者可能更难处理,因为它们往往具有强相关性。实现可靠性的另一个方面是构建对人为错误的抵抗力,我们看到了无责任事故报告作为从事件中学习的一种技术。
|
||||
|
||||
最后,我们检查了几个维护性的方面,包括支持运营团队的工作、管理复杂性,以及使应用功能随时间易于演进。关于如何实现这些目标没有简单的答案,但有一件事可以帮助,那就是使用提供有用抽象的、众所周知的构建块来构建应用程序。本书的其余部分将介绍一些最重要的这类构建块。
|
||||
|
||||
In this chapter we examined several examples of nonfunctional requirements: performance, reliability, scalability, and maintainability. Through these topics we have also encountered principles and terminology that we will need throughout the rest of the book. We started with a case study of how one might implement home timelines in a social network, which illustrated some of the challenges that arise at scale.
|
||||
|
||||
We discussed how to measure performance (e.g., using response time percentiles), the load on a system (e.g., using throughput metrics), and how they are used in SLAs. Scalability is a closely related concept: that is, ensuring performance stays the same when the load grows. We saw some general principles for scalability, such as breaking a task down into smaller parts that can operate independently, and we will dive into deep technical detail on scalability techniques in the following chapters.
|
||||
|
||||
To achieve reliability, you can use fault tolerance techniques, which allow a system to continue providing its service even if some component (e.g., a disk, a machine, or another service) is faulty. We saw examples of hardware faults that can occur, and distinguished them from software faults, which can be harder to deal with because they are often strongly correlated. Another aspect of achieving reliability is to build resilience against humans making mistakes, and we saw blameless postmortems as a technique for learning from incidents.
|
||||
|
||||
Finally, we examined several facets of maintainability, including supporting the work of operations teams, managing complexity, and making it easy to evolve an application’s functionality over time. There are no easy answers on how to achieve these things, but one thing that can help is to build applications using well-understood building blocks that provide useful abstractions. The rest of this book will cover a selection of the most important such building blocks.
|
||||
|
||||
|
||||
|
||||
--------
|
||||
|
||||
## 参考文献
|
||||
|
||||
[[1](ch02.html#Cvet2016-marker)] Mike Cvet. [How We Learned to Stop Worrying and Love Fan-In at Twitter](https://www.youtube.com/watch?v=WEgCjwyXvwc). At *QCon San Francisco*, December 2016.
|
||||
|
||||
[[2](ch02.html#Krikorian2012_ch2-marker)] Raffi Krikorian. [Timelines at Scale](http://www.infoq.com/presentations/Twitter-Timeline-Scalability). At *QCon San Francisco*, November 2012. Archived at [perma.cc/V9G5-KLYK](https://perma.cc/V9G5-KLYK)
|
||||
|
||||
[[3](ch02.html#Twitter2023-marker)] Twitter. [Twitter’s Recommendation Algorithm](https://blog.twitter.com/engineering/en_us/topics/open-source/2023/twitter-recommendation-algorithm). *blog.twitter.com*, March 2023. Archived at [perma.cc/L5GT-229T](https://perma.cc/L5GT-229T)
|
||||
|
||||
[[4](ch02.html#Krikorian2013-marker)] Raffi Krikorian. [New Tweets per second record, and how!](https://blog.twitter.com/engineering/en_us/a/2013/new-tweets-per-second-record-and-how) *blog.twitter.com*, August 2013. Archived at [perma.cc/6JZN-XJYN](https://perma.cc/6JZN-XJYN)
|
||||
|
||||
[[5](ch02.html#Axon2010_ch2-marker)] Samuel Axon. [3% of Twitter’s Servers Dedicated to Justin Bieber](http://mashable.com/2010/09/07/justin-bieber-twitter/). *mashable.com*, September 2010. Archived at [perma.cc/F35N-CGVX](https://perma.cc/F35N-CGVX)
|
||||
|
||||
[[6](ch02.html#Bronson2021-marker)] Nathan Bronson, Abutalib Aghayev, Aleksey Charapko, and Timothy Zhu. [Metastable Failures in Distributed Systems](https://sigops.org/s/conferences/hotos/2021/papers/hotos21-s11-bronson.pdf). At *Workshop on Hot Topics in Operating Systems* (HotOS), May 2021. [doi:10.1145/3458336.3465286](https://doi.org/10.1145/3458336.3465286)
|
||||
|
||||
[[7](ch02.html#Brooker2021-marker)] Marc Brooker. [Metastability and Distributed Systems](https://brooker.co.za/blog/2021/05/24/metastable.html). *brooker.co.za*, May 2021. Archived at [archive.org](https://web.archive.org/web/20230324043015/https://brooker.co.za/blog/2021/05/24/metastable.html)
|
||||
|
||||
[[8](ch02.html#Brooker2015-marker)] Marc Brooker. [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/). *aws.amazon.com*, March 2015. Archived at [perma.cc/R6MS-AZKH](https://perma.cc/R6MS-AZKH)
|
||||
|
||||
[[9](ch02.html#Brooker2022backoff-marker)] Marc Brooker. [What is Backoff For?](https://brooker.co.za/blog/2022/08/11/backoff.html) *brooker.co.za*, August 2022. Archived at [archive.org](https://web.archive.org/web/20230331022111/https://brooker.co.za/blog/2022/08/11/backoff.html)
|
||||
|
||||
[[10](ch02.html#Nygard2018-marker)] Michael T. Nygard. [*Release It!*](https://learning.oreilly.com/library/view/release-it-2nd/9781680504552/), 2nd Edition. Pragmatic Bookshelf, January 2018. ISBN: 9781680502398
|
||||
|
||||
[[11](ch02.html#Brooker2022retries-marker)] Marc Brooker. [Fixing retries with token buckets and circuit breakers](https://brooker.co.za/blog/2022/02/28/retries.html). *brooker.co.za*, February 2022. Archived at [archive.org](https://web.archive.org/web/20230325195445/https://brooker.co.za/blog/2022/02/28/retries.html)
|
||||
|
||||
[[12](ch02.html#YanacekLoadShedding-marker)] David Yanacek. [Using load shedding to avoid overload](https://aws.amazon.com/builders-library/using-load-shedding-to-avoid-overload/). Amazon Builders’ Library, *aws.amazon.com*. Archived at [perma.cc/9SAW-68MP](https://perma.cc/9SAW-68MP)
|
||||
|
||||
[[13](ch02.html#Sackman2016_ch2-marker)] Matthew Sackman. [Pushing Back](https://wellquite.org/posts/lshift/pushing_back/). *wellquite.org*, May 2016. Archived at [perma.cc/3KCZ-RUFY](https://perma.cc/3KCZ-RUFY)
|
||||
|
||||
[[14](ch02.html#Kopytkov2018-marker)] Dmitry Kopytkov and Patrick Lee. [Meet Bandaid, the Dropbox service proxy](https://dropbox.tech/infrastructure/meet-bandaid-the-dropbox-service-proxy). *dropbox.tech*, March 2018. Archived at [perma.cc/KUU6-YG4S](https://perma.cc/KUU6-YG4S)
|
||||
|
||||
[[15](ch02.html#Gunawi2018-marker)] Haryadi S. Gunawi, Riza O. Suminto, Russell Sears, Casey Golliher, Swaminathan Sundararaman, Xing Lin, Tim Emami, Weiguang Sheng, Nematollah Bidokhti, Caitie McCaffrey, Gary Grider, Parks M. Fields, Kevin Harms, Robert B. Ross, Andree Jacobson, Robert Ricci, Kirk Webb, Peter Alvaro, H. Birali Runesha, Mingzhe Hao, and Huaicheng Li. [Fail-Slow at Scale: Evidence of Hardware Performance Faults in Large Production Systems](https://www.usenix.org/system/files/conference/fast18/fast18-gunawi.pdf). At *16th USENIX Conference on File and Storage Technologies*, February 2018.
|
||||
|
||||
[[16](ch02.html#DeCandia2007_ch1-marker)] Giuseppe DeCandia, Deniz Hastorun, Madan Jampani, Gunavardhan Kakulapati, Avinash Lakshman, Alex Pilchin, Swaminathan Sivasubramanian, Peter Vosshall, and Werner Vogels. [Dynamo: Amazon’s Highly Available Key-Value Store](http://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf). At *21st ACM Symposium on Operating Systems Principles* (SOSP), October 2007. [doi:10.1145/1294261.1294281](https://doi.org/10.1145/1294261.1294281)
|
||||
|
||||
[[17](ch02.html#Whitenton2020-marker)] Kathryn Whitenton. [The Need for Speed, 23 Years Later](https://www.nngroup.com/articles/the-need-for-speed/). *nngroup.com*, May 2020. Archived at [perma.cc/C4ER-LZYA](https://perma.cc/C4ER-LZYA)
|
||||
|
||||
[[18](ch02.html#Linden2006-marker)] Greg Linden. [Marissa Mayer at Web 2.0](https://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html). *glinden.blogspot.com*, November 2005. Archived at [perma.cc/V7EA-3VXB](https://perma.cc/V7EA-3VXB)
|
||||
|
||||
[[19](ch02.html#Brutlag2009-marker)] Jake Brutlag. [Speed Matters for Google Web Search](https://services.google.com/fh/files/blogs/google_delayexp.pdf). *services.google.com*, June 2009. Archived at [perma.cc/BK7R-X7M2](https://perma.cc/BK7R-X7M2)
|
||||
|
||||
[[20](ch02.html#Schurman2009-marker)] Eric Schurman and Jake Brutlag. [Performance Related Changes and their User Impact](https://www.youtube.com/watch?v=bQSE51-gr2s). Talk at *Velocity 2009*.
|
||||
|
||||
[[21](ch02.html#Akamai2017-marker)] Akamai Technologies, Inc. [The State of Online Retail Performance](https://web.archive.org/web/20210729180749/https://www.akamai.com/us/en/multimedia/documents/report/akamai-state-of-online-retail-performance-spring-2017.pdf). *akamai.com*, April 2017. Archived at [perma.cc/UEK2-HYCS](https://perma.cc/UEK2-HYCS)
|
||||
|
||||
[[22](ch02.html#Bai2017-marker)] Xiao Bai, Ioannis Arapakis, B. Barla Cambazoglu, and Ana Freire. [Understanding and Leveraging the Impact of Response Latency on User Behaviour in Web Search](https://iarapakis.github.io/papers/TOIS17.pdf). *ACM Transactions on Information Systems*, volume 36, issue 2, article 21, April 2018. [doi:10.1145/3106372](https://doi.org/10.1145/3106372)
|
||||
|
||||
[[23](ch02.html#Dean2013-marker)] Jeffrey Dean and Luiz André Barroso. [The Tail at Scale](http://cacm.acm.org/magazines/2013/2/160173-the-tail-at-scale/fulltext). *Communications of the ACM*, volume 56, issue 2, pages 74–80, February 2013. [doi:10.1145/2408776.2408794](https://doi.org/10.1145/2408776.2408794)
|
||||
|
||||
[[24](ch02.html#Hidalgo2020-marker)] Alex Hidalgo. [*Implementing Service Level Objectives: A Practical Guide to SLIs, SLOs, and Error Budgets*](https://www.oreilly.com/library/view/implementing-service-level/9781492076803/). O’Reilly Media, September 2020. ISBN: 1492076813
|
||||
|
||||
[[25](ch02.html#Mogul2019-marker)] Jeffrey C. Mogul and John Wilkes. [Nines are Not Enough: Meaningful Metrics for Clouds](https://research.google/pubs/pub48033/). At *17th Workshop on Hot Topics in Operating Systems* (HotOS), May 2019. [doi:10.1145/3317550.3321432](https://doi.org/10.1145/3317550.3321432)
|
||||
|
||||
[[26](ch02.html#Hauer2020-marker)] Tamás Hauer, Philipp Hoffmann, John Lunney, Dan Ardelean, and Amer Diwan. [Meaningful Availability](https://www.usenix.org/conference/nsdi20/presentation/hauer). At *17th USENIX Symposium on Networked Systems Design and Implementation* (NSDI), February 2020.
|
||||
|
||||
[[27](ch02.html#Dunning2021-marker)] Ted Dunning. [The t-digest: Efficient estimates of distributions](https://www.sciencedirect.com/science/article/pii/S2665963820300403). *Software Impacts*, volume 7, article 100049, February 2021. [doi:10.1016/j.simpa.2020.100049](https://doi.org/10.1016/j.simpa.2020.100049)
|
||||
|
||||
[[28](ch02.html#Kohn2021-marker)] David Kohn. [How percentile approximation works (and why it’s more useful than averages)](https://www.timescale.com/blog/how-percentile-approximation-works-and-why-its-more-useful-than-averages/). *timescale.com*, September 2021. Archived at [perma.cc/3PDP-NR8B](https://perma.cc/3PDP-NR8B)
|
||||
|
||||
[[29](ch02.html#Hartmann2020-marker)] Heinrich Hartmann and Theo Schlossnagle. [Circllhist — A Log-Linear Histogram Data Structure for IT Infrastructure Monitoring](https://arxiv.org/pdf/2001.06561.pdf). *arxiv.org*, January 2020.
|
||||
|
||||
[[30](ch02.html#Masson2019-marker)] Charles Masson, Jee E. Rim, and Homin K. Lee. [DDSketch: A Fast and Fully-Mergeable Quantile Sketch with Relative-Error Guarantees](http://www.vldb.org/pvldb/vol12/p2195-masson.pdf). *Proceedings of the VLDB Endowment*, volume 12, issue 12, pages 2195–2205, August 2019. [doi:10.14778/3352063.3352135](https://doi.org/10.14778/3352063.3352135)
|
||||
|
||||
[[31](ch02.html#Schwartz2015-marker)] Baron Schwartz. [Why Percentiles Don’t Work the Way You Think](https://orangematter.solarwinds.com/2016/11/18/why-percentiles-dont-work-the-way-you-think/). *solarwinds.com*, November 2016. Archived at [perma.cc/469T-6UGB](https://perma.cc/469T-6UGB)
|
||||
|
||||
[[32](ch02.html#Heimerdinger1992-marker)] Walter L. Heimerdinger and Charles B. Weinstock. [A Conceptual Framework for System Fault Tolerance](https://resources.sei.cmu.edu/asset_files/TechnicalReport/1992_005_001_16112.pdf). Technical Report CMU/SEI-92-TR-033, Software Engineering Institute, Carnegie Mellon University, October 1992. Archived at [perma.cc/GD2V-DMJW](https://perma.cc/GD2V-DMJW)
|
||||
|
||||
[[33](ch02.html#Gaertner1999-marker)] Felix C. Gärtner. [Fundamentals of fault-tolerant distributed computing in asynchronous environments](https://dl.acm.org/doi/pdf/10.1145/311531.311532). *ACM Computing Surveys*, volume 31, issue 1, pages 1–26, March 1999. [doi:10.1145/311531.311532](https://doi.org/10.1145/311531.311532)
|
||||
|
||||
[[34](ch02.html#Yuan2014-marker)] Ding Yuan, Yu Luo, Xin Zhuang, Guilherme Renna Rodrigues, Xu Zhao, Yongle Zhang, Pranay U. Jain, and Michael Stumm. [Simple Testing Can Prevent Most Critical Failures: An Analysis of Production Failures in Distributed Data-Intensive Systems](https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf). At *11th USENIX Symposium on Operating Systems Design and Implementation* (OSDI), October 2014.
|
||||
|
||||
[[35](ch02.html#Rosenthal2020-marker)] Casey Rosenthal and Nora Jones. [*Chaos Engineering*](https://learning.oreilly.com/library/view/chaos-engineering/9781492043850/). O’Reilly Media, April 2020. ISBN: 9781492043867
|
||||
|
||||
[[36](ch02.html#Pinheiro2007-marker)] Eduardo Pinheiro, Wolf-Dietrich Weber, and Luiz Andre Barroso. [Failure Trends in a Large Disk Drive Population](https://www.usenix.org/legacy/events/fast07/tech/full_papers/pinheiro/pinheiro_old.pdf). At *5th USENIX Conference on File and Storage Technologies* (FAST), February 2007.
|
||||
|
||||
[[37](ch02.html#Schroeder2007-marker)] Bianca Schroeder and Garth A. Gibson. [Disk failures in the real world: What does an MTTF of 1,000,000 hours mean to you?](https://www.usenix.org/legacy/events/fast07/tech/schroeder/schroeder.pdf) At *5th USENIX Conference on File and Storage Technologies* (FAST), February 2007.
|
||||
|
||||
[[38](ch02.html#Klein2021-marker)] Andy Klein. [Backblaze Drive Stats for Q2 2021](https://www.backblaze.com/blog/backblaze-drive-stats-for-q2-2021/). *backblaze.com*, August 2021. Archived at [perma.cc/2943-UD5E](https://perma.cc/2943-UD5E)
|
||||
|
||||
[[39](ch02.html#Narayanan2016-marker)] Iyswarya Narayanan, Di Wang, Myeongjae Jeon, Bikash Sharma, Laura Caulfield, Anand Sivasubramaniam, Ben Cutler, Jie Liu, Badriddine Khessib, and Kushagra Vaid. [SSD Failures in Datacenters: What? When? and Why?](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/a7-narayanan.pdf) At *9th ACM International on Systems and Storage Conference* (SYSTOR), June 2016. [doi:10.1145/2928275.2928278](https://doi.org/10.1145/2928275.2928278)
|
||||
|
||||
[[40](ch02.html#Alibaba2019_ch2-marker)] Alibaba Cloud Storage Team. [Storage System Design Analysis: Factors Affecting NVMe SSD Performance (1)](https://www.alibabacloud.com/blog/594375). *alibabacloud.com*, January 2019. Archived at [archive.org](https://web.archive.org/web/20230522005034/https://www.alibabacloud.com/blog/594375)
|
||||
|
||||
[[41](ch02.html#Schroeder2016-marker)] Bianca Schroeder, Raghav Lagisetty, and Arif Merchant. [Flash Reliability in Production: The Expected and the Unexpected](https://www.usenix.org/system/files/conference/fast16/fast16-papers-schroeder.pdf). At *14th USENIX Conference on File and Storage Technologies* (FAST), February 2016.
|
||||
|
||||
[[42](ch02.html#Alter2019-marker)] Jacob Alter, Ji Xue, Alma Dimnaku, and Evgenia Smirni. [SSD failures in the field: symptoms, causes, and prediction models](https://dl.acm.org/doi/pdf/10.1145/3295500.3356172). At *International Conference for High Performance Computing, Networking, Storage and Analysis* (SC), November 2019. [doi:10.1145/3295500.3356172](https://doi.org/10.1145/3295500.3356172)
|
||||
|
||||
[[43](ch02.html#Ford2010-marker)] Daniel Ford, François Labelle, Florentina I. Popovici, Murray Stokely, Van-Anh Truong, Luiz Barroso, Carrie Grimes, and Sean Quinlan. [Availability in Globally Distributed Storage Systems](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Ford.pdf). At *9th USENIX Symposium on Operating Systems Design and Implementation* (OSDI), October 2010.
|
||||
|
||||
[[44](ch02.html#Vishwanath2010-marker)] Kashi Venkatesh Vishwanath and Nachiappan Nagappan. [Characterizing Cloud Computing Hardware Reliability](https://www.microsoft.com/en-us/research/wp-content/uploads/2010/06/socc088-vishwanath.pdf). At *1st ACM Symposium on Cloud Computing* (SoCC), June 2010. [doi:10.1145/1807128.1807161](https://doi.org/10.1145/1807128.1807161)
|
||||
|
||||
[[45](ch02.html#Hochschild2021-marker)] Peter H. Hochschild, Paul Turner, Jeffrey C. Mogul, Rama Govindaraju, Parthasarathy Ranganathan, David E. Culler, and Amin Vahdat. [Cores that don’t count](https://sigops.org/s/conferences/hotos/2021/papers/hotos21-s01-hochschild.pdf). At *Workshop on Hot Topics in Operating Systems* (HotOS), June 2021. [doi:10.1145/3458336.3465297](https://doi.org/10.1145/3458336.3465297)
|
||||
|
||||
[[46](ch02.html#Dixit2021-marker)] Harish Dattatraya Dixit, Sneha Pendharkar, Matt Beadon, Chris Mason, Tejasvi Chakravarthy, Bharath Muthiah, and Sriram Sankar. [Silent Data Corruptions at Scale](https://arxiv.org/abs/2102.11245). *arXiv:2102.11245*, February 2021.
|
||||
|
||||
[[47](ch02.html#Behrens2015-marker)] Diogo Behrens, Marco Serafini, Sergei Arnautov, Flavio P. Junqueira, and Christof Fetzer. [Scalable Error Isolation for Distributed Systems](https://www.usenix.org/conference/nsdi15/technical-sessions/presentation/behrens). At *12th USENIX Symposium on Networked Systems Design and Implementation* (NSDI), May 2015.
|
||||
|
||||
[[48](ch02.html#Schroeder2009-marker)] Bianca Schroeder, Eduardo Pinheiro, and Wolf-Dietrich Weber. [DRAM Errors in the Wild: A Large-Scale Field Study](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/35162.pdf). At *11th International Joint Conference on Measurement and Modeling of Computer Systems* (SIGMETRICS), June 2009. [doi:10.1145/1555349.1555372](https://doi.org/10.1145/1555349.1555372)
|
||||
|
||||
[[49](ch02.html#Kim2014-marker)] Yoongu Kim, Ross Daly, Jeremie Kim, Chris Fallin, Ji Hye Lee, Donghyuk Lee, Chris Wilkerson, Konrad Lai, and Onur Mutlu. [Flipping Bits in Memory Without Accessing Them: An Experimental Study of DRAM Disturbance Errors](https://users.ece.cmu.edu/~yoonguk/papers/kim-isca14.pdf). At *41st Annual International Symposium on Computer Architecture* (ISCA), June 2014. [doi:10.5555/2665671.2665726](https://doi.org/10.5555/2665671.2665726)
|
||||
|
||||
[[50](ch02.html#Cockcroft2019-marker)] Adrian Cockcroft. [Failure Modes and Continuous Resilience](https://adrianco.medium.com/failure-modes-and-continuous-resilience-6553078caad5). *adrianco.medium.com*, November 2019. Archived at [perma.cc/7SYS-BVJP](https://perma.cc/7SYS-BVJP)
|
||||
|
||||
[[51](ch02.html#Han2021-marker)] Shujie Han, Patrick P. C. Lee, Fan Xu, Yi Liu, Cheng He, and Jiongzhou Liu. [An In-Depth Study of Correlated Failures in Production SSD-Based Data Centers](https://www.usenix.org/conference/fast21/presentation/han). At *19th USENIX Conference on File and Storage Technologies* (FAST), February 2021.
|
||||
|
||||
[[52](ch02.html#Nightingale2011-marker)] Edmund B. Nightingale, John R. Douceur, and Vince Orgovan. [Cycles, Cells and Platters: An Empirical Analysis of Hardware Failures on a Million Consumer PCs](https://eurosys2011.cs.uni-salzburg.at/pdf/eurosys2011-nightingale.pdf). At *6th European Conference on Computer Systems* (EuroSys), April 2011. [doi:10.1145/1966445.1966477](https://doi.org/10.1145/1966445.1966477)
|
||||
|
||||
[[53](ch02.html#Gunawi2014-marker)] Haryadi S. Gunawi, Mingzhe Hao, Tanakorn Leesatapornwongsa, Tiratat Patana-anake, Thanh Do, Jeffry Adityatama, Kurnia J. Eliazar, Agung Laksono, Jeffrey F. Lukman, Vincentius Martin, and Anang D. Satria. [What Bugs Live in the Cloud?](http://ucare.cs.uchicago.edu/pdf/socc14-cbs.pdf) At *5th ACM Symposium on Cloud Computing* (SoCC), November 2014. [doi:10.1145/2670979.2670986](https://doi.org/10.1145/2670979.2670986)
|
||||
|
||||
[[54](ch02.html#Kreps2012_ch1-marker)] Jay Kreps. [Getting Real About Distributed System Reliability](http://blog.empathybox.com/post/19574936361/getting-real-about-distributed-system-reliability). *blog.empathybox.com*, March 2012. Archived at [perma.cc/9B5Q-AEBW](https://perma.cc/9B5Q-AEBW)
|
||||
|
||||
[[55](ch02.html#Minar2012_ch1-marker)] Nelson Minar. [Leap Second Crashes Half the Internet](http://www.somebits.com/weblog/tech/bad/leap-second-2012.html). *somebits.com*, July 2012. Archived at [perma.cc/2WB8-D6EU](https://perma.cc/2WB8-D6EU)
|
||||
|
||||
[[56](ch02.html#HPE2019-marker)] Hewlett Packard Enterprise. [Support Alerts – Customer Bulletin a00092491en_us](https://support.hpe.com/hpesc/public/docDisplay?docId=emr_na-a00092491en_us). *support.hpe.com*, November 2019. Archived at [perma.cc/S5F6-7ZAC](https://perma.cc/S5F6-7ZAC)
|
||||
|
||||
[[57](ch02.html#Hochstein2020-marker)] Lorin Hochstein. [awesome limits](https://github.com/lorin/awesome-limits). *github.com*, November 2020. Archived at [perma.cc/3R5M-E5Q4](https://perma.cc/3R5M-E5Q4)
|
||||
|
||||
[[58](ch02.html#Tang2023-marker)] Lilia Tang, Chaitanya Bhandari, Yongle Zhang, Anna Karanika, Shuyang Ji, Indranil Gupta, and Tianyin Xu. [Fail through the Cracks: Cross-System Interaction Failures in Modern Cloud Systems](https://tianyin.github.io/pub/csi-failures.pdf). At *18th European Conference on Computer Systems* (EuroSys), May 2023. [doi:10.1145/3552326.3587448](https://doi.org/10.1145/3552326.3587448)
|
||||
|
||||
[[59](ch02.html#Ulrich2016-marker)] Mike Ulrich. [Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/). In Betsy Beyer, Jennifer Petoff, Chris Jones, and Niall Richard Murphy (ed). [*Site Reliability Engineering: How Google Runs Production Systems*](https://www.oreilly.com/library/view/site-reliability-engineering/9781491929117/). O’Reilly Media, 2016. ISBN: 9781491929124
|
||||
|
||||
[[60](ch02.html#Fassbender2022-marker)] Harri Faßbender. [Cascading failures in large-scale distributed systems](https://blog.mi.hdm-stuttgart.de/index.php/2022/03/03/cascading-failures-in-large-scale-distributed-systems/). *blog.mi.hdm-stuttgart.de*, March 2022. Archived at [perma.cc/K7VY-YJRX](https://perma.cc/K7VY-YJRX)
|
||||
|
||||
[[61](ch02.html#Cook2000-marker)] Richard I. Cook. [How Complex Systems Fail](https://www.adaptivecapacitylabs.com/HowComplexSystemsFail.pdf). Cognitive Technologies Laboratory, April 2000. Archived at [perma.cc/RDS6-2YVA](https://perma.cc/RDS6-2YVA)
|
||||
|
||||
[[62](ch02.html#Woods2017-marker)] David D Woods. [STELLA: Report from the SNAFUcatchers Workshop on Coping With Complexity](https://snafucatchers.github.io/). *snafucatchers.github.io*, March 2017. Archived at [archive.org](https://web.archive.org/web/20230306130131/https://snafucatchers.github.io/)
|
||||
|
||||
[[63](ch02.html#Oppenheimer2003-marker)] David Oppenheimer, Archana Ganapathi, and David A. Patterson. [Why Do Internet Services Fail, and What Can Be Done About It?](http://static.usenix.org/legacy/events/usits03/tech/full_papers/oppenheimer/oppenheimer.pdf) At *4th USENIX Symposium on Internet Technologies and Systems* (USITS), March 2003.
|
||||
|
||||
[[64](ch02.html#Dekker2017-marker)] Sidney Dekker. [*The Field Guide to Understanding ‘Human Error’, 3rd Edition*](https://learning.oreilly.com/library/view/the-field-guide/9781317031833/). CRC Press, November 2017. ISBN: 9781472439055
|
||||
|
||||
[[65](ch02.html#Allspaw2012-marker)] John Allspaw. [Blameless PostMortems and a Just Culture](https://www.etsy.com/codeascraft/blameless-postmortems/). *etsy.com*, May 2012. Archived at [perma.cc/YMJ7-NTAP](https://perma.cc/YMJ7-NTAP)
|
||||
|
||||
[[66](ch02.html#Sabo2023-marker)] Itzy Sabo. [Uptime Guarantees — A Pragmatic Perspective](https://world.hey.com/itzy/uptime-guarantees-a-pragmatic-perspective-736d7ea4). *world.hey.com*, March 2023. Archived at [perma.cc/F7TU-78JB](https://perma.cc/F7TU-78JB)
|
||||
|
||||
[[67](ch02.html#Jurewitz2013-marker)] Michael Jurewitz. [The Human Impact of Bugs](http://jury.me/blog/2013/3/14/the-human-impact-of-bugs). *jury.me*, March 2013. Archived at [perma.cc/5KQ4-VDYL](https://perma.cc/5KQ4-VDYL)
|
||||
|
||||
[[68](ch02.html#Siddique2021-marker)] Haroon Siddique and Ben Quinn. [Court clears 39 post office operators convicted due to ‘corrupt data’](https://www.theguardian.com/uk-news/2021/apr/23/court-clears-39-post-office-staff-convicted-due-to-corrupt-data). *theguardian.com*, April 2021. Archived at [archive.org](https://web.archive.org/web/20220630124107/https://www.theguardian.com/uk-news/2021/apr/23/court-clears-39-post-office-staff-convicted-due-to-corrupt-data)
|
||||
|
||||
[[69](ch02.html#Bohm2022-marker)] Nicholas Bohm, James Christie, Peter Bernard Ladkin, Bev Littlewood, Paul Marshall, Stephen Mason, Martin Newby, Steven J. Murdoch, Harold Thimbleby, and Martyn Thomas. [The legal rule that computers are presumed to be operating correctly – unforeseen and unjust consequences](https://www.benthamsgaze.org/wp-content/uploads/2022/06/briefing-presumption-that-computers-are-reliable.pdf). Briefing note, *benthamsgaze.org*, June 2022. Archived at [perma.cc/WQ6X-TMW4](https://perma.cc/WQ6X-TMW4)
|
||||
|
||||
[[70](ch02.html#McKinley2015-marker)] Dan McKinley. [Choose Boring Technology](https://mcfunley.com/choose-boring-technology). *mcfunley.com*, March 2015. Archived at [perma.cc/7QW7-J4YP](https://perma.cc/7QW7-J4YP)
|
||||
|
||||
[[71](ch02.html#Warfield2023-marker)] Andy Warfield. [Building and operating a pretty big storage system called S3](https://www.allthingsdistributed.com/2023/07/building-and-operating-a-pretty-big-storage-system.html). *allthingsdistributed.com*, July 2023. Archived at [perma.cc/7LPK-TP7V](https://perma.cc/7LPK-TP7V)
|
||||
|
||||
[[72](ch02.html#Brooker2023-marker)] Marc Brooker. [Surprising Scalability of Multitenancy](https://brooker.co.za/blog/2023/03/23/economics.html). *brooker.co.za*, March 2023. Archived at [archive.org](https://web.archive.org/web/20230404065818/https://brooker.co.za/blog/2023/03/23/economics.html)
|
||||
|
||||
[[73](ch02.html#Stopford2009-marker)] Ben Stopford. [Shared Nothing vs. Shared Disk Architectures: An Independent View](http://www.benstopford.com/2009/11/24/understanding-the-shared-nothing-architecture/). *benstopford.com*, November 2009. Archived at [perma.cc/7BXH-EDUR](https://perma.cc/7BXH-EDUR)
|
||||
|
||||
[[74](ch02.html#Stonebraker1986-marker)] Michael Stonebraker. [The Case for Shared Nothing](http://db.cs.berkeley.edu/papers/hpts85-nothing.pdf). *IEEE Database Engineering Bulletin*, volume 9, issue 1, pages 4–9, March 1986.
|
||||
|
||||
[[75](ch02.html#Antonopoulos2019_ch2-marker)] Panagiotis Antonopoulos, Alex Budovski, Cristian Diaconu, Alejandro Hernandez Saenz, Jack Hu, Hanuma Kodavalla, Donald Kossmann, Sandeep Lingam, Umar Farooq Minhas, Naveen Prakash, Vijendra Purohit, Hugh Qu, Chaitanya Sreenivas Ravella, Krystyna Reisteter, Sheetal Shrotri, Dixin Tang, and Vikram Wakade. [Socrates: The New SQL Server in the Cloud](https://www.microsoft.com/en-us/research/uploads/prod/2019/05/socrates.pdf). At *ACM International Conference on Management of Data* (SIGMOD), pages 1743–1756, June 2019. [doi:10.1145/3299869.3314047](https://doi.org/10.1145/3299869.3314047)
|
||||
|
||||
[[76](ch02.html#Newman2021_ch2-marker)] Sam Newman. [*Building Microservices*, second edition](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/). O’Reilly Media, 2021. ISBN: 9781492034025
|
||||
|
||||
[[77](ch02.html#Ensmenger2016-marker)] Nathan Ensmenger. [When Good Software Goes Bad: The Surprising Durability of an Ephemeral Technology](https://themaintainers.wpengine.com/wp-content/uploads/2021/04/ensmenger-maintainers-v2.pdf). At *The Maintainers Conference*, April 2016. Archived at [perma.cc/ZXT4-HGZB](https://perma.cc/ZXT4-HGZB)
|
||||
|
||||
[[78](ch02.html#Glass2002-marker)] Robert L. Glass. [*Facts and Fallacies of Software Engineering*](https://learning.oreilly.com/library/view/facts-and-fallacies/0321117425/). Addison-Wesley Professional, October 2002. ISBN: 9780321117427
|
||||
|
||||
[[79](ch02.html#Bellotti2021-marker)] Marianne Bellotti. [*Kill It with Fire*](https://learning.oreilly.com/library/view/kill-it-with/9781098128883/). No Starch Press, April 2021. ISBN: 9781718501188
|
||||
|
||||
[[80](ch02.html#Bainbridge1983-marker)] Lisanne Bainbridge. [Ironies of automation](https://www.adaptivecapacitylabs.com/IroniesOfAutomation-Bainbridge83.pdf). *Automatica*, volume 19, issue 6, pages 775–779, November 1983. [doi:10.1016/0005-1098(83)90046-8](https://doi.org/10.1016/0005-1098(83)90046-8)
|
||||
|
||||
[[81](ch02.html#Hamilton2007-marker)] James Hamilton. [On Designing and Deploying Internet-Scale Services](https://www.usenix.org/legacy/events/lisa07/tech/full_papers/hamilton/hamilton.pdf). At *21st Large Installation System Administration Conference* (LISA), November 2007.
|
||||
|
||||
[[82](ch02.html#Horovits2021-marker)] Dotan Horovits. [Open Source for Better Observability](https://horovits.medium.com/open-source-for-better-observability-8c65b5630561). *horovits.medium.com*, October 2021. Archived at [perma.cc/R2HD-U2ZT](https://perma.cc/R2HD-U2ZT)
|
||||
|
||||
[[83](ch02.html#Foote1997-marker)] Brian Foote and Joseph Yoder. [Big Ball of Mud](http://www.laputan.org/pub/foote/mud.pdf). At *4th Conference on Pattern Languages of Programs* (PLoP), September 1997. Archived at [perma.cc/4GUP-2PBV](https://perma.cc/4GUP-2PBV)
|
||||
|
||||
[[84](ch02.html#Brooker2022-marker)] Marc Brooker. [What is a simple system?](https://brooker.co.za/blog/2022/05/03/simplicity.html) *brooker.co.za*, May 2022. Archived at [archive.org](https://web.archive.org/web/20220602141902/https://brooker.co.za/blog/2022/05/03/simplicity.html)
|
||||
|
||||
[[85](ch02.html#Brooks1995-marker)] Frederick P Brooks. [No Silver Bullet – Essence and Accident in Software Engineering](http://worrydream.com/refs/Brooks-NoSilverBullet.pdf). In [*The Mythical Man-Month*](https://www.oreilly.com/library/view/mythical-man-month-the/0201835959/), Anniversary edition, Addison-Wesley, 1995. ISBN: 9780201835953
|
||||
|
||||
[[86](ch02.html#Luu2020-marker)] Dan Luu. [Against essential and accidental complexity](https://danluu.com/essential-complexity/). *danluu.com*, December 2020. Archived at [perma.cc/H5ES-69KC](https://perma.cc/H5ES-69KC)
|
||||
|
||||
[[87](ch02.html#Gamma1994-marker)] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. [*Design Patterns: Elements of Reusable Object-Oriented Software*](https://learning.oreilly.com/library/view/design-patterns-elements/0201633612/). Addison-Wesley Professional, October 1994. ISBN: 9780201633610
|
||||
|
||||
[[88](ch02.html#Evans2003-marker)] Eric Evans. [*Domain-Driven Design: Tackling Complexity in the Heart of Software*](https://learning.oreilly.com/library/view/domain-driven-design-tackling/0321125215/). Addison-Wesley Professional, August 2003. ISBN: 9780321125217
|
||||
|
||||
[[89](ch02.html#Breivold2008-marker)] Hongyu Pei Breivold, Ivica Crnkovic, and Peter J. Eriksson. [Analyzing Software Evolvability](http://www.es.mdh.se/pdf_publications/1251.pdf). at *32nd Annual IEEE International Computer Software and Applications Conference* (COMPSAC), July 2008. [doi:10.1109/COMPSAC.2008.50](https://doi.org/10.1109/COMPSAC.2008.50)
|
||||
|
||||
[[90](ch02.html#Zaninotto2002-marker)] Enrico Zaninotto. [From X programming to the X organisation](https://martinfowler.com/articles/zaninotto.pdf). At *XP Conference*, May 2002. Archived at [perma.cc/R9AR-QCKZ](https://perma.cc/R9AR-QCKZ)
|
||||
|
||||
1234
content/v2/ch3.md
1234
content/v2/ch3.md
File diff suppressed because it is too large
Load diff
|
|
@ -1,92 +0,0 @@
|
|||
---
|
||||
title: "目录"
|
||||
linkTitle: "目录"
|
||||
weight: 10
|
||||
breadcrumbs: false
|
||||
---
|
||||
|
||||
|
||||

|
||||
|
||||
## [序言](/preface)
|
||||
|
||||
## [第一部分:数据系统基础](/part-i)
|
||||
|
||||
### [第一章:数据系统架构中的利弊权衡](/v2/ch1)
|
||||
* [关于数据系统的思考](/v2/ch1#关于数据系统的思考)
|
||||
* [可靠性](/v2/ch1#术语:前端与后端)
|
||||
* [可伸缩性](/v2/ch1#可伸缩性)
|
||||
* [可维护性](/v2/ch1#可维护性)
|
||||
* [本章小结](/v2/ch1#本章小结)
|
||||
### [第二章:数据模型与查询语言](/v2/ch2)
|
||||
* [关系模型与文档模型](/v2/ch2#关系模型与文档模型)
|
||||
* [数据查询语言](/v2/ch2#数据查询语言)
|
||||
* [图数据模型](/v2/ch2#图数据模型)
|
||||
* [本章小结](/v2/ch2#本章小结)
|
||||
### [第三章:存储与检索](/v2/ch3)
|
||||
* [驱动数据库的数据结构](/v2/ch3#驱动数据库的数据结构)
|
||||
* [事务处理还是分析?](/v2/ch3#事务处理还是分析)
|
||||
* [列式存储](/v2/ch3#列式存储)
|
||||
* [本章小结](/v2/ch3#本章小结)
|
||||
### [第四章:编码与演化](/v2/ch4)
|
||||
* [编码数据的格式](/v2/ch4#编码数据的格式)
|
||||
* [数据流的类型](/v2/ch4#数据流的类型)
|
||||
* [本章小结](/v2/ch4#本章小结)
|
||||
|
||||
## [第二部分:分布式数据](/part-ii)
|
||||
|
||||
### [第五章:复制](/v2/ch5)
|
||||
* [领导者与追随者](/v2/ch5#领导者与追随者)
|
||||
* [复制延迟问题](/v2/ch5#复制延迟问题)
|
||||
* [多主复制](/v2/ch5#多主复制)
|
||||
* [无主复制](/v2/ch5#无主复制)
|
||||
* [本章小结](/v2/ch5#本章小结)
|
||||
### [第六章:分区](/v2/ch6)
|
||||
* [分区与复制](/v2/ch6#分区与复制)
|
||||
* [键值数据的分区](/v2/ch6#键值数据的分区)
|
||||
* [分区与次级索引](/v2/ch6#分区与次级索引)
|
||||
* [分区再平衡](/v2/ch6#分区再平衡)
|
||||
* [请求路由](/v2/ch6#请求路由)
|
||||
* [本章小结](/v2/ch6#本章小结)
|
||||
### [第七章:事务](/v2/ch7)
|
||||
* [事务的棘手概念](/v2/ch7#事务的棘手概念)
|
||||
* [弱隔离级别](/v2/ch7#弱隔离级别)
|
||||
* [可串行化](/v2/ch7#可串行化)
|
||||
* [本章小结](/v2/ch7#本章小结)
|
||||
### [第八章:分布式系统的麻烦](/v2/ch8)
|
||||
* [故障与部分失效](/v2/ch8#故障与部分失效)
|
||||
* [不可靠的网络](/v2/ch8#不可靠的网络)
|
||||
* [不可靠的时钟](/v2/ch8#不可靠的时钟)
|
||||
* [知识、真相与谎言](/v2/ch8#知识真相与谎言)
|
||||
* [本章小结](/v2/ch8#本章小结)
|
||||
### [第九章:一致性与共识](/v2/ch9)
|
||||
* [一致性保证](/v2/ch9#一致性保证)
|
||||
* [线性一致性](/v2/ch9#线性一致性)
|
||||
* [顺序保证](/v2/ch9#顺序保证)
|
||||
* [分布式事务与共识](/v2/ch9#分布式事务与共识)
|
||||
* [本章小结](/v2/ch9#本章小结)
|
||||
|
||||
## [第三部分:衍生数据](/part-iii)
|
||||
|
||||
### [第十章:批处理](/v2/ch10)
|
||||
* [使用Unix工具的批处理](/v2/ch10#使用Unix工具的批处理)
|
||||
* [MapReduce和分布式文件系统](/v2/ch10#MapReduce和分布式文件系统)
|
||||
* [MapReduce之后](/v2/ch10#MapReduce之后)
|
||||
* [本章小结](/v2/ch10#本章小结)
|
||||
### [第十一章:流处理](/v2/ch11)
|
||||
* [传递事件流](/v2/ch11#传递事件流)
|
||||
* [数据库与流](/v2/ch11#数据库与流)
|
||||
* [流处理](/v2/ch11#流处理)
|
||||
* [本章小结](/v2/ch11#本章小结)
|
||||
### [第十二章:数据系统的未来](/v2/ch12)
|
||||
* [数据集成](/v2/ch12#数据集成)
|
||||
* [分拆数据库](/v2/ch12#分拆数据库)
|
||||
* [将事情做正确](/v2/ch12#将事情做正确)
|
||||
* [做正确的事情](/v2/ch12#做正确的事情)
|
||||
* [本章小结](/v2/ch12#本章小结)
|
||||
|
||||
### [术语表](/glossary)
|
||||
|
||||
### [后记](/colophon)
|
||||
|
||||
|
||||
|
|
@ -41,10 +41,10 @@ breadcrumbs: false
|
|||
|
||||
为了帮助你理解可以做出哪些选择,本章比较了几个对比的概念,并探讨了它们的权衡:
|
||||
|
||||
* 事务型系统和分析型系统之间的区别(["分析型与事务型系统"](/en/ch1#sec_introduction_analytics));
|
||||
* 云服务和自托管系统的利弊(["云服务与自托管"](/en/ch1#sec_introduction_cloud));
|
||||
* 何时从单节点系统转向分布式系统(["分布式与单节点系统"](/en/ch1#sec_introduction_distributed));以及
|
||||
* 平衡业务需求和用户权利(["数据系统、法律与社会"](/en/ch1#sec_introduction_compliance))。
|
||||
* 事务型系统和分析型系统之间的区别(["分析型与事务型系统"](/ch1#sec_introduction_analytics));
|
||||
* 云服务和自托管系统的利弊(["云服务与自托管"](/ch1#sec_introduction_cloud));
|
||||
* 何时从单节点系统转向分布式系统(["分布式与单节点系统"](/ch1#sec_introduction_distributed));以及
|
||||
* 平衡业务需求和用户权利(["数据系统、法律与社会"](/ch1#sec_introduction_compliance))。
|
||||
|
||||
此外,本章将为你提供本书其余部分所需的术语。
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ breadcrumbs: false
|
|||
|
||||
## 分析型与事务型系统 {#sec_introduction_analytics}
|
||||
|
||||
如果你在企业中从事数据系统工作,你可能会遇到几种不同类型的数据工作人员。第一类是构建处理读取和更新数据请求的服务的 **后端工程师**;这些服务通常直接或通过其他服务间接地为外部用户提供服务(参见["微服务和无服务器"](/en/ch1#sec_introduction_microservices))。有时服务是供组织其他部分内部使用的。
|
||||
如果你在企业中从事数据系统工作,你可能会遇到几种不同类型的数据工作人员。第一类是构建处理读取和更新数据请求的服务的 **后端工程师**;这些服务通常直接或通过其他服务间接地为外部用户提供服务(参见["微服务和无服务器"](/ch1#sec_introduction_microservices))。有时服务是供组织其他部分内部使用的。
|
||||
|
||||
除了管理后端服务的团队外,通常还有另外两组人需要访问组织的数据:**业务分析师**,他们生成有关组织活动的报告,以帮助管理层做出更好的决策(**商业智能** 或 **BI**),以及 **数据科学家**,他们在数据中寻找新颖的见解或创建由数据分析和机器学习/AI 支持的面向用户的产品功能(例如,电子商务网站上的"购买了 X 的人也购买了 Y"推荐、预测分析如风险评分或垃圾邮件过滤,以及搜索结果排名)。
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ breadcrumbs: false
|
|||
在商业数据处理的早期,对数据库的写入通常对应于正在发生的 **商业交易**:进行销售、向供应商下订单、支付员工工资等。随着数据库扩展到不涉及资金交换的领域,术语 **事务** 仍然保留了下来,指的是形成逻辑单元的一组读写操作。
|
||||
|
||||
> [!NOTE]
|
||||
> [第 8 章](/en/ch8#ch_transactions) 详细探讨了我们所说的事务的含义。本章宽泛地使用该术语来指代低延迟的读写操作。
|
||||
> [第 8 章](/ch8#ch_transactions) 详细探讨了我们所说的事务的含义。本章宽泛地使用该术语来指代低延迟的读写操作。
|
||||
|
||||
尽管数据库开始用于许多不同类型的数据——社交媒体上的帖子、游戏中的动作、地址簿中的联系人等等——基本访问模式仍然类似于处理商业交易。事务型系统通常通过某个键查找少量记录(这称为 **点查询**)。基于用户的输入插入、更新或删除记录。因为这些应用程序是交互式的,这种访问模式被称为 **联机事务处理**(OLTP)。
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ breadcrumbs: false
|
|||
* 哪个品牌的婴儿食品最常与品牌 X 纸尿裤一起购买?
|
||||
|
||||
这些查询产生的报告对商业智能很重要,帮助管理层决定下一步做什么。为了将这种使用数据库的模式与事务处理区分开来,它被称为 **联机分析处理**(OLAP)[^5]。
|
||||
OLTP 和分析之间的区别并不总是明确的,但一些典型特征列在[表 1-1](/en/ch1#tab_oltp_vs_olap) 中。
|
||||
OLTP 和分析之间的区别并不总是明确的,但一些典型特征列在[表 1-1](/ch1#tab_oltp_vs_olap) 中。
|
||||
|
||||
{{< figure id="tab_oltp_vs_olap" title="表 1-1. 比较事务型和分析型系统的特征" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -117,14 +117,14 @@ OLTP 和分析之间的区别并不总是明确的,但一些典型特征列在
|
|||
由于几个原因,业务分析师和数据科学家直接查询这些 OLTP 系统通常是不可取的:
|
||||
|
||||
* 感兴趣的数据可能分散在多个事务型系统中,使得在单个查询中组合这些数据集变得困难(这个问题被称为 **数据孤岛**);
|
||||
* 适合 OLTP 的模式和数据布局类型不太适合分析(参见["用于分析的星型和雪花型模式"](/en/ch3#sec_datamodels_analytics));
|
||||
* 适合 OLTP 的模式和数据布局类型不太适合分析(参见["用于分析的星型和雪花型模式"](/ch3#sec_datamodels_analytics));
|
||||
* 分析查询可能相当昂贵,在 OLTP 数据库上运行它们会影响其他用户的性能;以及
|
||||
* OLTP 系统可能位于用户出于安全或合规原因不允许直接访问的单独网络中。
|
||||
|
||||
相比之下,**数据仓库** 是一个单独的数据库,分析师可以随心所欲地查询,而不会影响 OLTP 操作 [^7]。
|
||||
正如我们将在[第 4 章](/en/ch4#ch_storage)中看到的,数据仓库通常以与 OLTP 数据库非常不同的方式存储数据,以优化分析中常见的查询类型。
|
||||
正如我们将在[第 4 章](/ch4#ch_storage)中看到的,数据仓库通常以与 OLTP 数据库非常不同的方式存储数据,以优化分析中常见的查询类型。
|
||||
|
||||
数据仓库包含公司中所有各种 OLTP 系统中数据的只读副本。数据从 OLTP 数据库中提取(使用定期数据转储或连续更新流),转换为分析友好的模式,清理,然后加载到数据仓库中。将数据导入数据仓库的过程称为 **提取-转换-加载**(ETL),如[图 1-1](/en/ch1#fig_dwh_etl) 所示。有时 **转换** 和 **加载** 步骤的顺序会交换(即,转换在数据仓库中完成,在加载之后),导致 **ELT**。
|
||||
数据仓库包含公司中所有各种 OLTP 系统中数据的只读副本。数据从 OLTP 数据库中提取(使用定期数据转储或连续更新流),转换为分析友好的模式,清理,然后加载到数据仓库中。将数据导入数据仓库的过程称为 **提取-转换-加载**(ETL),如[图 1-1](/ch1#fig_dwh_etl) 所示。有时 **转换** 和 **加载** 步骤的顺序会交换(即,转换在数据仓库中完成,在加载之后),导致 **ELT**。
|
||||
|
||||
{{< figure src="/fig/ddia_0101.png" id="fig_dwh_etl" caption="图 1-1. ETL 到数据仓库的简化概述。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ OLTP 和分析之间的区别并不总是明确的,但一些典型特征列在
|
|||
一些数据库系统提供 **混合事务/分析处理**(HTAP),旨在在单个系统中启用 OLTP 和分析,而无需从一个系统到另一个系统的 ETL [^8] [^9]。
|
||||
然而,许多 HTAP 系统内部由一个 OLTP 系统和一个单独的分析系统组成,隐藏在一个公共接口后面——因此两者之间的区别对于理解这些系统的工作原理仍然很重要。
|
||||
|
||||
此外,即使 HTAP 存在,由于不同的目标和要求,事务型和分析型系统之间的分离也很常见。特别是,每个事务型系统都有自己的数据库被认为是良好的实践(参见["微服务和无服务器"](/en/ch1#sec_introduction_microservices)),导致数百个独立的事务型数据库;另一方面,企业通常只有一个数据仓库,以便业务分析师可以在单个查询中组合来自多个事务型系统的数据。
|
||||
此外,即使 HTAP 存在,由于不同的目标和要求,事务型和分析型系统之间的分离也很常见。特别是,每个事务型系统都有自己的数据库被认为是良好的实践(参见["微服务和无服务器"](/ch1#sec_introduction_microservices)),导致数百个独立的事务型数据库;另一方面,企业通常只有一个数据仓库,以便业务分析师可以在单个查询中组合来自多个事务型系统的数据。
|
||||
|
||||
因此,HTAP 并不能取代数据仓库。相反,它在同一个应用程序既需要执行扫描大量行的分析查询,又需要以低延迟读取和更新单个记录的场景中很有用。例如,欺诈检测可能涉及此类工作负载 [^10]。
|
||||
|
||||
|
|
@ -141,16 +141,16 @@ OLTP 和分析之间的区别并不总是明确的,但一些典型特征列在
|
|||
|
||||
#### 从数据仓库到数据湖 {#from-data-warehouse-to-data-lake}
|
||||
|
||||
数据仓库通常使用通过 SQL 查询的 **关系** 数据模型(参见[第 3 章](/en/ch3#ch_datamodels)),可能使用专门的商业智能软件。这个模型非常适合业务分析师需要进行的查询类型,但不太适合数据科学家的需求,他们可能需要执行以下任务:
|
||||
数据仓库通常使用通过 SQL 查询的 **关系** 数据模型(参见[第 3 章](/ch3#ch_datamodels)),可能使用专门的商业智能软件。这个模型非常适合业务分析师需要进行的查询类型,但不太适合数据科学家的需求,他们可能需要执行以下任务:
|
||||
|
||||
* 将数据转换为适合训练机器学习模型的形式;通常这需要将数据库表的行和列转换为称为 **特征** 的数值向量或矩阵。以最大化训练模型性能的方式执行此转换的过程称为 **特征工程**,它通常需要难以使用 SQL 表达的自定义代码。
|
||||
* 获取文本数据(例如,产品评论)并使用自然语言处理技术尝试从中提取结构化信息(例如,作者的情感或他们提到的主题)。同样,他们可能需要使用计算机视觉技术从照片中提取结构化信息。
|
||||
|
||||
虽然已经努力将机器学习运算符添加到 SQL 数据模型 [^12] 并在关系基础上构建高效的机器学习系统 [^13],但许多数据科学家更喜欢不在关系数据库(如数据仓库)中工作。相反,许多人更喜欢使用 Python 数据分析库(如 pandas 和 scikit-learn)、统计分析语言(如 R)以及分布式分析框架(如 Spark)[^14]。
|
||||
我们将在["数据框、矩阵和数组"](/en/ch3#sec_datamodels_dataframes)中进一步讨论这些。
|
||||
我们将在["数据框、矩阵和数组"](/ch3#sec_datamodels_dataframes)中进一步讨论这些。
|
||||
|
||||
因此,组织面临着以适合数据科学家使用的形式提供数据的需求。答案是 **数据湖**:一个中央数据存储库,保存可能对分析有用的任何数据的副本,通过 ETL 过程从事务型系统获得。与数据仓库的区别在于,数据湖只包含文件,而不强加任何特定的文件格式或数据模型。数据湖中的文件可能是数据库记录的集合,使用文件格式(如 Avro 或 Parquet)编码(参见[第 5 章](/en/ch5#ch_encoding)),但它们同样可以包含文本、图像、视频、传感器读数、稀疏矩阵、特征向量、基因组序列或任何其他类型的数据 [^15]。
|
||||
除了更加灵活之外,这通常也比关系数据存储更便宜,因为数据湖可以使用商品化的文件存储,如对象存储(参见["云原生系统架构"](/en/ch1#sec_introduction_cloud_native))。
|
||||
因此,组织面临着以适合数据科学家使用的形式提供数据的需求。答案是 **数据湖**:一个中央数据存储库,保存可能对分析有用的任何数据的副本,通过 ETL 过程从事务型系统获得。与数据仓库的区别在于,数据湖只包含文件,而不强加任何特定的文件格式或数据模型。数据湖中的文件可能是数据库记录的集合,使用文件格式(如 Avro 或 Parquet)编码(参见[第 5 章](/ch5#ch_encoding)),但它们同样可以包含文本、图像、视频、传感器读数、稀疏矩阵、特征向量、基因组序列或任何其他类型的数据 [^15]。
|
||||
除了更加灵活之外,这通常也比关系数据存储更便宜,因为数据湖可以使用商品化的文件存储,如对象存储(参见["云原生系统架构"](/ch1#sec_introduction_cloud_native))。
|
||||
|
||||
ETL 过程已经被推广为 **数据管道**,在某些情况下,数据湖已成为从事务型系统到数据仓库路径上的中间站。数据湖包含由事务型系统产生的"原始"形式的数据,没有转换为关系数据仓库模式。这种方法的优点是数据的每个消费者都可以将原始数据转换为最适合其需求的形式。它被称为 **寿司原则**:"原始数据更好"[^16]。
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
#### 超越数据湖 {#beyond-the-data-lake}
|
||||
|
||||
随着分析实践的成熟,组织越来越关注分析系统和数据管道的管理和操作,例如 DataOps 宣言中所捕获的 [^18]。
|
||||
其中一部分是治理、隐私以及符合 GDPR 和 CCPA 等法规的问题,我们将在["数据系统、法律与社会"](/en/ch1#sec_introduction_compliance)和[待补充链接]中讨论。
|
||||
其中一部分是治理、隐私以及符合 GDPR 和 CCPA 等法规的问题,我们将在["数据系统、法律与社会"](/ch1#sec_introduction_compliance)和[待补充链接]中讨论。
|
||||
|
||||
此外,分析数据越来越多地不仅作为文件和关系表提供,还作为事件流提供(参见[待补充链接])。使用基于文件的数据分析,你可以定期(例如,每天)重新运行分析以响应数据的变化,但流处理允许分析系统以秒级的速度响应事件。根据应用程序及其时间敏感性,流处理方法可能很有价值,例如识别和阻止潜在的欺诈或滥用活动。
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
与事务型和分析型系统的区别相关,本书还区分了 **权威记录系统** 和 **派生数据系统**。这些术语很有用,因为它们可以帮助你理清数据在系统中的流动:
|
||||
|
||||
权威记录系统
|
||||
: 权威记录系统(SoR, System of Record),也称为 **权威数据源**,保存某些数据的权威或 **规范** 版本。当新数据进入时,例如作为用户输入,它首先被写入这里。每个事实只表示一次(表示通常是 **规范化的**;参见["规范化、反规范化和连接"](/en/ch3#sec_datamodels_normalization))。如果另一个系统和权威记录系统之间存在任何差异,那么权威记录系统中的值(按定义)是正确的。
|
||||
: 权威记录系统(SoR, System of Record),也称为 **权威数据源**,保存某些数据的权威或 **规范** 版本。当新数据进入时,例如作为用户输入,它首先被写入这里。每个事实只表示一次(表示通常是 **规范化的**;参见["规范化、反规范化和连接"](/ch3#sec_datamodels_normalization))。如果另一个系统和权威记录系统之间存在任何差异,那么权威记录系统中的值(按定义)是正确的。
|
||||
|
||||
派生数据系统
|
||||
: 派生系统中的数据是通过获取另一个系统中的一些现有数据并以某种方式转换或处理它的结果。如果你丢失了派生数据,你可以从原始源重新创建它。一个经典的例子是缓存:如果存在,可以从缓存中提供数据,但如果缓存不包含你需要的内容,你可以回退到底层数据库。反规范化的值、索引、物化视图、转换的数据表示以及在数据集上训练的模型也属于这一类别。
|
||||
|
|
@ -197,7 +197,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
最终,这是一个关于业务优先级的问题。管理智慧认为,作为组织核心竞争力或竞争优势的事情应该在内部完成,而非核心、常规或普通的事情应该留给供应商 [^21]。
|
||||
举一个极端的例子,大多数公司不自己发电(除非他们是能源公司,撇开紧急备用电源不谈),因为从电网购买电力更便宜。
|
||||
|
||||
对于软件,需要做出两个重要决定:谁构建软件以及谁部署它。外包每个决定的程度有多种可能性,如[图 1-2](/en/ch1#fig_cloud_spectrum) 所示。一个极端是你自己编写和运行的定制软件;另一个极端是广泛使用的云服务或软件即服务(SaaS)产品,由外部供应商实施和运营,你只能通过 Web 界面或 API 访问。
|
||||
对于软件,需要做出两个重要决定:谁构建软件以及谁部署它。外包每个决定的程度有多种可能性,如[图 1-2](/ch1#fig_cloud_spectrum) 所示。一个极端是你自己编写和运行的定制软件;另一个极端是广泛使用的云服务或软件即服务(SaaS)产品,由外部供应商实施和运营,你只能通过 Web 界面或 API 访问。
|
||||
|
||||
{{< figure src="/fig/ddia_0102.png" id="fig_cloud_spectrum" caption="图 1-2. 软件类型及其操作的范围。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
|
||||
云服务是否实际上比自托管更便宜、更容易,很大程度上取决于你的技能和系统的工作负载。如果你已经有设置和操作所需系统的经验,并且你的负载相当可预测(即,你需要的机器数量不会剧烈波动),那么购买自己的机器并自己运行软件通常更便宜 [^22] [^23]。
|
||||
|
||||
另一方面,如果你需要一个你还不知道如何部署和操作的系统,那么采用云服务通常比自己学习管理系统更容易、更快。如果你必须专门雇用和培训员工来维护和操作系统,那可能会变得非常昂贵。当你使用云时,你仍然需要一个运维团队(参见["云时代的运维"](/en/ch1#sec_introduction_operations)),但外包基本系统管理可以让你的团队专注于更高层次的问题。
|
||||
另一方面,如果你需要一个你还不知道如何部署和操作的系统,那么采用云服务通常比自己学习管理系统更容易、更快。如果你必须专门雇用和培训员工来维护和操作系统,那可能会变得非常昂贵。当你使用云时,你仍然需要一个运维团队(参见["云时代的运维"](/ch1#sec_introduction_operations)),但外包基本系统管理可以让你的团队专注于更高层次的问题。
|
||||
|
||||
当你将系统的操作外包给专门运行该服务的公司时,这可能会带来更好的服务,因为提供商从为许多客户提供服务中获得了运营专业知识。另一方面,如果你自己运行服务,你可以配置和调整它以在你的特定工作负载上表现良好;云服务不太可能愿意代表你进行此类自定义。
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
除了具有不同的经济模型(订阅服务而不是购买硬件和许可软件在其上运行),云的兴起还对数据系统在技术层面上的实现产生了深远的影响。术语 **云原生** 用于描述旨在利用云服务的架构。
|
||||
|
||||
原则上,几乎任何你可以自托管的软件也可以作为云服务提供,实际上,许多流行的数据系统现在都有托管服务。然而,从头开始设计为云原生的系统已被证明具有几个优势:在相同硬件上具有更好的性能、更快地从故障中恢复、能够快速扩展计算资源以匹配负载,以及支持更大的数据集 [^25] [^26] [^27]。
|
||||
[表 1-2](/en/ch1#tab_cloud_native_dbs) 列出了两种类型系统的一些示例。
|
||||
[表 1-2](/ch1#tab_cloud_native_dbs) 列出了两种类型系统的一些示例。
|
||||
|
||||
{{< figure id="#tab_cloud_native_dbs" title="表 1-2. 自托管和云原生数据库系统的示例" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -265,10 +265,10 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
|
||||
作为本地磁盘的替代方案,云服务还提供虚拟磁盘存储,可以从一个实例分离并附加到另一个实例(Amazon EBS、Azure 托管磁盘和 Google Cloud 中的持久磁盘)。这样的虚拟磁盘实际上不是物理磁盘,而是由一组单独的机器提供的云服务,它模拟磁盘(**块设备**,其中每个块通常为 4 KiB 大小)的行为。这项技术使得在云中运行传统的基于磁盘的软件成为可能,但块设备仿真引入了可以在从头开始为云设计的系统中避免的开销 [^25]。它还使应用程序对网络故障非常敏感,因为虚拟块设备上的每个 I/O 实际上都是网络调用 [^28]。
|
||||
|
||||
为了解决这个问题,云原生服务通常避免使用虚拟磁盘,而是建立在针对特定工作负载优化的专用存储服务上。对象存储服务(如 S3)专为长期存储相当大的文件而设计,大小从数百千字节到几千兆字节不等。存储在数据库中的单个行或值通常比这小得多;因此,云数据库通常在单独的服务中管理较小的值,并在对象存储中存储较大的数据块(包含许多单个值)[^26] [^29]。我们将在[第 4 章](/en/ch4#ch_storage)中看到这样做的方法。
|
||||
为了解决这个问题,云原生服务通常避免使用虚拟磁盘,而是建立在针对特定工作负载优化的专用存储服务上。对象存储服务(如 S3)专为长期存储相当大的文件而设计,大小从数百千字节到几千兆字节不等。存储在数据库中的单个行或值通常比这小得多;因此,云数据库通常在单独的服务中管理较小的值,并在对象存储中存储较大的数据块(包含许多单个值)[^26] [^29]。我们将在[第 4 章](/ch4#ch_storage)中看到这样做的方法。
|
||||
|
||||
在传统系统架构中,同一台计算机负责存储(磁盘)和计算(CPU 和 RAM),但在云原生系统中,这两个职责已经在某种程度上分离或 **解耦** [^9] [^27] [^30] [^31]:
|
||||
例如,S3 只存储文件,如果你想分析该数据,你必须在 S3 之外的某个地方运行分析代码。这意味着通过网络传输数据,我们将在["分布式与单节点系统"](/en/ch1#sec_introduction_distributed)中进一步讨论。
|
||||
例如,S3 只存储文件,如果你想分析该数据,你必须在 S3 之外的某个地方运行分析代码。这意味着通过网络传输数据,我们将在["分布式与单节点系统"](/ch1#sec_introduction_distributed)中进一步讨论。
|
||||
|
||||
此外,云原生系统通常是 **多租户** 的,这意味着不是每个客户都有一个单独的机器,而是来自多个不同客户的数据和计算由同一服务在同一共享硬件上处理 [^32]。
|
||||
|
||||
|
|
@ -280,7 +280,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
|
||||
运维的作用是确保服务可靠地交付给用户(包括配置基础设施和部署应用程序),并确保稳定的生产环境(包括监控和诊断可能影响可靠性的任何问题)。对于自托管系统,运维传统上涉及在单个机器级别上的大量工作,例如容量规划(例如,监控可用磁盘空间并在空间用完之前添加更多磁盘)、配置新机器、将服务从一台机器移动到另一台机器以及安装操作系统补丁。
|
||||
|
||||
许多云服务提供了一个隐藏实际实现服务的单个机器的 API。例如,云存储用 **计量计费** 取代了固定大小的磁盘,你可以存储数据而无需提前规划容量需求,然后根据实际使用的空间收费。此外,许多云服务保持高可用,即使单个机器发生故障(参见["可靠性和容错"](/en/ch2#sec_introduction_reliability))。
|
||||
许多云服务提供了一个隐藏实际实现服务的单个机器的 API。例如,云存储用 **计量计费** 取代了固定大小的磁盘,你可以存储数据而无需提前规划容量需求,然后根据实际使用的空间收费。此外,许多云服务保持高可用,即使单个机器发生故障(参见["可靠性和容错"](/ch2#sec_introduction_reliability))。
|
||||
|
||||
从单个机器到服务的重点转移伴随着运维角色的变化。提供可靠服务的高级目标保持不变,但流程和工具已经发展。DevOps/SRE 理念更加强调:
|
||||
|
||||
|
|
@ -298,7 +298,7 @@ Apache Hive、Spark SQL、Presto 和 Trino 是这种方法的示例。
|
|||
|
||||
采用云服务可能比运行自己的基础设施更容易、更快,尽管即使在这里,学习如何使用它也有成本,也许还要解决其限制。随着越来越多的供应商提供针对不同用例的越来越广泛的云服务,不同服务之间的集成成为一个特别的挑战 [^39] [^40]。
|
||||
|
||||
ETL(参见["数据仓库"](/en/ch1#sec_introduction_dwh))只是故事的一部分;事务型云服务也需要相互集成。目前,缺乏促进这种集成的标准,因此它通常涉及大量的手动工作。
|
||||
ETL(参见["数据仓库"](/ch1#sec_introduction_dwh))只是故事的一部分;事务型云服务也需要相互集成。目前,缺乏促进这种集成的标准,因此它通常涉及大量的手动工作。
|
||||
|
||||
其他不能完全外包给云服务的运维方面包括维护应用程序及其使用的库的安全性、管理你自己的服务之间的交互、监控服务的负载以及追踪问题的原因,例如性能下降或中断。虽然云正在改变运维的角色,但对运维的需求仍然很大。
|
||||
|
||||
|
|
@ -315,13 +315,13 @@ ETL(参见["数据仓库"](/en/ch1#sec_introduction_dwh))只是故事的一
|
|||
: 如果数据存储在一个服务中但在另一个服务中处理,则必须通过网络从一个服务传输到另一个服务。
|
||||
|
||||
容错/高可用
|
||||
: 如果你的应用程序需要继续工作,即使一台机器(或几台机器、网络或整个数据中心)宕机,你可以使用多台机器来提供冗余。当一个失败时,另一个可以接管。参见["可靠性和容错"](/en/ch2#sec_introduction_reliability)和[第 6 章](/en/ch6#ch_replication)关于复制。
|
||||
: 如果你的应用程序需要继续工作,即使一台机器(或几台机器、网络或整个数据中心)宕机,你可以使用多台机器来提供冗余。当一个失败时,另一个可以接管。参见["可靠性和容错"](/ch2#sec_introduction_reliability)和[第 6 章](/ch6#ch_replication)关于复制。
|
||||
|
||||
可扩展性
|
||||
: 如果你的数据量或计算需求增长超过单台机器可以处理的范围,你可以潜在地将负载分散到多台机器上。参见["可扩展性"](/en/ch2#sec_introduction_scalability)。
|
||||
: 如果你的数据量或计算需求增长超过单台机器可以处理的范围,你可以潜在地将负载分散到多台机器上。参见["可扩展性"](/ch2#sec_introduction_scalability)。
|
||||
|
||||
延迟
|
||||
: 如果你在世界各地都有用户,你可能希望在全球各个地区都有服务器,以便每个用户都可以从地理上靠近他们的服务器获得服务。这避免了用户必须等待网络数据包在世界各地传输才能回答他们的请求。参见["描述性能"](/en/ch2#sec_introduction_percentiles)。
|
||||
: 如果你在世界各地都有用户,你可能希望在全球各个地区都有服务器,以便每个用户都可以从地理上靠近他们的服务器获得服务。这避免了用户必须等待网络数据包在世界各地传输才能回答他们的请求。参见["描述性能"](/ch2#sec_introduction_percentiles)。
|
||||
|
||||
弹性
|
||||
: 如果你的应用程序在某些时候繁忙而在其他时候空闲,云部署可以根据需求扩大或缩小,因此你只需为正在积极使用的资源付费。这在单台机器上更困难,它需要配置以处理最大负载,即使在几乎不使用时也是如此。
|
||||
|
|
@ -340,7 +340,7 @@ ETL(参见["数据仓库"](/en/ch1#sec_introduction_dwh))只是故事的一
|
|||
|
||||
### 分布式系统的问题 {#sec_introduction_dist_sys_problems}
|
||||
|
||||
分布式系统也有缺点。通过网络进行的每个请求和 API 调用都需要处理失败的可能性:网络可能中断,或者服务可能过载或崩溃,因此任何请求都可能超时而不收到响应。在这种情况下,我们不知道服务是否收到了请求,简单地重试它可能不安全。我们将在[第 9 章](/en/ch9#ch_distributed)中详细讨论这些问题。
|
||||
分布式系统也有缺点。通过网络进行的每个请求和 API 调用都需要处理失败的可能性:网络可能中断,或者服务可能过载或崩溃,因此任何请求都可能超时而不收到响应。在这种情况下,我们不知道服务是否收到了请求,简单地重试它可能不安全。我们将在[第 9 章](/ch9#ch_distributed)中详细讨论这些问题。
|
||||
|
||||
虽然数据中心网络很快,但调用另一个服务仍然比在同一进程中调用函数慢得多 [^44]。
|
||||
|
||||
|
|
@ -351,15 +351,15 @@ ETL(参见["数据仓库"](/en/ch1#sec_introduction_dwh))只是故事的一
|
|||
故障排除分布式系统通常很困难:如果系统响应缓慢,你如何找出问题所在?在 **可观测性** 的标题下开发了诊断分布式系统问题的技术 [^47] [^48],
|
||||
这涉及收集有关系统执行的数据,并允许以允许分析高级指标和单个事件的方式对其进行查询。**追踪** 工具(如 OpenTelemetry、Zipkin 和 Jaeger)允许你跟踪哪个客户端为哪个操作调用了哪个服务器,以及每个调用花费了多长时间 [^49]。
|
||||
|
||||
数据库提供了各种机制来确保数据一致性,正如我们将在[第 6 章](/en/ch6#ch_replication)和[第 8 章](/en/ch8#ch_transactions)中看到的。然而,当每个服务都有自己的数据库时,维护这些不同服务之间数据的一致性就成了应用程序的问题。
|
||||
分布式事务(我们在[第 8 章](/en/ch8#ch_transactions)中探讨)是确保一致性的一种可能技术,但它们很少在微服务环境中使用,因为它们与使服务彼此独立的目标背道而驰,并且许多数据库不支持它们 [^50]。
|
||||
数据库提供了各种机制来确保数据一致性,正如我们将在[第 6 章](/ch6#ch_replication)和[第 8 章](/ch8#ch_transactions)中看到的。然而,当每个服务都有自己的数据库时,维护这些不同服务之间数据的一致性就成了应用程序的问题。
|
||||
分布式事务(我们在[第 8 章](/ch8#ch_transactions)中探讨)是确保一致性的一种可能技术,但它们很少在微服务环境中使用,因为它们与使服务彼此独立的目标背道而驰,并且许多数据库不支持它们 [^50]。
|
||||
|
||||
由于所有这些原因,如果你可以在单台机器上做某事,与设置分布式系统相比,这通常要简单得多,成本也低得多 [^23] [^46] [^51]。
|
||||
CPU、内存和磁盘已经变得更大、更快、更可靠。与单节点数据库(如 DuckDB、SQLite 和 KùzuDB)结合使用时,许多工作负载现在可以在单个节点上运行。我们将在[第 4 章](/en/ch4#ch_storage)中更多地探讨这个主题。
|
||||
CPU、内存和磁盘已经变得更大、更快、更可靠。与单节点数据库(如 DuckDB、SQLite 和 KùzuDB)结合使用时,许多工作负载现在可以在单个节点上运行。我们将在[第 4 章](/ch4#ch_storage)中更多地探讨这个主题。
|
||||
|
||||
### 微服务与 Serverless {#sec_introduction_microservices}
|
||||
|
||||
将系统分布在多台机器上的最常见方法是将它们分为客户端和服务器,并让客户端向服务器发出请求。最常见的是,HTTP 用于此通信,正如我们将在["通过服务的数据流:REST 和 RPC"](/en/ch5#sec_encoding_dataflow_rpc)中讨论的。同一进程可能既是服务器(处理传入请求)又是客户端(向其他服务发出出站请求)。
|
||||
将系统分布在多台机器上的最常见方法是将它们分为客户端和服务器,并让客户端向服务器发出请求。最常见的是,HTTP 用于此通信,正如我们将在["通过服务的数据流:REST 和 RPC"](/ch5#sec_encoding_dataflow_rpc)中讨论的。同一进程可能既是服务器(处理传入请求)又是客户端(向其他服务发出出站请求)。
|
||||
|
||||
这种构建应用程序的方式传统上被称为 **面向服务的架构**(SOA);最近,这个想法已经被细化为 **微服务** 架构 [^52] [^53]。
|
||||
在这种架构中,服务有一个明确定义的目的(例如,在 S3 的情况下,这将是文件存储);每个服务公开一个可以由客户端通过网络调用的 API,每个服务有一个负责其维护的团队。因此,复杂的应用程序可以分解为多个交互服务,每个服务由单独的团队管理。
|
||||
|
|
@ -368,7 +368,7 @@ CPU、内存和磁盘已经变得更大、更快、更可靠。与单节点数
|
|||
|
||||
另一方面,拥有许多服务本身可能会滋生复杂性:每个服务都需要基础设施来部署新版本、调整分配的硬件资源以匹配负载、收集日志、监控服务健康状况,并在出现问题时向值班工程师发出警报。**编排** 框架(如 Kubernetes)已成为部署服务的流行方式,因为它们为这种基础设施提供了基础。在开发期间测试服务可能会很复杂,因为你还需要运行它所依赖的所有其他服务。
|
||||
|
||||
微服务 API 的演进可能具有挑战性。调用 API 的客户端期望 API 具有某些字段。开发人员可能希望随着业务需求的变化向 API 添加或删除字段,但这样做可能会导致客户端失败。更糟糕的是,这种失败通常直到开发周期的后期才被发现,当更新的服务 API 部署到暂存或生产环境时。API 描述标准(如 OpenAPI 和 gRPC)有助于管理客户端和服务器 API 之间的关系;我们将在[第 5 章](/en/ch5#ch_encoding)中进一步讨论这些。
|
||||
微服务 API 的演进可能具有挑战性。调用 API 的客户端期望 API 具有某些字段。开发人员可能希望随着业务需求的变化向 API 添加或删除字段,但这样做可能会导致客户端失败。更糟糕的是,这种失败通常直到开发周期的后期才被发现,当更新的服务 API 部署到暂存或生产环境时。API 描述标准(如 OpenAPI 和 gRPC)有助于管理客户端和服务器 API 之间的关系;我们将在[第 5 章](/ch5#ch_encoding)中进一步讨论这些。
|
||||
|
||||
微服务主要是人员问题的技术解决方案:允许不同的团队独立取得进展,而无需相互协调。这在大公司中很有价值,但在没有很多团队的小公司中,使用微服务可能是不必要的开销,最好以最简单的方式实现应用程序 [^52]。
|
||||
|
||||
|
|
@ -392,7 +392,7 @@ CPU、内存和磁盘已经变得更大、更快、更可靠。与单节点数
|
|||
这些拓扑为具有已知通信模式的 HPC 工作负载产生更好的性能。
|
||||
* 云计算允许节点分布在多个地理区域,而超级计算机通常假设其所有节点都紧密相邻。
|
||||
|
||||
大规模分析系统有时与超级计算共享一些特征,如果你在这个领域工作,了解这些技术可能是值得的。然而,本书主要关注需要持续可用的服务,如["可靠性和容错"](/en/ch2#sec_introduction_reliability)中所讨论的。
|
||||
大规模分析系统有时与超级计算共享一些特征,如果你在这个领域工作,了解这些技术可能是值得的。然而,本书主要关注需要持续可用的服务,如["可靠性和容错"](/ch2#sec_introduction_reliability)中所讨论的。
|
||||
|
||||
## 数据系统、法律与社会 {#sec_introduction_compliance}
|
||||
|
||||
|
|
@ -405,7 +405,7 @@ CPU、内存和磁盘已经变得更大、更快、更可靠。与单节点数
|
|||
从事此类系统工作的每个人都有责任考虑道德影响并确保它们符合相关法律。没有必要让每个人都成为法律和道德专家,但对法律和道德原则的基本认识与对分布式系统的一些基础知识一样重要。
|
||||
|
||||
法律考虑正在影响数据系统设计的基础 [^61]。
|
||||
例如,GDPR 授予个人根据要求删除其数据的权利(有时称为 **被遗忘权**)。然而,正如我们将在本书中看到的,许多数据系统依赖于不可变构造(如仅追加日志)作为其设计的一部分;我们如何确保删除应该是不可变的文件中间的某些数据?我们如何处理已纳入派生数据集(参见["权威记录系统与派生数据"](/en/ch1#sec_introduction_derived))的数据的删除,例如机器学习模型的训练数据?回答这些问题会带来新的工程挑战。
|
||||
例如,GDPR 授予个人根据要求删除其数据的权利(有时称为 **被遗忘权**)。然而,正如我们将在本书中看到的,许多数据系统依赖于不可变构造(如仅追加日志)作为其设计的一部分;我们如何确保删除应该是不可变的文件中间的某些数据?我们如何处理已纳入派生数据集(参见["权威记录系统与派生数据"](/ch1#sec_introduction_derived))的数据的删除,例如机器学习模型的训练数据?回答这些问题会带来新的工程挑战。
|
||||
|
||||
目前,我们没有关于哪些特定技术或系统架构应被视为"符合 GDPR"的明确指导方针。该法规故意不强制要求特定技术,因为随着技术的进步,这些技术可能会迅速变化。相反,法律文本阐述了需要解释的高级原则。这意味着如何遵守隐私法规的问题没有简单的答案,但我们将通过这个视角来看待本书中的一些技术。
|
||||
|
||||
|
|
@ -424,11 +424,11 @@ CPU、内存和磁盘已经变得更大、更快、更可靠。与单节点数
|
|||
|
||||
本章的主题是理解权衡:也就是说,认识到对于许多问题没有一个正确的答案,而是有几种不同的方法,每种方法都有各种利弊。我们探讨了影响数据系统架构的一些最重要的选择,并介绍了本书其余部分所需的术语。
|
||||
|
||||
我们首先区分了事务型(事务处理,OLTP)和分析型(OLAP)系统,并看到了它们的不同特征:不仅管理具有不同访问模式的不同类型的数据,而且还服务于不同的受众。我们遇到了数据仓库和数据湖的概念,它们通过 ETL 从事务型系统接收数据馈送。在[第 4 章](/en/ch4#ch_storage)中,我们将看到事务型和分析型系统由于需要服务的不同类型的查询,通常使用非常不同的内部数据布局。
|
||||
我们首先区分了事务型(事务处理,OLTP)和分析型(OLAP)系统,并看到了它们的不同特征:不仅管理具有不同访问模式的不同类型的数据,而且还服务于不同的受众。我们遇到了数据仓库和数据湖的概念,它们通过 ETL 从事务型系统接收数据馈送。在[第 4 章](/ch4#ch_storage)中,我们将看到事务型和分析型系统由于需要服务的不同类型的查询,通常使用非常不同的内部数据布局。
|
||||
|
||||
然后,我们将云服务(一个相对较新的发展)与传统的自托管软件范式进行了比较,后者以前主导着数据系统架构。这些方法中哪一个更具成本效益在很大程度上取决于你的特定情况,但不可否认的是,云原生方法正在为数据系统的架构带来重大变化,例如它们分离存储和计算的方式。
|
||||
|
||||
云系统本质上是分布式的,我们简要地研究了分布式系统与使用单台机器相比的一些权衡。在某些情况下,你无法避免分布式,但如果可能将其保留在单台机器上,建议不要急于使系统分布式。在[第 9 章](/en/ch9#ch_distributed)中,我们将更详细地介绍分布式系统的挑战。
|
||||
云系统本质上是分布式的,我们简要地研究了分布式系统与使用单台机器相比的一些权衡。在某些情况下,你无法避免分布式,但如果可能将其保留在单台机器上,建议不要急于使系统分布式。在[第 9 章](/ch9#ch_distributed)中,我们将更详细地介绍分布式系统的挑战。
|
||||
|
||||
最后,我们看到数据系统架构不仅由部署系统的业务需求决定,还由保护其数据被处理的人的权利的隐私法规决定——这是许多工程师倾向于忽视的一个方面。我们如何将法律要求转化为技术实现还不太清楚,但在我们浏览本书的其余部分时,记住这个问题很重要。
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ breadcrumbs: false
|
|||
|
||||
许多非功能性需求,如安全性,超出了本书的范围。但是有一些非功能性需求我们将考虑,本章将帮助你为自己的系统阐明它们:
|
||||
|
||||
* 如何定义和衡量系统的 **性能**(参见 ["描述性能"](/en/ch2#sec_introduction_percentiles));
|
||||
* 服务 **可靠** 意味着什么——即使在出现问题时也能继续正确工作(参见 ["可靠性和容错"](/en/ch2#sec_introduction_reliability));
|
||||
* 通过在系统负载增长时有效添加计算能力的方法,使系统具有 **可扩展性**(参见 ["可扩展性"](/en/ch2#sec_introduction_scalability));以及
|
||||
* 使系统在长期内更容易维护(参见 ["可维护性"](/en/ch2#sec_introduction_maintainability))。
|
||||
* 如何定义和衡量系统的 **性能**(参见 ["描述性能"](/ch2#sec_introduction_percentiles));
|
||||
* 服务 **可靠** 意味着什么——即使在出现问题时也能继续正确工作(参见 ["可靠性和容错"](/ch2#sec_introduction_reliability));
|
||||
* 通过在系统负载增长时有效添加计算能力的方法,使系统具有 **可扩展性**(参见 ["可扩展性"](/ch2#sec_introduction_scalability));以及
|
||||
* 使系统在长期内更容易维护(参见 ["可维护性"](/ch2#sec_introduction_maintainability))。
|
||||
|
||||
本章介绍的术语在后续章节中也很有用,当我们深入了解数据密集型系统的实现细节时。然而,抽象定义可能相当枯燥;为了让这些想法更具体,我们将以一个社交网络服务如何工作的案例研究开始本章,这将提供性能和可扩展性的实际示例。
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ breadcrumbs: false
|
|||
|
||||
### 表示用户、帖子与关注关系 {#id20}
|
||||
|
||||
想象我们将所有数据保存在关系数据库中,如 [图 2-1](/en/ch2#fig_twitter_relational) 所示。我们有一个用户表,一个帖子表,和一个关注关系表。
|
||||
想象我们将所有数据保存在关系数据库中,如 [图 2-1](/ch2#fig_twitter_relational) 所示。我们有一个用户表,一个帖子表,和一个关注关系表。
|
||||
|
||||
{{< figure src="/fig/ddia_0201.png" id="fig_twitter_relational" caption="图 2-1. 用户可以相互关注的社交网络的简单关系模式。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ SELECT posts.*, users.* FROM posts
|
|||
|
||||
想象一下,对于每个用户,我们存储一个包含其主页时间线的数据结构,即他们关注的人的最近帖子。每次用户发布帖子时,我们查找他们的所有粉丝,并将该帖子插入每个粉丝的主页时间线——就像将消息投递到邮箱一样。现在当用户登录时,我们可以简单地给他们我们预先计算的主页时间线。此外,要接收有关其时间线上任何新帖子的通知,用户的客户端只需订阅添加到其主页时间线的帖子流。
|
||||
|
||||
这种方法的缺点是,每次用户发布帖子时,我们现在需要做更多的工作,因为主页时间线是需要更新的派生数据。该过程如 [图 2-2](/en/ch2#fig_twitter_timelines) 所示。当一个初始请求导致执行几个下游请求时,我们使用术语 **扇出** 来描述请求数量增加的因子。
|
||||
这种方法的缺点是,每次用户发布帖子时,我们现在需要做更多的工作,因为主页时间线是需要更新的派生数据。该过程如 [图 2-2](/ch2#fig_twitter_timelines) 所示。当一个初始请求导致执行几个下游请求时,我们使用术语 **扇出** 来描述请求数量增加的因子。
|
||||
|
||||
{{< figure src="/fig/ddia_0202.png" id="fig_twitter_timelines" caption="图 2-2. 扇出:将新帖子投递给发布帖子的用户的每个粉丝。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ SELECT posts.*, users.* FROM posts
|
|||
|
||||
在社交网络案例研究中,"每秒帖子数"和"每秒时间线写入数"是吞吐量指标,而"加载主页时间线所需的时间"或"帖子投递给粉丝所需的时间"是响应时间指标。
|
||||
|
||||
吞吐量和响应时间之间通常存在联系;[图 2-3](/en/ch2#fig_throughput) 勾画了在线服务的这种关系示例。当请求吞吐量低时,服务的响应时间低,但随着负载增加,响应时间增加。这是因为 **排队**:当请求到达高负载系统时,CPU 很可能已经在处理较早的请求,因此传入的请求需要等待较早的请求完成。随着吞吐量接近硬件可以处理的最大值,排队延迟急剧增加。
|
||||
吞吐量和响应时间之间通常存在联系;[图 2-3](/ch2#fig_throughput) 勾画了在线服务的这种关系示例。当请求吞吐量低时,服务的响应时间低,但随着负载增加,响应时间增加。这是因为 **排队**:当请求到达高负载系统时,CPU 很可能已经在处理较早的请求,因此传入的请求需要等待较早的请求完成。随着吞吐量接近硬件可以处理的最大值,排队延迟急剧增加。
|
||||
|
||||
{{< figure src="/fig/ddia_0203.png" id="fig_throughput" caption="图 2-3. 当服务的吞吐量接近其容量时,由于排队,响应时间急剧增加。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -100,11 +100,11 @@ SELECT posts.*, users.* FROM posts
|
|||
|
||||
就性能指标而言,响应时间通常是用户最关心的,而吞吐量决定了所需的计算资源(例如,你需要多少服务器),因此决定了服务特定工作负载的成本。如果吞吐量可能增长超过当前硬件可以处理的范围,则需要扩展容量;如果通过添加计算资源可以显著增加其最大吞吐量,则称系统是 **可扩展的**。
|
||||
|
||||
在本节中,我们将主要关注响应时间,我们将在 ["可扩展性"](/en/ch2#sec_introduction_scalability) 中回到吞吐量和可扩展性。
|
||||
在本节中,我们将主要关注响应时间,我们将在 ["可扩展性"](/ch2#sec_introduction_scalability) 中回到吞吐量和可扩展性。
|
||||
|
||||
### 延迟与响应时间 {#id23}
|
||||
|
||||
"延迟"和"响应时间"有时可以互换使用,但在本书中,我们将以特定方式使用这些术语(如 [图 2-4](/en/ch2#fig_response_time) 所示):
|
||||
"延迟"和"响应时间"有时可以互换使用,但在本书中,我们将以特定方式使用这些术语(如 [图 2-4](/ch2#fig_response_time) 所示):
|
||||
|
||||
* **响应时间** 是客户端看到的;它包括系统中任何地方产生的所有延迟。
|
||||
* **服务时间** 是服务主动处理用户请求的持续时间。
|
||||
|
|
@ -113,15 +113,15 @@ SELECT posts.*, users.* FROM posts
|
|||
|
||||
{{< figure src="/fig/ddia_0204.png" id="fig_response_time" caption="图 2-4. 响应时间、服务时间、网络延迟和排队延迟。" class="w-full my-4" >}}
|
||||
|
||||
在 [图 2-4](/en/ch2#fig_response_time) 中,时间从左到右流动,每个通信节点显示为水平线,请求或响应消息显示为从一个节点到另一个节点的粗对角箭头。你将在本书的过程中经常遇到这种风格的图表。
|
||||
在 [图 2-4](/ch2#fig_response_time) 中,时间从左到右流动,每个通信节点显示为水平线,请求或响应消息显示为从一个节点到另一个节点的粗对角箭头。你将在本书的过程中经常遇到这种风格的图表。
|
||||
|
||||
响应时间可能从一个请求到下一个请求显著变化,即使你一遍又一遍地发出相同的请求。许多因素可能会增加随机延迟:例如,上下文切换到后台进程、网络数据包丢失和 TCP 重传、垃圾回收暂停、强制从磁盘读取的页面错误、服务器机架中的机械振动 [^17],或许多其他原因。我们将在 ["超时和无界延迟"](/en/ch9#sec_distributed_queueing) 中更详细地讨论这个主题。
|
||||
响应时间可能从一个请求到下一个请求显著变化,即使你一遍又一遍地发出相同的请求。许多因素可能会增加随机延迟:例如,上下文切换到后台进程、网络数据包丢失和 TCP 重传、垃圾回收暂停、强制从磁盘读取的页面错误、服务器机架中的机械振动 [^17],或许多其他原因。我们将在 ["超时和无界延迟"](/ch9#sec_distributed_queueing) 中更详细地讨论这个主题。
|
||||
|
||||
排队延迟通常占响应时间变化的很大一部分。由于服务器只能并行处理少量事物(例如,受其 CPU 核心数量的限制),只需要少量慢速请求就可以阻止后续请求的处理——这种效应称为 **队头阻塞**。即使那些后续请求具有快速的服务时间,由于等待先前请求完成的时间,客户端也会看到缓慢的整体响应时间。排队延迟不是服务时间的一部分,因此在客户端测量响应时间很重要。
|
||||
|
||||
### 平均值、中位数与百分位数 {#id24}
|
||||
|
||||
因为响应时间从一个请求到下一个请求各不相同,我们需要将其视为不是单个数字,而是可以测量的值的 **分布**。在 [图 2-5](/en/ch2#fig_lognormal) 中,每个灰色条代表对服务的请求,其高度显示该请求花费了多长时间。大多数请求相当快,但偶尔有 **异常值** 需要更长时间。网络延迟的变化也称为 **抖动**。
|
||||
因为响应时间从一个请求到下一个请求各不相同,我们需要将其视为不是单个数字,而是可以测量的值的 **分布**。在 [图 2-5](/ch2#fig_lognormal) 中,每个灰色条代表对服务的请求,其高度显示该请求花费了多长时间。大多数请求相当快,但偶尔有 **异常值** 需要更长时间。网络延迟的变化也称为 **抖动**。
|
||||
|
||||
{{< figure src="/fig/ddia_0205.png" id="fig_lognormal" caption="图 2-5. 说明平均值和百分位数:对服务的 100 个请求样本的响应时间。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ SELECT posts.*, users.* FROM posts
|
|||
|
||||
通常使用 **百分位数** 会更好。如果你将响应时间列表从最快到最慢排序,那么 **中位数** 是中间点:例如,如果你的中位数响应时间是 200 毫秒,这意味着你的一半请求在不到 200 毫秒内返回,一半请求需要更长时间。这使中位数成为一个很好的指标,如果你想知道用户通常需要等待多长时间。中位数也称为 **第 50 百分位数**,有时缩写为 **p50**。
|
||||
|
||||
为了弄清楚你的异常值有多糟糕,你可以查看更高的百分位数:**第 95**、**第 99** 和 **第 99.9** 百分位数是常见的(缩写为 **p95**、**p99** 和 **p999**)。它们是响应时间阈值,95%、99% 或 99.9% 的请求比该特定阈值快。例如,如果第 95 百分位响应时间是 1.5 秒,这意味着 100 个请求中有 95 个需要不到 1.5 秒,100 个请求中有 5 个需要 1.5 秒或更长时间。这在 [图 2-5](/en/ch2#fig_lognormal) 中有所说明。
|
||||
为了弄清楚你的异常值有多糟糕,你可以查看更高的百分位数:**第 95**、**第 99** 和 **第 99.9** 百分位数是常见的(缩写为 **p95**、**p99** 和 **p999**)。它们是响应时间阈值,95%、99% 或 99.9% 的请求比该特定阈值快。例如,如果第 95 百分位响应时间是 1.5 秒,这意味着 100 个请求中有 95 个需要不到 1.5 秒,100 个请求中有 5 个需要 1.5 秒或更长时间。这在 [图 2-5](/ch2#fig_lognormal) 中有所说明。
|
||||
|
||||
响应时间的高百分位数,也称为 **尾延迟**,很重要,因为它们直接影响用户对服务的体验。例如,亚马逊根据第 99.9 百分位数描述内部服务的响应时间要求,即使它只影响 1,000 个请求中的 1 个。这是因为请求最慢的客户通常是那些在其帐户上拥有最多数据的客户,因为他们进行了许多购买——也就是说,他们是最有价值的客户 [^19]。通过确保网站对他们来说速度快,保持这些客户满意很重要。
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
### 响应时间指标的应用 {#sec_introduction_slo_sla}
|
||||
|
||||
高百分位数在后端服务中尤其重要,这些服务在服务单个最终用户请求时被多次调用。即使你并行进行调用,最终用户请求仍然需要等待最慢的并行调用完成。只需要一个慢速调用就能使整个最终用户请求变慢,如 [图 2-6](/en/ch2#fig_tail_amplification) 所示。即使只有一小部分后端调用很慢,如果最终用户请求需要多个后端调用,获得慢速调用的机会也会增加,因此更高比例的最终用户请求最终会变慢(一种称为 **尾延迟放大** 的效应 [^26])。
|
||||
高百分位数在后端服务中尤其重要,这些服务在服务单个最终用户请求时被多次调用。即使你并行进行调用,最终用户请求仍然需要等待最慢的并行调用完成。只需要一个慢速调用就能使整个最终用户请求变慢,如 [图 2-6](/ch2#fig_tail_amplification) 所示。即使只有一小部分后端调用很慢,如果最终用户请求需要多个后端调用,获得慢速调用的机会也会增加,因此更高比例的最终用户请求最终会变慢(一种称为 **尾延迟放大** 的效应 [^26])。
|
||||
|
||||
{{< figure src="/fig/ddia_0206.png" id="fig_tail_amplification" caption="图 2-6. 当需要几个后端调用来服务请求时,只需要一个慢速后端请求就能减慢整个最终用户请求。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -219,11 +219,11 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
当组件故障是独立的时,冗余最有效,即一个故障的发生不会改变另一个故障发生的可能性。然而,经验表明,组件故障之间通常存在显著相关性 [^41] [^57] [^58];整个服务器机架或整个数据中心的不可用仍然比我们希望的更频繁。
|
||||
|
||||
硬件冗余增加了单台机器的正常运行时间;然而,如 ["分布式与单节点系统"](/en/ch1#sec_introduction_distributed) 中所讨论的,使用分布式系统有一些优势,例如能够容忍一个数据中心的完全中断。出于这个原因,云系统倾向于较少关注单个机器的可靠性,而是旨在通过在软件级别容忍故障节点来使服务高度可用。云提供商使用 **可用区** 来识别哪些资源在物理上位于同一位置;同一地方的资源比地理上分离的资源更有可能同时故障。
|
||||
硬件冗余增加了单台机器的正常运行时间;然而,如 ["分布式与单节点系统"](/ch1#sec_introduction_distributed) 中所讨论的,使用分布式系统有一些优势,例如能够容忍一个数据中心的完全中断。出于这个原因,云系统倾向于较少关注单个机器的可靠性,而是旨在通过在软件级别容忍故障节点来使服务高度可用。云提供商使用 **可用区** 来识别哪些资源在物理上位于同一位置;同一地方的资源比地理上分离的资源更有可能同时故障。
|
||||
|
||||
我们在本书中讨论的容错技术旨在容忍整个机器、机架或可用区的丢失。它们通常通过允许一个数据中心的机器在另一个数据中心的机器故障或变得无法访问时接管来工作。我们将在 [第 6 章](/en/ch6#ch_replication)、[第 10 章](/en/ch10#ch_consistency) 以及本书的其他各个点讨论这种容错技术。
|
||||
我们在本书中讨论的容错技术旨在容忍整个机器、机架或可用区的丢失。它们通常通过允许一个数据中心的机器在另一个数据中心的机器故障或变得无法访问时接管来工作。我们将在 [第 6 章](/ch6#ch_replication)、[第 10 章](/ch10#ch_consistency) 以及本书的其他各个点讨论这种容错技术。
|
||||
|
||||
可以容忍整个机器丢失的系统也具有操作优势:如果你需要重新启动机器(例如,应用操作系统安全补丁),单服务器系统需要计划停机时间,而多节点容错系统可以通过一次重新启动一个节点来打补丁,而不会影响用户的服务。这称为 **滚动升级**,我们将在 [第 5 章](/en/ch5#ch_encoding) 中进一步讨论它。
|
||||
可以容忍整个机器丢失的系统也具有操作优势:如果你需要重新启动机器(例如,应用操作系统安全补丁),单服务器系统需要计划停机时间,而多节点容错系统可以通过一次重新启动一个节点来打补丁,而不会影响用户的服务。这称为 **滚动升级**,我们将在 [第 5 章](/ch5#ch_encoding) 中进一步讨论它。
|
||||
|
||||
#### 软件故障 {#software-faults}
|
||||
|
||||
|
|
@ -237,7 +237,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
导致这些软件故障的错误通常会潜伏很长时间,直到它们被一组不寻常的情况触发。在这些情况下,会发现软件对其环境做出了某种假设——虽然该假设通常是正确的,但由于某种原因它最终不再正确 [^68] [^69]。
|
||||
|
||||
软件中系统性故障的问题没有快速解决方案。许多小事情可以帮助:仔细考虑系统中的假设和交互;彻底测试;进程隔离;允许进程崩溃并重新启动;避免反馈循环,如重试风暴(参见 ["当过载系统无法恢复时"](/en/ch2#sidebar_metastable));测量、监控和分析生产中的系统行为。
|
||||
软件中系统性故障的问题没有快速解决方案。许多小事情可以帮助:仔细考虑系统中的假设和交互;彻底测试;进程隔离;允许进程崩溃并重新启动;避免反馈循环,如重试风暴(参见 ["当过载系统无法恢复时"](/ch2#sidebar_metastable));测量、监控和分析生产中的系统行为。
|
||||
|
||||
### 人类与可靠性 {#id31}
|
||||
|
||||
|
|
@ -245,7 +245,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
将这些问题标记为"人为错误"并希望通过更严格的程序和规则合规来更好地控制人类行为来解决它们是很诱人的。然而,为错误责怪人们是适得其反的。我们所说的"人为错误"实际上不是事件的原因,而是人们尽力完成工作的社会技术系统中问题的症状 [^71]。通常,复杂系统具有紧急行为,其中组件之间的意外交互也可能导致故障 [^72]。
|
||||
|
||||
各种技术措施可以帮助最小化人为错误的影响,包括彻底测试(手写测试和对大量随机输入的 **属性测试**)[^38]、用于快速恢复配置更改的回滚机制、新代码的逐步推出、详细而清晰的监控、用于诊断生产问题的可观察性工具(参见 ["分布式系统的问题"](/en/ch1#sec_introduction_dist_sys_problems)),以及设计良好的接口,鼓励"正确的事情"并阻止"错误的事情"。
|
||||
各种技术措施可以帮助最小化人为错误的影响,包括彻底测试(手写测试和对大量随机输入的 **属性测试**)[^38]、用于快速恢复配置更改的回滚机制、新代码的逐步推出、详细而清晰的监控、用于诊断生产问题的可观察性工具(参见 ["分布式系统的问题"](/ch1#sec_introduction_dist_sys_problems)),以及设计良好的接口,鼓励"正确的事情"并阻止"错误的事情"。
|
||||
|
||||
然而,这些事情需要时间和金钱的投资,在日常业务的实用现实中,组织通常优先考虑创收活动,而不是增加其对错误的弹性的措施。如果在更多功能和更多测试之间有选择,许多组织可以理解地选择功能。鉴于这种选择,当不可避免地发生可预防的错误时,责怪犯错误的人是没有意义的——问题是组织的优先事项。
|
||||
|
||||
|
|
@ -285,7 +285,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
### 描述负载 {#id33}
|
||||
|
||||
首先,我们需要简洁地描述系统上的当前负载;只有这样我们才能讨论增长问题(如果我们的负载翻倍会发生什么?)。通常这将是吞吐量的度量:例如,服务的每秒请求数、每天到达的新数据的千兆字节数,或每小时购物车结账的数量。有时你关心某些可变数量的峰值,例如 ["案例研究:社交网络主页时间线"](/en/ch2#sec_introduction_twitter) 中同时在线用户的数量。
|
||||
首先,我们需要简洁地描述系统上的当前负载;只有这样我们才能讨论增长问题(如果我们的负载翻倍会发生什么?)。通常这将是吞吐量的度量:例如,服务的每秒请求数、每天到达的新数据的千兆字节数,或每小时购物车结账的数量。有时你关心某些可变数量的峰值,例如 ["案例研究:社交网络主页时间线"](/ch2#sec_introduction_twitter) 中同时在线用户的数量。
|
||||
|
||||
通常,负载的其他统计特征也会影响访问模式,从而影响可扩展性要求。例如,你可能需要知道数据库中读取与写入的比率、缓存的命中率或每个用户的数据项数(例如,社交网络案例研究中的粉丝数)。也许平均情况对你很重要,或者你的瓶颈可能由少数极端情况主导。这一切都取决于你特定应用程序的细节。
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
* 当你以某种方式增加负载并保持系统资源(CPU、内存、网络带宽等)不变时,你的系统性能如何受到影响?
|
||||
* 当你以某种方式增加负载时,如果你想保持性能不变,你需要增加多少资源?
|
||||
|
||||
通常我们的目标是在满足 SLA 要求的同时保持系统的性能(参见 ["响应时间指标的使用"](/en/ch2#sec_introduction_slo_sla)),同时最小化运行系统的成本。所需的计算资源越多,成本就越高。可能某些类型的硬件比其他类型更具成本效益,随着新型硬件的出现,这些因素可能会随着时间而改变。
|
||||
通常我们的目标是在满足 SLA 要求的同时保持系统的性能(参见 ["响应时间指标的使用"](/ch2#sec_introduction_slo_sla)),同时最小化运行系统的成本。所需的计算资源越多,成本就越高。可能某些类型的硬件比其他类型更具成本效益,随着新型硬件的出现,这些因素可能会随着时间而改变。
|
||||
|
||||
如果你可以加倍资源以处理两倍的负载,同时保持性能相同,我们说你有 **线性可扩展性**,这被认为是一件好事。偶尔可以用少于两倍的资源处理两倍的负载,这是由于规模经济或峰值负载的更好分布 [^79] [^80]。更有可能的是,成本增长速度快于线性,效率低下可能有很多原因。例如,如果你有大量数据,那么处理单个写入请求可能涉及比你有少量数据时更多的工作,即使请求的大小相同。
|
||||
|
||||
|
|
@ -308,9 +308,9 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
相比之下,**无共享架构** [^82](也称为 **水平扩展** 或 **横向扩展**)已经获得了很大的普及。在这种方法中,我们使用具有多个节点的分布式系统,每个节点都有自己的 CPU、RAM 和磁盘。节点之间的任何协调都在软件级别通过传统网络完成。
|
||||
|
||||
无共享的优点是它具有线性扩展的潜力,它可以使用提供最佳性价比的任何硬件(特别是在云中),它可以在负载增加或减少时更轻松地调整其硬件资源,并且它可以通过在多个数据中心和区域分布系统来实现更高的容错性。缺点是它需要显式分片(参见 [第 7 章](/en/ch7#ch_sharding)),并且会产生分布式系统的所有复杂性([第 9 章](/en/ch9#ch_distributed))。
|
||||
无共享的优点是它具有线性扩展的潜力,它可以使用提供最佳性价比的任何硬件(特别是在云中),它可以在负载增加或减少时更轻松地调整其硬件资源,并且它可以通过在多个数据中心和区域分布系统来实现更高的容错性。缺点是它需要显式分片(参见 [第 7 章](/ch7#ch_sharding)),并且会产生分布式系统的所有复杂性([第 9 章](/ch9#ch_distributed))。
|
||||
|
||||
一些云原生数据库系统使用单独的服务进行存储和事务执行(参见 ["存储和计算分离"](/en/ch1#sec_introduction_storage_compute)),多个计算节点共享对同一存储服务的访问。该模型与共享磁盘架构有一些相似之处,但它避免了旧系统的可扩展性问题:它不提供文件系统(NAS)或块设备(SAN)抽象,而是提供专门为数据库的特定需求设计的专用 API [^83]。
|
||||
一些云原生数据库系统使用单独的服务进行存储和事务执行(参见 ["存储和计算分离"](/ch1#sec_introduction_storage_compute)),多个计算节点共享对同一存储服务的访问。该模型与共享磁盘架构有一些相似之处,但它避免了旧系统的可扩展性问题:它不提供文件系统(NAS)或块设备(SAN)抽象,而是提供专门为数据库的特定需求设计的专用 API [^83]。
|
||||
|
||||
### 可伸缩性原则 {#id35}
|
||||
|
||||
|
|
@ -318,9 +318,9 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
此外,适合一个负载级别的架构不太可能应对 10 倍的负载。如果你正在处理快速增长的服务,因此你可能需要在每个数量级负载增加时重新考虑你的架构。由于应用程序的需求可能会发展,通常不值得提前规划未来的扩展需求超过一个数量级。
|
||||
|
||||
可扩展性的一个良好通用原则是将系统分解为可以在很大程度上相互独立运行的较小组件。这是微服务背后的基本原则(参见 ["微服务和无服务器"](/en/ch1#sec_introduction_microservices))、分片([第 7 章](/en/ch7#ch_sharding))、流处理([Link to Come])和无共享架构。然而,挑战在于知道在哪里划定应该在一起的事物和应该分开的事物之间的界限。微服务的设计指南可以在其他书籍中找到 [^84],我们在 [第 7 章](/en/ch7#ch_sharding) 中讨论无共享系统的分片。
|
||||
可扩展性的一个良好通用原则是将系统分解为可以在很大程度上相互独立运行的较小组件。这是微服务背后的基本原则(参见 ["微服务和无服务器"](/ch1#sec_introduction_microservices))、分片([第 7 章](/ch7#ch_sharding))、流处理([Link to Come])和无共享架构。然而,挑战在于知道在哪里划定应该在一起的事物和应该分开的事物之间的界限。微服务的设计指南可以在其他书籍中找到 [^84],我们在 [第 7 章](/ch7#ch_sharding) 中讨论无共享系统的分片。
|
||||
|
||||
另一个好原则是不要让事情变得比必要的更复杂。如果单机数据库可以完成工作,它可能比复杂的分布式设置更可取。自动扩展系统(根据需求自动添加或删除资源)很酷,但如果你的负载相当可预测,手动扩展的系统可能会有更少的操作意外(参见 ["操作:自动或手动再平衡"](/en/ch7#sec_sharding_operations))。具有五个服务的系统比具有五十个服务的系统更简单。良好的架构通常涉及方法的实用混合。
|
||||
另一个好原则是不要让事情变得比必要的更复杂。如果单机数据库可以完成工作,它可能比复杂的分布式设置更可取。自动扩展系统(根据需求自动添加或删除资源)很酷,但如果你的负载相当可预测,手动扩展的系统可能会有更少的操作意外(参见 ["操作:自动或手动再平衡"](/ch7#sec_sharding_operations))。具有五个服务的系统比具有五十个服务的系统更简单。良好的架构通常涉及方法的实用混合。
|
||||
|
||||
## 可运维性 {#sec_introduction_maintainability}
|
||||
|
||||
|
|
@ -343,7 +343,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
### 可运维性:让运维更轻松 {#id37}
|
||||
|
||||
我们之前在 ["云时代的运维"](/en/ch1#sec_introduction_operations) 中讨论了运维的作用,我们看到人类流程对于可靠运维至少与软件工具一样重要。事实上,有人建议"良好的运维通常可以解决糟糕(或不完整)软件的限制,但良好的软件无法通过糟糕的运维可靠地运行" [^60]。
|
||||
我们之前在 ["云时代的运维"](/ch1#sec_introduction_operations) 中讨论了运维的作用,我们看到人类流程对于可靠运维至少与软件工具一样重要。事实上,有人建议"良好的运维通常可以解决糟糕(或不完整)软件的限制,但良好的软件无法通过糟糕的运维可靠地运行" [^60]。
|
||||
|
||||
在由数千台机器组成的大规模系统中,手动维护将是不合理的昂贵,自动化是必不可少的。然而,自动化可能是一把双刃剑:总会有边缘情况(例如罕见的故障场景)需要运维团队的手动干预。由于无法自动处理的情况是最复杂的问题,更大的自动化需要一个 **更** 熟练的运维团队来解决这些问题 [^88]。
|
||||
|
||||
|
|
@ -351,7 +351,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒使电子
|
|||
|
||||
良好的可操作性意味着使例行任务变得容易,允许运维团队将精力集中在高价值活动上。数据系统可以做各种事情来使例行任务变得容易,包括 [^89]:
|
||||
|
||||
* 允许监控工具检查系统的关键指标,并支持可观察性工具(参见 ["分布式系统的问题"](/en/ch1#sec_introduction_dist_sys_problems))以深入了解系统的运行时行为。各种商业和开源工具可以在这里提供帮助 [^90]。
|
||||
* 允许监控工具检查系统的关键指标,并支持可观察性工具(参见 ["分布式系统的问题"](/ch1#sec_introduction_dist_sys_problems))以深入了解系统的运行时行为。各种商业和开源工具可以在这里提供帮助 [^90]。
|
||||
* 避免对单个机器的依赖(允许在系统作为一个整体继续不间断运行的同时关闭机器进行维护)
|
||||
* 提供良好的文档和易于理解的操作模型("如果我做 X,Y 将发生")
|
||||
* 提供良好的默认行为,但也给管理员在需要时覆盖默认值的自由
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ breadcrumbs: false
|
|||
|
||||
在最基础的层面上,数据库需要做两件事:当你给它一些数据时,它应该存储这些数据,当你稍后再次询问时,它应该把数据返回给你。
|
||||
|
||||
在 [第三章](/en/ch3#ch_datamodels) 中,我们讨论了数据模型和查询语言——即你向数据库提供数据的格式,以及稍后再次请求数据的接口。在本章中,我们从数据库的角度讨论同样的问题:数据库如何存储你提供给它的数据,以及当你请求时如何再次找到数据。
|
||||
在 [第三章](/ch3#ch_datamodels) 中,我们讨论了数据模型和查询语言——即你向数据库提供数据的格式,以及稍后再次请求数据的接口。在本章中,我们从数据库的角度讨论同样的问题:数据库如何存储你提供给它的数据,以及当你请求时如何再次找到数据。
|
||||
|
||||
作为应用程序开发人员,为什么要关心数据库内部如何处理存储和检索?你可能不会从头开始实现自己的存储引擎,但你 *确实* 需要从众多可用的存储引擎中选择一个适合你应用程序的。为了配置存储引擎在你的工作负载类型上表现良好,你需要对存储引擎在底层做什么有个大致的了解。
|
||||
|
||||
特别是,针对事务性工作负载(OLTP)优化的存储引擎与针对分析优化的存储引擎之间存在很大差异(我们在 ["分析与操作系统"](/en/ch1#sec_introduction_analytics) 中介绍了这种区别)。本章首先研究两种 OLTP 存储引擎系列:写出不可变数据文件的 *日志结构* 存储引擎,以及像 *B 树* 这样就地更新数据的存储引擎。这些结构既用于键值存储,也用于二级索引。
|
||||
特别是,针对事务性工作负载(OLTP)优化的存储引擎与针对分析优化的存储引擎之间存在很大差异(我们在 ["分析与操作系统"](/ch1#sec_introduction_analytics) 中介绍了这种区别)。本章首先研究两种 OLTP 存储引擎系列:写出不可变数据文件的 *日志结构* 存储引擎,以及像 *B 树* 这样就地更新数据的存储引擎。这些结构既用于键值存储,也用于二级索引。
|
||||
|
||||
稍后在 ["分析型数据存储"](/en/ch4#sec_storage_analytics) 中,我们将讨论一系列针对分析优化的存储引擎,在 ["多维和全文索引"](/en/ch4#sec_storage_multidimensional) 中,我们将简要介绍用于更高级查询(如文本检索)的索引。
|
||||
稍后在 ["分析型数据存储"](/ch4#sec_storage_analytics) 中,我们将讨论一系列针对分析优化的存储引擎,在 ["多维和全文索引"](/ch4#sec_storage_multidimensional) 中,我们将简要介绍用于更高级查询(如文本检索)的索引。
|
||||
|
||||
## OLTP 系统的存储与索引 {#sec_storage_oltp}
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ $ cat database
|
|||
|
||||
### 日志结构存储 {#sec_storage_log_structured}
|
||||
|
||||
首先,让我们假设你想继续将数据存储在 `db_set` 写入的仅追加文件中,只是想加速读取。你可以通过在内存中保留一个哈希映射来做到这一点,其中每个键都映射到文件中可以找到该键最新值的字节偏移量,如 [图 4-1](/en/ch4#fig_storage_csv_hash_index) 所示。
|
||||
首先,让我们假设你想继续将数据存储在 `db_set` 写入的仅追加文件中,只是想加速读取。你可以通过在内存中保留一个哈希映射来做到这一点,其中每个键都映射到文件中可以找到该键最新值的字节偏移量,如 [图 4-1](/ch4#fig_storage_csv_hash_index) 所示。
|
||||
|
||||
{{< figure src="/fig/ddia_0401.png" id="fig_storage_csv_hash_index" caption="图 4-1. 以类似 CSV 格式存储键值对日志,使用内存中的哈希映射进行索引。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -100,15 +100,15 @@ $ cat database
|
|||
|
||||
#### SSTable 文件格式 {#the-sstable-file-format}
|
||||
|
||||
实际上,哈希表很少用于数据库索引,相反,保持数据按 *键排序* 的结构更为常见 [^3]。这种结构的一个例子是 *排序字符串表*,简称 *SSTable*,如 [图 4-2](/en/ch4#fig_storage_sstable_index) 所示。这种文件格式也存储键值对,但它确保它们按键排序,每个键在文件中只出现一次。
|
||||
实际上,哈希表很少用于数据库索引,相反,保持数据按 *键排序* 的结构更为常见 [^3]。这种结构的一个例子是 *排序字符串表*,简称 *SSTable*,如 [图 4-2](/ch4#fig_storage_sstable_index) 所示。这种文件格式也存储键值对,但它确保它们按键排序,每个键在文件中只出现一次。
|
||||
|
||||
{{< figure src="/fig/ddia_0402.png" id="fig_storage_sstable_index" caption="图 4-2. 带有稀疏索引的 SSTable,允许查询跳转到正确的块。" class="w-full my-4" >}}
|
||||
|
||||
现在你不需要在内存中保留所有键:你可以将 SSTable 中的键值对分组到几千字节的 *块* 中,然后在索引中存储每个块的第一个键。这种只存储部分键的索引称为 *稀疏* 索引。该索引存储在 SSTable 的单独部分中,例如使用不可变 B 树、trie 或其他允许查询快速查找特定键的数据结构 [^4]。
|
||||
|
||||
例如,在 [图 4-2](/en/ch4#fig_storage_sstable_index) 中,一个块的第一个键是 `handbag`,下一个块的第一个键是 `handsome`。现在假设你要查找键 `handiwork`,它不出现在稀疏索引中。由于排序,你知道 `handiwork` 必须出现在 `handbag` 和 `handsome` 之间。这意味着你可以寻址到 `handbag` 的偏移量,并从那里扫描文件,直到找到 `handiwork`(或没有,如果键不在文件中)。几千字节的块可以非常快速地扫描。
|
||||
例如,在 [图 4-2](/ch4#fig_storage_sstable_index) 中,一个块的第一个键是 `handbag`,下一个块的第一个键是 `handsome`。现在假设你要查找键 `handiwork`,它不出现在稀疏索引中。由于排序,你知道 `handiwork` 必须出现在 `handbag` 和 `handsome` 之间。这意味着你可以寻址到 `handbag` 的偏移量,并从那里扫描文件,直到找到 `handiwork`(或没有,如果键不在文件中)。几千字节的块可以非常快速地扫描。
|
||||
|
||||
此外,每个记录块可以被压缩(如 [图 4-2](/en/ch4#fig_storage_sstable_index) 中的阴影区域所示)。除了节省磁盘空间外,压缩还减少了 I/O 带宽的使用,代价是使用更多的 CPU 时间。
|
||||
此外,每个记录块可以被压缩(如 [图 4-2](/ch4#fig_storage_sstable_index) 中的阴影区域所示)。除了节省磁盘空间外,压缩还减少了 I/O 带宽的使用,代价是使用更多的 CPU 时间。
|
||||
|
||||
#### 构建和合并 SSTable {#constructing-and-merging-sstables}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ SSTable 文件格式比仅追加日志更适合读取,但它使写入更加困
|
|||
3. 为了读取某个键的值,首先尝试在内存表和最新的磁盘段中查找键。如果不在那里,查看下一个较旧的段,等等,直到找到键或到达最旧的段。如果键不出现在任何段中,它就不存在于数据库中。
|
||||
4. 不时地,在后台运行合并和压缩过程来合并段文件并丢弃被覆盖或删除的值。
|
||||
|
||||
合并段的工作类似于 *归并排序* 算法 [^5]。该过程如 [图 4-3](/en/ch4#fig_storage_sstable_merging) 所示:并排开始读取输入文件,查看每个文件中的第一个键,将最低键(根据排序顺序)复制到输出文件,然后重复。如果同一个键出现在多个输入文件中,只保留较新的值。这产生了一个新的合并段文件,也按键排序,每个键一个值,并且使用最少的内存,因为我们可以一次迭代一个键的 SSTable。
|
||||
合并段的工作类似于 *归并排序* 算法 [^5]。该过程如 [图 4-3](/ch4#fig_storage_sstable_merging) 所示:并排开始读取输入文件,查看每个文件中的第一个键,将最低键(根据排序顺序)复制到输出文件,然后重复。如果同一个键出现在多个输入文件中,只保留较新的值。这产生了一个新的合并段文件,也按键排序,每个键一个值,并且使用最少的内存,因为我们可以一次迭代一个键的 SSTable。
|
||||
|
||||
{{< figure src="/fig/ddia_0403.png" id="fig_storage_sstable_merging" caption="图 4-3. 合并几个 SSTable 段,只保留每个键的最新值。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -137,17 +137,17 @@ SSTable 文件格式比仅追加日志更适合读取,但它使写入更加困
|
|||
|
||||
段文件不一定必须存储在本地磁盘上:它们也非常适合写入对象存储。例如,SlateDB 和 Delta Lake [^12] 采用了这种方法。
|
||||
|
||||
拥有不可变段文件也简化了崩溃恢复:如果在写出内存表或合并段时发生崩溃,数据库可以删除未完成的 SSTable 并重新开始。持久化写入到内存表的日志如果在写入记录的中途发生崩溃,或者磁盘已满,可能包含不完整的记录;这些通常通过在日志中包含校验和来检测,并丢弃损坏或不完整的日志条目。我们将在 [第 8 章](/en/ch8#ch_transactions) 中更多地讨论持久性和崩溃恢复。
|
||||
拥有不可变段文件也简化了崩溃恢复:如果在写出内存表或合并段时发生崩溃,数据库可以删除未完成的 SSTable 并重新开始。持久化写入到内存表的日志如果在写入记录的中途发生崩溃,或者磁盘已满,可能包含不完整的记录;这些通常通过在日志中包含校验和来检测,并丢弃损坏或不完整的日志条目。我们将在 [第 8 章](/ch8#ch_transactions) 中更多地讨论持久性和崩溃恢复。
|
||||
|
||||
#### 布隆过滤器 {#bloom-filters}
|
||||
|
||||
使用 LSM 存储,读取很久以前最后更新或不存在的键可能会很慢,因为存储引擎需要检查几个段文件。为了加速此类读取,LSM 存储引擎通常在每个段中包含一个 *布隆过滤器* [^13],它提供了一种快速但近似的方法来检查特定键是否出现在特定 SSTable 中。
|
||||
|
||||
[图 4-4](/en/ch4#fig_storage_bloom) 显示了一个包含两个键和 16 位的布隆过滤器示例(实际上,它会包含更多键和更多位)。对于 SSTable 中的每个键,我们计算一个哈希函数,生成一组数字,然后将其解释为位数组的索引 [^14]。我们将对应于这些索引的位设置为 1,其余保留为 0。例如,键 `handbag` 哈希到数字(2、9、4),所以我们将第 2、9 和 4 位设置为 1。然后将位图作为 SSTable 的一部分存储,与键的稀疏索引一起。这需要一些额外的空间,但与 SSTable 的其余部分相比,布隆过滤器通常很小。
|
||||
[图 4-4](/ch4#fig_storage_bloom) 显示了一个包含两个键和 16 位的布隆过滤器示例(实际上,它会包含更多键和更多位)。对于 SSTable 中的每个键,我们计算一个哈希函数,生成一组数字,然后将其解释为位数组的索引 [^14]。我们将对应于这些索引的位设置为 1,其余保留为 0。例如,键 `handbag` 哈希到数字(2、9、4),所以我们将第 2、9 和 4 位设置为 1。然后将位图作为 SSTable 的一部分存储,与键的稀疏索引一起。这需要一些额外的空间,但与 SSTable 的其余部分相比,布隆过滤器通常很小。
|
||||
|
||||
{{< figure src="/fig/ddia_0404.png" id="fig_storage_bloom" caption="图 4-4. 布隆过滤器提供了一种快速的概率检查,以确定特定键是否存在于特定 SSTable 中。" class="w-full my-4" >}}
|
||||
|
||||
当我们想知道一个键是否出现在 SSTable 中时,我们像以前一样计算该键的相同哈希,并检查这些索引处的位。例如,在 [图 4-4](/en/ch4#fig_storage_bloom) 中,我们正在查询键 `handheld`,它哈希到(6、11、2)。其中一个位是 1(即第 2 位),而另外两个是 0。这些检查可以使用所有 CPU 支持的位运算极其快速地进行。
|
||||
当我们想知道一个键是否出现在 SSTable 中时,我们像以前一样计算该键的相同哈希,并检查这些索引处的位。例如,在 [图 4-4](/ch4#fig_storage_bloom) 中,我们正在查询键 `handheld`,它哈希到(6、11、2)。其中一个位是 1(即第 2 位),而另外两个是 0。这些检查可以使用所有 CPU 支持的位运算极其快速地进行。
|
||||
|
||||
如果至少有一个位是 0,我们知道键肯定不出现在 SSTable 中。如果查询中的位都是 1,键很可能在 SSTable 中,但也可能巧合地所有这些位都被其他键设置为 1。这种看起来好像键存在,即使它不存在的情况,称为 *误报*。
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ SSTable 文件格式比仅追加日志更适合读取,但它使写入更加困
|
|||
|
||||
作为经验法则,如果你主要有写入和很少读取,大小分层压缩性能更好,而如果你的工作负载以读取为主,分层压缩性能更好。如果你频繁写入少量键,很少写入大量键,那么分层压缩也可能有优势 [^18]。
|
||||
|
||||
尽管有许多细微之处,LSM 树的基本思想——保持在后台合并的 SSTable 级联——简单而有效。我们在 ["比较 B 树和 LSM 树"](/en/ch4#sec_storage_btree_lsm_comparison) 中更详细地讨论它们的性能特征。
|
||||
尽管有许多细微之处,LSM 树的基本思想——保持在后台合并的 SSTable 级联——简单而有效。我们在 ["比较 B 树和 LSM 树"](/ch4#sec_storage_btree_lsm_comparison) 中更详细地讨论它们的性能特征。
|
||||
|
||||
--------
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ SSTable 文件格式比仅追加日志更适合读取,但它使写入更加困
|
|||
>
|
||||
> 嵌入式数据库在移动应用程序中非常常用,用于存储本地用户的数据。在后端,如果数据足够小以适合单台机器,并且没有很多并发事务,它们可能是合适的选择。例如,在多租户系统中,如果每个租户足够小且彼此完全分离(即,你不需要运行组合来自多个租户的数据的查询),你可能可以为每个租户使用单独的嵌入式数据库实例 [^20]。
|
||||
>
|
||||
> 我们在本章中讨论的存储和检索方法既用于嵌入式数据库,也用于客户端-服务器数据库。在 [第 6 章](/en/ch6#ch_replication) 和 [第 7 章](/en/ch7#ch_sharding) 中,我们将讨论跨多台机器扩展数据库的技术。
|
||||
> 我们在本章中讨论的存储和检索方法既用于嵌入式数据库,也用于客户端-服务器数据库。在 [第 6 章](/ch6#ch_replication) 和 [第 7 章](/ch7#ch_sharding) 中,我们将讨论跨多台机器扩展数据库的技术。
|
||||
|
||||
--------
|
||||
|
||||
|
|
@ -194,21 +194,21 @@ B 树于 1970 年引入 [^21],不到 10 年后被称为"无处不在" [^22],
|
|||
|
||||
我们之前看到的日志结构索引将数据库分解为可变大小的 *段*,通常为几兆字节或更大,一次写入然后不可变。相比之下,B 树将数据库分解为固定大小的 *块* 或 *页*,并可能就地覆盖页。页传统上大小为 4 KiB,但 PostgreSQL 现在默认使用 8 KiB,MySQL 默认使用 16 KiB。
|
||||
|
||||
每个页可以使用页号标识,这允许一个页引用另一个页——类似于指针,但在磁盘上而不是在内存中。如果所有页都存储在同一个文件中,将页号乘以页大小会给我们页所在文件中的字节偏移量。我们可以使用这些页引用来构建页树,如 [图 4-5](/en/ch4#fig_storage_b_tree) 所示。
|
||||
每个页可以使用页号标识,这允许一个页引用另一个页——类似于指针,但在磁盘上而不是在内存中。如果所有页都存储在同一个文件中,将页号乘以页大小会给我们页所在文件中的字节偏移量。我们可以使用这些页引用来构建页树,如 [图 4-5](/ch4#fig_storage_b_tree) 所示。
|
||||
|
||||
{{< figure src="/fig/ddia_0405.png" id="fig_storage_b_tree" caption="图 4-5. 使用 B 树索引查找键 251。从根页开始,我们首先跟随引用到键 200-300 的页,然后是键 250-270 的页。" class="w-full my-4" >}}
|
||||
|
||||
一个页被指定为 B 树的 *根*;每当你想在索引中查找一个键时,你从这里开始。该页包含几个键和对子页的引用。每个子负责一个连续的键范围,引用之间的键指示这些范围之间的边界在哪里。(这种结构有时称为 B+ 树,但我们不需要将其与其他 B 树变体区分开来。)
|
||||
|
||||
在 [图 4-5](/en/ch4#fig_storage_b_tree) 的示例中,我们正在查找键 251,所以我们知道需要跟随 200 和 300 之间的页引用。这将我们带到一个外观相似的页,进一步将 200-300 范围分解为子范围。最终我们到达包含单个键的页(*叶页*),它要么内联包含每个键的值,要么包含对可以找到值的页的引用。
|
||||
在 [图 4-5](/ch4#fig_storage_b_tree) 的示例中,我们正在查找键 251,所以我们知道需要跟随 200 和 300 之间的页引用。这将我们带到一个外观相似的页,进一步将 200-300 范围分解为子范围。最终我们到达包含单个键的页(*叶页*),它要么内联包含每个键的值,要么包含对可以找到值的页的引用。
|
||||
|
||||
B 树的一页中对子页的引用数称为 *分支因子*。例如,在 [图 4-5](/en/ch4#fig_storage_b_tree) 中,分支因子为六。实际上,分支因子取决于存储页引用和范围边界所需的空间量,但通常是几百。
|
||||
B 树的一页中对子页的引用数称为 *分支因子*。例如,在 [图 4-5](/ch4#fig_storage_b_tree) 中,分支因子为六。实际上,分支因子取决于存储页引用和范围边界所需的空间量,但通常是几百。
|
||||
|
||||
如果你想更新 B 树中现有键的值,你搜索包含该键的叶页,并用包含新值的版本覆盖磁盘上的该页。如果你想添加新键,你需要找到其范围包含新键的页并将其添加到该页。如果页中没有足够的空闲空间来容纳新键,则页分成两个半满页,并更新父页以考虑键范围的新细分。
|
||||
|
||||
{{< figure src="/fig/ddia_0406.png" id="fig_storage_b_tree_split" caption="图 4-6. 通过在边界键 337 上分割页来增长 B 树。父页被更新以引用两个子页。" class="w-full my-4" >}}
|
||||
|
||||
在 [图 4-6](/en/ch4#fig_storage_b_tree_split) 的示例中,我们想插入键 334,但范围 333-345 的页已满。因此,我们将其分成范围 333-337(包括新键)的页和 337-344 的页。我们还必须更新父页以引用两个子页,它们之间的边界值为 337。如果父页没有足够的空间用于新引用,它也可能需要分割,分割可以一直持续到树的根。当根分割时,我们在其上方创建一个新根。删除键(可能需要合并节点)更复杂 [^5]。
|
||||
在 [图 4-6](/ch4#fig_storage_b_tree_split) 的示例中,我们想插入键 334,但范围 333-345 的页已满。因此,我们将其分成范围 333-337(包括新键)的页和 337-344 的页。我们还必须更新父页以引用两个子页,它们之间的边界值为 337。如果父页没有足够的空间用于新引用,它也可能需要分割,分割可以一直持续到树的根。当根分割时,我们在其上方创建一个新根。删除键(可能需要合并节点)更复杂 [^5]。
|
||||
|
||||
该算法确保树保持 *平衡*:具有 *n* 个键的 B 树始终具有 *O*(log *n*) 的深度。大多数数据库可以适合三或四层深的 B 树,因此你不需要跟随很多页引用来找到你要查找的页。(具有 500 的分支因子的 4 KiB 页的四层树可以存储多达 250 TB。)
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ B 树的基本底层写入操作是用新数据覆盖磁盘上的页。假设覆
|
|||
|
||||
由于 B 树已经存在了很长时间,多年来已经开发了许多变体。仅举几例:
|
||||
|
||||
* 一些数据库(如 LMDB)使用写时复制方案 [^26],而不是覆盖页并维护 WAL 进行崩溃恢复。修改的页被写入不同的位置,并创建树中父页的新版本,指向新位置。这种方法对于并发控制也很有用,正如我们将在 ["快照隔离和可重复读"](/en/ch8#sec_transactions_snapshot_isolation) 中看到的。
|
||||
* 一些数据库(如 LMDB)使用写时复制方案 [^26],而不是覆盖页并维护 WAL 进行崩溃恢复。修改的页被写入不同的位置,并创建树中父页的新版本,指向新位置。这种方法对于并发控制也很有用,正如我们将在 ["快照隔离和可重复读"](/ch8#sec_transactions_snapshot_isolation) 中看到的。
|
||||
* 我们可以通过不存储整个键,而是缩写它来节省页中的空间。特别是在树内部的页上,键只需要提供足够的信息来充当键范围之间的边界。将更多键打包到页中允许树具有更高的分支因子,从而减少层级。
|
||||
* 为了加速按排序顺序扫描键范围,一些 B 树实现尝试布局树,使叶页在磁盘上按顺序出现,减少磁盘寻道次数。然而,随着树的增长,很难维持该顺序。
|
||||
* 已向树添加了额外的指针。例如,每个叶页可能具有对其左右兄弟页的引用,这允许按顺序扫描键而无需跳回父页。
|
||||
|
|
@ -249,7 +249,7 @@ B 树的基本底层写入操作是用新数据覆盖磁盘上的页。假设覆
|
|||
|
||||
使用 B 树,如果应用程序写入分散在整个键空间中的键,则产生的磁盘操作也是随机分散的,因为存储引擎需要覆盖的页可能位于磁盘上的任何位置。另一方面,日志结构存储引擎一次写入整个段文件(要么写出内存表,要么压缩现有段),这比 B 树中的页大得多。
|
||||
|
||||
许多小的、分散的写入模式(如在 B 树中发现的)称为 *随机写入*,而较少的大写入模式(如在 LSM 树中发现的)称为 *顺序写入*。磁盘通常具有比随机写入吞吐量更高的顺序写入吞吐量,这意味着日志结构存储引擎通常可以在相同硬件上处理比 B 树更高的写入吞吐量。这种差异在旋转磁盘硬盘(HDD)上特别大;在今天大多数数据库使用的固态硬盘(SSD)上,差异较小,但仍然很明显(参见 ["SSD 上的顺序与随机写入"](/en/ch4#sidebar_sequential))。
|
||||
许多小的、分散的写入模式(如在 B 树中发现的)称为 *随机写入*,而较少的大写入模式(如在 LSM 树中发现的)称为 *顺序写入*。磁盘通常具有比随机写入吞吐量更高的顺序写入吞吐量,这意味着日志结构存储引擎通常可以在相同硬件上处理比 B 树更高的写入吞吐量。这种差异在旋转磁盘硬盘(HDD)上特别大;在今天大多数数据库使用的固态硬盘(SSD)上,差异较小,但仍然很明显(参见 ["SSD 上的顺序与随机写入"](/ch4#sidebar_sequential))。
|
||||
|
||||
--------
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ B 树索引必须至少写入每条数据两次:一次到预写日志,一次
|
|||
|
||||
B 树会随着时间的推移变得 *碎片化*:例如,如果删除了大量键,数据库文件可能包含许多不再被 B 树使用的页。对 B 树的后续添加可以使用那些空闲页,但它们不能轻易地返回给操作系统,因为它们在文件中间,因此它们仍然占用文件系统上的空间。因此,数据库需要一个后台进程来移动页以更好地放置它们,例如 PostgreSQL 中的 vacuum 进程 [^25]。
|
||||
|
||||
碎片化在 LSM 树中不是问题,因为压缩过程无论如何都会定期重写数据文件,并且 SSTable 没有未使用空间的页。此外,键值对块可以在 SSTable 中更好地压缩,因此通常在磁盘上产生比 B 树更小的文件。被覆盖的键和值继续消耗空间,直到它们被压缩删除,但使用分层压缩时此开销相当低 [^40] [^41]。大小分层压缩(参见 ["压缩策略"](/en/ch4#sec_storage_lsm_compaction))使用更多磁盘空间,特别是在压缩期间暂时使用。
|
||||
碎片化在 LSM 树中不是问题,因为压缩过程无论如何都会定期重写数据文件,并且 SSTable 没有未使用空间的页。此外,键值对块可以在 SSTable 中更好地压缩,因此通常在磁盘上产生比 B 树更小的文件。被覆盖的键和值继续消耗空间,直到它们被压缩删除,但使用分层压缩时此开销相当低 [^40] [^41]。大小分层压缩(参见 ["压缩策略"](/ch4#sec_storage_lsm_compaction))使用更多磁盘空间,特别是在压缩期间暂时使用。
|
||||
|
||||
在磁盘上有某些数据的多个副本也可能是一个问题,当你需要删除某些数据,并确信它真的已被删除(可能是为了遵守数据保护法规)。例如,在大多数 LSM 存储引擎中,已删除的记录可能仍然存在于较高级别,直到代表删除的墓碑通过所有压缩级别传播,这可能需要很长时间。专门的存储引擎设计可以更快地传播删除 [^42]。
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ B 树会随着时间的推移变得 *碎片化*:例如,如果删除了大量
|
|||
|
||||
到目前为止,我们只讨论了键值索引,它们类似于关系模型中的 *主键* 索引。主键唯一标识关系表中的一行、文档数据库中的一个文档或图数据库中的一个顶点。数据库中的其他记录可以通过其主键(或 ID)引用该行/文档/顶点,索引用于解析此类引用。
|
||||
|
||||
拥有 *二级索引* 也很常见。在关系数据库中,你可以使用 `CREATE INDEX` 命令在同一个表上创建几个二级索引,允许你按主键以外的列进行搜索。例如,在 [第 3 章](/en/ch3#ch_datamodels) 的 [图 3-1](/en/ch3#fig_obama_relational) 中,你很可能在 `user_id` 列上有一个二级索引,以便你可以在每个表中找到属于同一用户的所有行。
|
||||
拥有 *二级索引* 也很常见。在关系数据库中,你可以使用 `CREATE INDEX` 命令在同一个表上创建几个二级索引,允许你按主键以外的列进行搜索。例如,在 [第 3 章](/ch3#ch_datamodels) 的 [图 3-1](/ch3#fig_obama_relational) 中,你很可能在 `user_id` 列上有一个二级索引,以便你可以在每个表中找到属于同一用户的所有行。
|
||||
|
||||
二级索引可以很容易地从键值索引构建。主要区别在于,在二级索引中,索引值不一定是唯一的;也就是说,同一个索引条目下可能有许多行(文档、顶点)。这可以通过两种方式解决:要么使索引中的每个值成为匹配行标识符列表(如全文索引中的发布列表),要么通过向其附加行标识符使每个条目唯一。具有就地更新的存储引擎(如 B 树)和日志结构存储都可以用于实现索引。
|
||||
|
||||
|
|
@ -306,7 +306,7 @@ B 树会随着时间的推移变得 *碎片化*:例如,如果删除了大量
|
|||
* 或者,值可以是对实际数据的引用:要么是相关行的主键(InnoDB 对二级索引执行此操作),要么是对磁盘上位置的直接引用。在后一种情况下,存储行的地方称为 *堆文件*,它以无特定顺序存储数据(它可能是仅追加的,或者它可能跟踪已删除的行以便稍后用新数据覆盖它们)。例如,Postgres 使用堆文件方法 [^44]。
|
||||
* 两者之间的中间地带是 *覆盖索引* 或 *包含列的索引*,除了在堆或主键聚簇索引上存储完整行外,还在索引中存储表的 *某些* 列 [^45]。这允许仅使用索引回答某些查询,而无需解析主键或查看堆文件(在这种情况下,索引称为 *覆盖* 查询)。这可以使某些查询更快,但数据的重复意味着索引使用更多磁盘空间并减慢写入速度。
|
||||
|
||||
到目前为止讨论的索引只将单个键映射到值。如果你需要同时查询表的多个列(或文档中的多个字段),请参阅 ["多维和全文索引"](/en/ch4#sec_storage_multidimensional)。
|
||||
到目前为止讨论的索引只将单个键映射到值。如果你需要同时查询表的多个列(或文档中的多个字段),请参阅 ["多维和全文索引"](/ch4#sec_storage_multidimensional)。
|
||||
|
||||
在不更改键的情况下更新值时,堆文件方法可以允许记录就地覆盖,前提是新值不大于旧值。如果新值更大,情况会更复杂,因为它可能需要移动到堆中有足够空间的新位置。在这种情况下,要么所有索引都需要更新以指向记录的新堆位置,要么在旧堆位置留下转发指针 [^2]。
|
||||
|
||||
|
|
@ -335,13 +335,13 @@ Redis 和 Couchbase 通过异步写入磁盘提供弱持久性。
|
|||
|
||||
表面上,数据仓库和关系型 OLTP 数据库看起来很相似,因为它们都有 SQL 查询接口。然而,系统的内部可能看起来完全不同,因为它们针对非常不同的查询模式进行了优化。许多数据库供应商现在专注于支持事务处理或分析工作负载,但不是两者都支持。
|
||||
|
||||
一些数据库,如 Microsoft SQL Server、SAP HANA 和 SingleStore,在同一产品中支持事务处理和数据仓库。然而,这些混合事务和分析处理(HTAP)数据库(在 ["数据仓库"](/en/ch1#sec_introduction_dwh) 中介绍)越来越多地变成两个独立的存储和查询引擎,它们恰好可以通过通用的 SQL 接口访问 [^50] [^51] [^52] [^53]。
|
||||
一些数据库,如 Microsoft SQL Server、SAP HANA 和 SingleStore,在同一产品中支持事务处理和数据仓库。然而,这些混合事务和分析处理(HTAP)数据库(在 ["数据仓库"](/ch1#sec_introduction_dwh) 中介绍)越来越多地变成两个独立的存储和查询引擎,它们恰好可以通过通用的 SQL 接口访问 [^50] [^51] [^52] [^53]。
|
||||
|
||||
### 云数据仓库 {#sec_cloud_data_warehouses}
|
||||
|
||||
Teradata、Vertica 和 SAP HANA 等数据仓库供应商既销售商业许可的本地仓库,也销售基于云的解决方案。但随着他们的许多客户迁移到云,新的云数据仓库如 Google Cloud BigQuery、Amazon Redshift 和 Snowflake 也已被广泛采用。与传统数据仓库不同,云数据仓库利用可扩展的云基础设施,如对象存储和无服务器计算平台。
|
||||
|
||||
云数据仓库往往与其他云服务集成得更好,并且更具弹性。例如,许多云仓库支持自动日志摄取,并提供与数据处理框架(如 Google Cloud 的 Dataflow 或 Amazon Web Services 的 Kinesis)的轻松集成。这些仓库也更具弹性,因为它们将查询计算与存储层解耦 [^54]。数据持久化在对象存储而不是本地磁盘上,这使得独立调整存储容量和查询计算资源变得容易,正如我们之前在 ["云原生系统架构"](/en/ch1#sec_introduction_cloud_native) 中看到的。
|
||||
云数据仓库往往与其他云服务集成得更好,并且更具弹性。例如,许多云仓库支持自动日志摄取,并提供与数据处理框架(如 Google Cloud 的 Dataflow 或 Amazon Web Services 的 Kinesis)的轻松集成。这些仓库也更具弹性,因为它们将查询计算与存储层解耦 [^54]。数据持久化在对象存储而不是本地磁盘上,这使得独立调整存储容量和查询计算资源变得容易,正如我们之前在 ["云原生系统架构"](/ch1#sec_introduction_cloud_native) 中看到的。
|
||||
|
||||
开源数据仓库如 Apache Hive、Trino 和 Apache Spark 也随着云而发展。随着分析数据存储转移到对象存储上的数据湖,开源仓库已经开始分解 [^55]。以下组件以前集成在单个系统(如 Apache Hive)中,现在通常作为单独的组件实现:
|
||||
|
||||
|
|
@ -355,13 +355,13 @@ Teradata、Vertica 和 SAP HANA 等数据仓库供应商既销售商业许可的
|
|||
: 以 Apache Parquet 和类似存储格式编写的文件一旦写入通常是不可变的。为了支持行插入和删除,使用表格式,如 Apache Iceberg 或 Databricks 的 Delta 格式。表格式指定一种文件格式,该格式定义哪些文件构成表以及表的模式。此类格式还提供高级功能,如时间旅行(查询表在先前时间点的状态的能力)、垃圾收集,甚至事务。
|
||||
|
||||
数据目录
|
||||
: 就像表格式定义哪些文件组成表一样,数据目录定义哪些表组成数据库。目录用于创建、重命名和删除表。与存储和表格式不同,数据目录如 Snowflake 的 Polaris 和 Databricks 的 Unity Catalog 通常作为独立服务运行,可以使用 REST 接口查询。Apache Iceberg 也提供目录,可以在客户端内运行或作为单独的进程运行。查询引擎在读取和写入表时使用目录信息。传统上,目录和查询引擎是集成的,但将它们解耦使数据发现和数据治理系统(在 ["数据系统、法律和社会"](/en/ch1#sec_introduction_compliance) 中讨论)也能访问目录的元数据。
|
||||
: 就像表格式定义哪些文件组成表一样,数据目录定义哪些表组成数据库。目录用于创建、重命名和删除表。与存储和表格式不同,数据目录如 Snowflake 的 Polaris 和 Databricks 的 Unity Catalog 通常作为独立服务运行,可以使用 REST 接口查询。Apache Iceberg 也提供目录,可以在客户端内运行或作为单独的进程运行。查询引擎在读取和写入表时使用目录信息。传统上,目录和查询引擎是集成的,但将它们解耦使数据发现和数据治理系统(在 ["数据系统、法律和社会"](/ch1#sec_introduction_compliance) 中讨论)也能访问目录的元数据。
|
||||
|
||||
### 列式存储 {#sec_storage_column}
|
||||
|
||||
如 ["星型和雪花型:分析模式"](/en/ch3#sec_datamodels_analytics) 中所讨论的,数据仓库按照惯例通常使用关系模式,其中有一个包含对维度表的外键引用的大型事实表。如果你的事实表中有数万亿行和 PB 级数据,有效地存储和查询它们就成为一个具有挑战性的问题。维度表通常要小得多(数百万行),因此在本节中我们将重点关注事实的存储。
|
||||
如 ["星型和雪花型:分析模式"](/ch3#sec_datamodels_analytics) 中所讨论的,数据仓库按照惯例通常使用关系模式,其中有一个包含对维度表的外键引用的大型事实表。如果你的事实表中有数万亿行和 PB 级数据,有效地存储和查询它们就成为一个具有挑战性的问题。维度表通常要小得多(数百万行),因此在本节中我们将重点关注事实的存储。
|
||||
|
||||
尽管事实表通常超过 100 列宽,但典型的数据仓库查询一次只访问其中的 4 或 5 列(分析很少需要 `"SELECT *"` 查询)[^52]。以 [示例 4-1](/en/ch4#fig_storage_analytics_query) 中的查询为例:它访问大量行(2024 日历年中每次有人购买水果或糖果的情况),但它只需要访问 `fact_sales` 表的三列:`date_key`、`product_sk` 和 `quantity`。查询忽略所有其他列。
|
||||
尽管事实表通常超过 100 列宽,但典型的数据仓库查询一次只访问其中的 4 或 5 列(分析很少需要 `"SELECT *"` 查询)[^52]。以 [示例 4-1](/ch4#fig_storage_analytics_query) 中的查询为例:它访问大量行(2024 日历年中每次有人购买水果或糖果的情况),但它只需要访问 `fact_sales` 表的三列:`date_key`、`product_sk` 和 `quantity`。查询忽略所有其他列。
|
||||
|
||||
{{< figure id="fig_storage_analytics_query" title="示例 4-1. 分析人们是否更倾向于购买新鲜水果或糖果,取决于星期几" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -381,11 +381,11 @@ GROUP BY
|
|||
|
||||
我们如何有效地执行这个查询?
|
||||
|
||||
在大多数 OLTP 数据库中,存储以 *行式* 方式布局:表的一行中的所有值彼此相邻存储。文档数据库类似:整个文档通常作为一个连续的字节序列存储。你可以在 [图 4-1](/en/ch4#fig_storage_csv_hash_index) 的 CSV 示例中看到这一点。
|
||||
在大多数 OLTP 数据库中,存储以 *行式* 方式布局:表的一行中的所有值彼此相邻存储。文档数据库类似:整个文档通常作为一个连续的字节序列存储。你可以在 [图 4-1](/ch4#fig_storage_csv_hash_index) 的 CSV 示例中看到这一点。
|
||||
|
||||
为了处理像 [示例 4-1](/en/ch4#fig_storage_analytics_query) 这样的查询,你可能在 `fact_sales.date_key` 和/或 `fact_sales.product_sk` 上有索引,它们告诉存储引擎在哪里找到特定日期或特定产品的所有销售。但是,行式存储引擎仍然需要将所有这些行(每行由 100 多个属性组成)从磁盘加载到内存中,解析它们,并过滤掉不符合所需条件的行。这可能需要很长时间。
|
||||
为了处理像 [示例 4-1](/ch4#fig_storage_analytics_query) 这样的查询,你可能在 `fact_sales.date_key` 和/或 `fact_sales.product_sk` 上有索引,它们告诉存储引擎在哪里找到特定日期或特定产品的所有销售。但是,行式存储引擎仍然需要将所有这些行(每行由 100 多个属性组成)从磁盘加载到内存中,解析它们,并过滤掉不符合所需条件的行。这可能需要很长时间。
|
||||
|
||||
*列式*(或 *列式*)存储背后的想法很简单:不要将一行中的所有值存储在一起,而是将每个 *列* 中的所有值存储在一起 [^56]。如果每列单独存储,查询只需要读取和解析该查询中使用的那些列,这可以节省大量工作。[图 4-7](/en/ch4#fig_column_store) 使用 [图 3-5](/en/ch3#fig_dwh_schema) 中事实表的扩展版本显示了这一原理。
|
||||
*列式*(或 *列式*)存储背后的想法很简单:不要将一行中的所有值存储在一起,而是将每个 *列* 中的所有值存储在一起 [^56]。如果每列单独存储,查询只需要读取和解析该查询中使用的那些列,这可以节省大量工作。[图 4-7](/ch4#fig_column_store) 使用 [图 3-5](/ch3#fig_dwh_schema) 中事实表的扩展版本显示了这一原理。
|
||||
|
||||
--------
|
||||
|
||||
|
|
@ -406,13 +406,13 @@ GROUP BY
|
|||
|
||||
除了只从磁盘加载查询所需的那些列外,我们还可以通过压缩数据进一步减少对磁盘吞吐量和网络带宽的需求。幸运的是,列式存储通常非常适合压缩。
|
||||
|
||||
看看 [图 4-7](/en/ch4#fig_column_store) 中每列的值序列:它们通常看起来相当重复,这是压缩的好兆头。根据列中的数据,可以使用不同的压缩技术。在数据仓库中特别有效的一种技术是 *位图编码*,如 [图 4-8](/en/ch4#fig_bitmap_index) 所示。
|
||||
看看 [图 4-7](/ch4#fig_column_store) 中每列的值序列:它们通常看起来相当重复,这是压缩的好兆头。根据列中的数据,可以使用不同的压缩技术。在数据仓库中特别有效的一种技术是 *位图编码*,如 [图 4-8](/ch4#fig_bitmap_index) 所示。
|
||||
|
||||
{{< figure src="/fig/ddia_0408.png" id="fig_bitmap_index" caption="图 4-8. 单列的压缩、位图索引存储。" class="w-full my-4" >}}
|
||||
|
||||
通常,列中不同值的数量与行数相比很小(例如,零售商可能有数十亿笔销售交易,但只有 100,000 种不同的产品)。我们现在可以将具有 *n* 个不同值的列转换为 *n* 个单独的位图:每个不同值一个位图,每行一位。如果行具有该值,则位为 1,否则为 0。
|
||||
|
||||
一种选择是使用每行一位存储这些位图。然而,这些位图通常包含大量零(我们说它们是 *稀疏* 的)。在这种情况下,位图可以额外进行游程编码:计算连续零或一的数量并存储该数字,如 [图 4-8](/en/ch4#fig_bitmap_index) 底部所示。*咆哮位图* 等技术在两种位图表示之间切换,使用更紧凑的表示 [^73]。这可以使列的编码非常高效。
|
||||
一种选择是使用每行一位存储这些位图。然而,这些位图通常包含大量零(我们说它们是 *稀疏* 的)。在这种情况下,位图可以额外进行游程编码:计算连续零或一的数量并存储该数字,如 [图 4-8](/ch4#fig_bitmap_index) 底部所示。*咆哮位图* 等技术在两种位图表示之间切换,使用更紧凑的表示 [^73]。这可以使列的编码非常高效。
|
||||
|
||||
像这样的位图索引非常适合数据仓库中常见的查询类型。例如:
|
||||
|
||||
|
|
@ -439,15 +439,15 @@ GROUP BY
|
|||
|
||||
相反,数据需要一次排序整行,即使它是按列存储的。数据库管理员可以使用他们对常见查询的了解来选择表应该按哪些列排序。例如,如果查询经常针对日期范围(如上个月),那么将 `date_key` 作为第一个排序键可能是有意义的。然后查询只能扫描上个月的行,这将比扫描所有行快得多。
|
||||
|
||||
第二列可以确定在第一列中具有相同值的任何行的排序顺序。例如,如果 `date_key` 是 [图 4-7](/en/ch4#fig_column_store) 中的第一个排序键,那么 `product_sk` 作为第二个排序键可能是有意义的,这样同一天同一产品的所有销售在存储中组合在一起。这将有助于需要在特定日期范围内按产品分组或过滤销售的查询。
|
||||
第二列可以确定在第一列中具有相同值的任何行的排序顺序。例如,如果 `date_key` 是 [图 4-7](/ch4#fig_column_store) 中的第一个排序键,那么 `product_sk` 作为第二个排序键可能是有意义的,这样同一天同一产品的所有销售在存储中组合在一起。这将有助于需要在特定日期范围内按产品分组或过滤销售的查询。
|
||||
|
||||
排序顺序的另一个优点是它可以帮助列的压缩。如果主排序列没有很多不同的值,那么排序后,它将有很长的序列,其中相同的值连续重复多次。简单的游程编码(就像我们在 [图 4-8](/en/ch4#fig_bitmap_index) 中用于位图的那样)可以将该列压缩到几千字节——即使表有数十亿行。
|
||||
排序顺序的另一个优点是它可以帮助列的压缩。如果主排序列没有很多不同的值,那么排序后,它将有很长的序列,其中相同的值连续重复多次。简单的游程编码(就像我们在 [图 4-8](/ch4#fig_bitmap_index) 中用于位图的那样)可以将该列压缩到几千字节——即使表有数十亿行。
|
||||
|
||||
该压缩效果在第一个排序键上最强。第二个和第三个排序键将更加混乱,因此不会有如此长的重复值运行。排序优先级更低的列基本上以随机顺序出现,因此它们可能不会压缩得那么好。但是,对前几列进行排序总体上仍然是一种胜利。
|
||||
|
||||
#### 写入列式存储 {#writing-to-column-oriented-storage}
|
||||
|
||||
我们在 ["表征事务处理和分析"](/en/ch1#sec_introduction_oltp) 中看到,数据仓库中的读取往往包括对大量行的聚合;列式存储、压缩和排序都有助于使这些读取查询更快。数据仓库中的写入往往是数据的批量导入,通常通过 ETL 过程。
|
||||
我们在 ["表征事务处理和分析"](/ch1#sec_introduction_oltp) 中看到,数据仓库中的读取往往包括对大量行的聚合;列式存储、压缩和排序都有助于使这些读取查询更快。数据仓库中的写入往往是数据的批量导入,通常通过 ETL 过程。
|
||||
|
||||
使用列式存储,在排序表中间某处写入单个行将非常低效,因为你必须从插入位置开始重写所有压缩列。然而,一次批量写入许多行可以分摊重写这些列的成本,使其高效。
|
||||
|
||||
|
|
@ -470,7 +470,7 @@ GROUP BY
|
|||
向量化处理
|
||||
: 查询被解释,而不是编译,但通过批量处理列中的许多值而不是逐行迭代来加快速度。一组固定的预定义算子内置于数据库中;我们可以向它们传递参数并获得一批结果 [^50] [^75]。
|
||||
|
||||
例如,我们可以将 `product_sk` 列和"香蕉"的 ID 传递给相等算子,并获得一个位图(输入列中每个值一位,如果是香蕉则为 1);然后我们可以将 `store_sk` 列和感兴趣商店的 ID 传递给同一个相等算子,并获得另一个位图;然后我们可以将两个位图传递给"按位 AND"算子,如 [图 4-9](/en/ch4#fig_bitmap_and) 所示。结果将是一个位图,其中包含特定商店中所有香蕉销售的 1。
|
||||
例如,我们可以将 `product_sk` 列和"香蕉"的 ID 传递给相等算子,并获得一个位图(输入列中每个值一位,如果是香蕉则为 1);然后我们可以将 `store_sk` 列和感兴趣商店的 ID 传递给同一个相等算子,并获得另一个位图;然后我们可以将两个位图传递给"按位 AND"算子,如 [图 4-9](/ch4#fig_bitmap_and) 所示。结果将是一个位图,其中包含特定商店中所有香蕉销售的 1。
|
||||
|
||||
{{< figure src="/fig/ddia_0409.png" id="fig_bitmap_and" caption="图 4-9. 两个位图之间的按位 AND 适合向量化。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -483,17 +483,17 @@ GROUP BY
|
|||
|
||||
### 物化视图与多维数据集 {#sec_storage_materialized_views}
|
||||
|
||||
我们之前在 ["物化和更新时间线"](/en/ch2#sec_introduction_materializing) 中遇到了 *物化视图*:在关系数据模型中,它们是类似表的对象,其内容是某些查询的结果。区别在于物化视图是查询结果的实际副本,写入磁盘,而虚拟视图只是编写查询的快捷方式。当你从虚拟视图读取时,SQL 引擎会即时将其扩展为视图的底层查询,然后处理扩展的查询。
|
||||
我们之前在 ["物化和更新时间线"](/ch2#sec_introduction_materializing) 中遇到了 *物化视图*:在关系数据模型中,它们是类似表的对象,其内容是某些查询的结果。区别在于物化视图是查询结果的实际副本,写入磁盘,而虚拟视图只是编写查询的快捷方式。当你从虚拟视图读取时,SQL 引擎会即时将其扩展为视图的底层查询,然后处理扩展的查询。
|
||||
|
||||
当底层数据发生变化时,物化视图需要相应地更新。一些数据库可以自动执行此操作,还有像 Materialize 这样专门从事物化视图维护的系统 [^81]。执行此类更新意味着写入时需要更多工作,但物化视图可以提高重复需要执行相同查询的工作负载中的读取性能。
|
||||
|
||||
*物化聚合* 是一种可以在数据仓库中有用的物化视图类型。如前所述,数据仓库查询通常涉及聚合函数,如 SQL 中的 `COUNT`、`SUM`、`AVG`、`MIN` 或 `MAX`。如果许多不同的查询使用相同的聚合,每次都处理原始数据可能是浪费的。为什么不缓存查询最常使用的一些计数或总和呢?*数据立方体* 或 *OLAP 立方体* 通过创建按不同维度分组的聚合网格来做到这一点 [^82]。[图 4-10](/en/ch4#fig_data_cube) 显示了一个示例。
|
||||
*物化聚合* 是一种可以在数据仓库中有用的物化视图类型。如前所述,数据仓库查询通常涉及聚合函数,如 SQL 中的 `COUNT`、`SUM`、`AVG`、`MIN` 或 `MAX`。如果许多不同的查询使用相同的聚合,每次都处理原始数据可能是浪费的。为什么不缓存查询最常使用的一些计数或总和呢?*数据立方体* 或 *OLAP 立方体* 通过创建按不同维度分组的聚合网格来做到这一点 [^82]。[图 4-10](/ch4#fig_data_cube) 显示了一个示例。
|
||||
|
||||
{{< figure src="/fig/ddia_0410.png" id="fig_data_cube" caption="图 4-10. 数据立方体的两个维度,通过求和聚合数据。" class="w-full my-4" >}}
|
||||
|
||||
现在想象每个事实只有两个维度表的外键——在 [图 4-10](/en/ch4#fig_data_cube) 中,这些是 `date_key` 和 `product_sk`。你现在可以绘制一个二维表,日期沿一个轴,产品沿另一个轴。每个单元格包含具有该日期-产品组合的所有事实的属性(例如,`net_price`)的聚合(例如,`SUM`)。然后,你可以沿着每行或列应用相同的聚合,并获得已减少一个维度的摘要(无论日期如何的产品销售,或无论产品如何的日期销售)。
|
||||
现在想象每个事实只有两个维度表的外键——在 [图 4-10](/ch4#fig_data_cube) 中,这些是 `date_key` 和 `product_sk`。你现在可以绘制一个二维表,日期沿一个轴,产品沿另一个轴。每个单元格包含具有该日期-产品组合的所有事实的属性(例如,`net_price`)的聚合(例如,`SUM`)。然后,你可以沿着每行或列应用相同的聚合,并获得已减少一个维度的摘要(无论日期如何的产品销售,或无论产品如何的日期销售)。
|
||||
|
||||
一般来说,事实通常有两个以上的维度。在 [图 3-5](/en/ch3#fig_dwh_schema) 中有五个维度:日期、产品、商店、促销和客户。很难想象五维超立方体会是什么样子,但原理保持不变:每个单元格包含特定日期-产品-商店-促销-客户组合的销售。然后可以沿着每个维度重复汇总这些值。
|
||||
一般来说,事实通常有两个以上的维度。在 [图 3-5](/ch3#fig_dwh_schema) 中有五个维度:日期、产品、商店、促销和客户。很难想象五维超立方体会是什么样子,但原理保持不变:每个单元格包含特定日期-产品-商店-促销-客户组合的销售。然后可以沿着每个维度重复汇总这些值。
|
||||
|
||||
物化数据立方体的优点是某些查询变得非常快,因为它们已经有效地被预先计算。例如,如果你想知道昨天每家商店的总销售额,你只需要查看适当维度的总计——无需扫描数百万行。
|
||||
|
||||
|
|
@ -525,9 +525,9 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
|||
|
||||
然而,在其核心,你可以将全文搜索视为另一种多维查询:在这种情况下,可能出现在文本中的每个单词(*词项*)是一个维度。包含词项 *x* 的文档在维度 *x* 中的值为 1,不包含 *x* 的文档的值为 0。搜索提到"红苹果"的文档意味着查询在 *红* 维度中查找 1,同时在 *苹果* 维度中查找 1。因此,维度数可能非常大。
|
||||
|
||||
许多搜索引擎用来回答此类查询的数据结构称为 *倒排索引*。这是一个键值结构,其中键是词项,值是包含该词项的所有文档的 ID 列表(*发布列表*)。如果文档 ID 是连续数字,发布列表也可以表示为稀疏位图,如 [图 4-8](/en/ch4#fig_bitmap_index) 所示:词项 *x* 的位图中的第 *n* 位是 1,如果 ID 为 *n* 的文档包含词项 *x* [^89]。
|
||||
许多搜索引擎用来回答此类查询的数据结构称为 *倒排索引*。这是一个键值结构,其中键是词项,值是包含该词项的所有文档的 ID 列表(*发布列表*)。如果文档 ID 是连续数字,发布列表也可以表示为稀疏位图,如 [图 4-8](/ch4#fig_bitmap_index) 所示:词项 *x* 的位图中的第 *n* 位是 1,如果 ID 为 *n* 的文档包含词项 *x* [^89]。
|
||||
|
||||
现在,查找包含词项 *x* 和 *y* 的所有文档类似于搜索匹配两个条件的行的向量化数据仓库查询([图 4-9](/en/ch4#fig_bitmap_and)):加载词项 *x* 和 *y* 的两个位图并计算它们的按位 AND。即使位图是游程编码的,这也可以非常高效地完成。
|
||||
现在,查找包含词项 *x* 和 *y* 的所有文档类似于搜索匹配两个条件的行的向量化数据仓库查询([图 4-9](/ch4#fig_bitmap_and)):加载词项 *x* 和 *y* 的两个位图并计算它们的按位 AND。即使位图是游程编码的,这也可以非常高效地完成。
|
||||
|
||||
例如,Elasticsearch 和 Solr 使用的全文索引引擎 Lucene 就是这样工作的 [^90]。它将词项到发布列表的映射存储在类似 SSTable 的排序文件中,这些文件使用我们在本章前面看到的相同日志结构方法在后台合并 [^91]。PostgreSQL 的 GIN 索引类型也使用发布列表来支持全文搜索和 JSON 文档内的索引 [^92] [^93]。
|
||||
|
||||
|
|
@ -545,7 +545,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
|||
--------
|
||||
|
||||
> [!NOTE]
|
||||
> 我们在 ["查询执行:编译和向量化"](/en/ch4#sec_storage_vectorized) 中看到了术语 *向量化处理*。语义搜索中的向量具有不同的含义。在向量化处理中,向量指的是可以使用专门优化的代码处理的一批位。在嵌入模型中,向量是表示多维空间中位置的浮点数列表。
|
||||
> 我们在 ["查询执行:编译和向量化"](/ch4#sec_storage_vectorized) 中看到了术语 *向量化处理*。语义搜索中的向量具有不同的含义。在向量化处理中,向量指的是可以使用专门优化的代码处理的一批位。在嵌入模型中,向量是表示多维空间中位置的浮点数列表。
|
||||
|
||||
--------
|
||||
|
||||
|
|
@ -566,7 +566,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
|||
: 向量空间被聚类成向量的分区(称为 *质心*),以减少必须比较的向量数量。IVF 索引比平面索引更快,但只能给出近似结果:查询和文档可能落入不同的分区,即使它们彼此接近。IVF 索引上的查询首先定义 *探针*,这只是要检查的分区数。使用更多探针的查询将更准确,但会更慢,因为必须比较更多向量。
|
||||
|
||||
分层可导航小世界(HNSW)
|
||||
: HNSW 索引维护向量空间的多个层,如 [图 4-11](/en/ch4#fig_vector_hnsw) 所示。每一层都表示为一个图,其中节点表示向量,边表示与附近向量的接近度。查询首先在最顶层定位最近的向量,该层具有少量节点。然后查询移动到下面层中的同一节点,并跟随该层中的边,该层连接更密集,寻找更接近查询向量的向量。该过程一直持续到到达最后一层。与 IVF 索引一样,HNSW 索引是近似的。
|
||||
: HNSW 索引维护向量空间的多个层,如 [图 4-11](/ch4#fig_vector_hnsw) 所示。每一层都表示为一个图,其中节点表示向量,边表示与附近向量的接近度。查询首先在最顶层定位最近的向量,该层具有少量节点。然后查询移动到下面层中的同一节点,并跟随该层中的边,该层连接更密集,寻找更接近查询向量的向量。该过程一直持续到到达最后一层。与 IVF 索引一样,HNSW 索引是近似的。
|
||||
|
||||
{{< figure src="/fig/ddia_0411.png" id="fig_vector_hnsw" caption="图 4-11. 在 HNSW 索引中搜索最接近给定查询向量的数据库条目。" class="w-full my-4" >}}
|
||||
|
||||
|
|
@ -578,7 +578,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
|||
|
||||
在本章中,我们试图深入了解数据库如何执行存储和检索。当你将数据存储在数据库中时会发生什么,以及当你稍后再次查询数据时数据库会做什么?
|
||||
|
||||
["分析与操作系统"](/en/ch1#sec_introduction_analytics) 介绍了事务处理(OLTP)和分析(OLAP)之间的区别。在本章中,我们看到为 OLTP 优化的存储引擎与为分析优化的存储引擎看起来非常不同:
|
||||
["分析与操作系统"](/ch1#sec_introduction_analytics) 介绍了事务处理(OLTP)和分析(OLAP)之间的区别。在本章中,我们看到为 OLTP 优化的存储引擎与为分析优化的存储引擎看起来非常不同:
|
||||
|
||||
* OLTP 系统针对大量请求进行了优化,每个请求读取和写入少量记录,并且需要快速响应。记录通常通过主键或二级索引访问,这些索引通常是从键到记录的有序映射,也支持范围查询。
|
||||
* 数据仓库和类似的分析系统针对扫描大量记录的复杂读取查询进行了优化。它们通常使用列式存储布局和压缩,以最大限度地减少此类查询需要从磁盘读取的数据量,并使用查询的即时编译或向量化来最大限度地减少处理数据所花费的 CPU 时间。
|
||||
|
|
|
|||
Loading…
Reference in a new issue