This commit is contained in:
Shang-Hao Yang 2025-08-24 20:47:20 +08:00
parent 685825b316
commit 1c17625f53
12 changed files with 28 additions and 28 deletions

View File

@ -461,7 +461,7 @@ MapReduce 作業的輸出處理遵循同樣的原理。透過將輸入視為不
### Hadoop與分散式資料庫的對比
正如我們所看到的Hadoop 有點像 Unix 的分散式版本,其中 HDFS 是檔案系統,而 MapReduce 是 Unix 程序的怪異實現(總是在 Map 階段和 Reduce 階段執行 `sort` 工具)。我們瞭解如何在這些原語的基礎上實現各種連線和分組操作。
正如我們所看到的Hadoop 有點像 Unix 的分散式版本,其中 HDFS 是檔案系統,而 MapReduce 是 Unix 程序的怪異實現(總是在 Map 階段和 Reduce 階段執行 `sort` 工具)。我們瞭解如何在這些原語的基礎上實現各種連線和分組操作。
當 MapReduce 論文發表時【1】它從某種意義上來說 —— 並不新鮮。我們在前幾節中討論的所有處理和並行連線演算法已經在十多年前所謂的 **大規模並行處理MPPmassively parallel processing** 資料庫中實現了【3,40】。比如 Gamma database machine、Teradata 和 Tandem NonStop SQL 就是這方面的先驅【52】。
@ -477,7 +477,7 @@ MapReduce 作業的輸出處理遵循同樣的原理。透過將輸入視為不
這個想法與資料倉庫類似(請參閱 “[資料倉庫](/tw/ch3#資料倉庫)”將大型組織的各個部分的資料集中在一起是很有價值的因為它可以跨越以前相互分離的資料集進行連線。MPP 資料庫所要求的謹慎模式設計拖慢了集中式資料收集速度;以原始形式收集資料,稍後再操心模式的設計,能使資料收集速度加快(有時被稱為 “**資料湖data lake**” 或 “**企業資料中心enterprise data hub**”【55】
不加區分的資料轉儲轉移解釋資料的負擔:資料集的生產者不再需要強制將其轉化為標準格式,資料的解釋成為消費者的問題(**讀時模式** 方法【56】請參閱 “[文件模型中的模式靈活性](/tw/ch2#文件模型中的模式靈活性)”)。如果生產者和消費者是不同優先順序的不同團隊,這可能是一種優勢。甚至可能不存在一個理想的資料模型,對於不同目的有不同的合適視角。以原始形式簡單地轉儲資料,可以允許多種這樣的轉換。這種方法被稱為 **壽司原則sushi principle**“原始資料更好”【57】。
不加區分的資料轉儲轉移解釋資料的負擔:資料集的生產者不再需要強制將其轉化為標準格式,資料的解釋成為消費者的問題(**讀時模式** 方法【56】請參閱 “[文件模型中的模式靈活性](/tw/ch2#文件模型中的模式靈活性)”)。如果生產者和消費者是不同優先順序的不同團隊,這可能是一種優勢。甚至可能不存在一個理想的資料模型,對於不同目的有不同的合適視角。以原始形式簡單地轉儲資料,可以允許多種這樣的轉換。這種方法被稱為 **壽司原則sushi principle**“原始資料更好”【57】。
因此Hadoop 經常被用於實現 ETL 過程(請參閱 “[資料倉庫](/tw/ch3#資料倉庫)”):事務處理系統中的資料以某種原始形式轉儲到分散式檔案系統中,然後編寫 MapReduce 作業來清理資料,將其轉換為關係形式,並將其匯入 MPP 資料倉庫以進行分析。資料建模仍然在進行,但它在一個單獨的步驟中進行,與資料收集相解耦。這種解耦是可行的,因為分散式檔案系統支援以任何格式編碼的資料。

View File

@ -48,7 +48,7 @@ breadcrumbs: false
例如,你可能會首先將資料寫入 **記錄系統** 資料庫,捕獲對該資料庫所做的變更(請參閱 “[變更資料捕獲](/tw/ch12#變更資料捕獲)”然後將變更以相同的順序應用於搜尋索引。如果變更資料捕獲CDC是更新索引的唯一方式則可以確定該索引完全派生自記錄系統因此與其保持一致除軟體錯誤外。寫入資料庫是向該系統提供新輸入的唯一方式。
允許應用程式直接寫入搜尋索引和資料庫引入如 [圖 11-4](/fig/ddia_1204.png) 所示的問題,其中兩個客戶端同時傳送衝突的寫入,且兩個儲存系統按不同順序處理它們。在這種情況下,既不是資料庫說了算,也不是搜尋索引說了算,所以它們做出了相反的決定,進入彼此間永續性的不一致狀態。
允許應用程式直接寫入搜尋索引和資料庫引入如 [圖 11-4](/fig/ddia_1204.png) 所示的問題,其中兩個客戶端同時傳送衝突的寫入,且兩個儲存系統按不同順序處理它們。在這種情況下,既不是資料庫說了算,也不是搜尋索引說了算,所以它們做出了相反的決定,進入彼此間永續性的不一致狀態。
如果你可以透過單個系統來提供所有使用者輸入,從而決定所有寫入的排序,則透過按相同順序處理寫入,可以更容易地派生出其他資料表示。這是狀態機複製方法的一個應用,我們在 “[全序廣播](/tw/ch9#全序廣播)” 中看到。無論你使用變更資料捕獲還是事件溯源日誌,都不如簡單的基於全序的決策原則更重要。
@ -181,7 +181,7 @@ Unix 和關係資料庫以非常不同的哲學來處理資訊管理問題。Uni
* 複製日誌,保持其他節點上資料的副本最新(請參閱 “[複製日誌的實現](/tw/ch5#複製日誌的實現)”)
* 全文檢索索引,允許在文字中進行關鍵字搜尋(請參閱 “[全文檢索與模糊索引](/tw/ch3#全文檢索與模糊索引)”也內置於某些關係資料庫【1】
在 [第十一章](/tw/ch11) 和 [第十二章](/tw/ch12) 中,出現了類似的主題。我們討論了如何構建全文檢索索引(請參閱 “[批處理工作流的輸出](/tw/ch11#批處理工作流的輸出)”),瞭解如何維護物化檢視(請參閱 “[維護物化檢視](/tw/ch12#維護物化檢視)”)以及如何將變更從資料庫複製到派生資料系統(請參閱 “[變更資料捕獲](/tw/ch12#變更資料捕獲)”)。
在 [第十一章](/tw/ch11) 和 [第十二章](/tw/ch12) 中,出現了類似的主題。我們討論了如何構建全文檢索索引(請參閱 “[批處理工作流的輸出](/tw/ch11#批處理工作流的輸出)”),瞭解如何維護物化檢視(請參閱 “[維護物化檢視](/tw/ch12#維護物化檢視)”)以及如何將變更從資料庫複製到派生資料系統(請參閱 “[變更資料捕獲](/tw/ch12#變更資料捕獲)”)。
資料庫中內建的功能與人們用批處理和流處理器構建的派生資料系統似乎有相似之處。

View File

@ -17,7 +17,7 @@ breadcrumbs: false
1. 作為應用程式開發者,你觀察現實世界(其中有人員、組織、貨物、行為、資金流動、感測器等),並用物件或資料結構,以及操作這些資料結構的 API 來建模。這些結構通常是特定於應用程式的。
2. 當你想要儲存這些資料結構時,你用通用的資料模型來表達它們,例如 JSON 或 XML 文件、關係資料庫中的表,或者圖中的頂點和邊。這些資料模型是本章的主題。
3. 構建你的資料庫軟體的工程師決定了如何用記憶體、磁碟或網路上的位元組來表示文件/關係/圖資料。這種表示可能允許以各種方式查詢、搜尋、操作和處理資料。我們將在 [第 4 章](/tw/ch4#ch_storage) 中討論這些儲存引擎的設計。
4. 在更低的層次上,硬體工程師已經想出如何用電流、光脈衝、磁場等來表示位元組的方法。
4. 在更低的層次上,硬體工程師已經想出如何用電流、光脈衝、磁場等來表示位元組的方法。
在複雜的應用程式中可能有更多的中間層,例如基於 API 之上的 API但基本思想仍然相同每一層透過提供一個簡潔的資料模型來隱藏下層的複雜性。這些抽象允許不同的人群 —— 例如,資料庫供應商的工程師和使用他們資料庫的應用程式開發者 —— 有效地合作。
@ -499,7 +499,7 @@ CREATE
當 [圖 3-6](/tw/ch3#fig_datamodels_graph) 的所有頂點和邊都新增到資料庫後,我們可以開始提出有趣的問題:例如,*查詢所有從美國移民到歐洲的人的姓名*。也就是說,找到所有具有指向美國境內位置的 `BORN_IN` 邊,以及指向歐洲境內位置的 `LIVING_IN` 邊的頂點,並返回每個頂點的 `name` 屬性。
[示例 3-5](/tw/ch3#fig_cypher_query) 顯示如何在 Cypher 中表達該查詢。相同的箭頭符號用於 `MATCH` 子句中以在圖中查詢模式:`(person) -[:BORN_IN]-> ()` 匹配由標記為 `BORN_IN` 的邊相關的任意兩個頂點。該邊的尾頂點繫結到變數 `person`,頭頂點未命名。
[示例 3-5](/tw/ch3#fig_cypher_query) 顯示如何在 Cypher 中表達該查詢。相同的箭頭符號用於 `MATCH` 子句中以在圖中查詢模式:`(person) -[:BORN_IN]-> ()` 匹配由標記為 `BORN_IN` 的邊相關的任意兩個頂點。該邊的尾頂點繫結到變數 `person`,頭頂點未命名。
{{< figure id="fig_cypher_query" title="示例 3-5. Cypher 查詢查詢從美國移民到歐洲的人" class="w-full my-4" >}}
@ -741,7 +741,7 @@ Datalog 實際上基於關係資料模型,而不是圖,但它出現在本書
Datalog 資料庫的內容由 *事實* 組成,每個事實對應於關係表中的一行。例如,假設我們有一個包含位置的表 *location*,它有三列:*ID*、*name* 和 *type*。美國是一個國家的事實可以寫成 `location(2, "United States", "country")`,其中 `2` 是美國的 ID。一般來說語句 `table(val1, val2, …​)` 意味著 `table` 包含一行,其中第一列包含 `val1`,第二列包含 `val2`,依此類推。
[示例 3-11](/tw/ch3#fig_datalog_triples) 顯示如何在 Datalog 中編寫 [圖 3-6](/tw/ch3#fig_datamodels_graph) 左側的資料。圖的邊(`within`、`born_in` 和 `lives_in`表示為兩列連線表。例如Lucy 的 ID 是 100愛達荷州的 ID 是 3所以關係"Lucy 出生在愛達荷州"表示為 `born_in(100, 3)`
[示例 3-11](/tw/ch3#fig_datalog_triples) 顯示如何在 Datalog 中編寫 [圖 3-6](/tw/ch3#fig_datamodels_graph) 左側的資料。圖的邊(`within`、`born_in` 和 `lives_in`表示為兩列連線表。例如Lucy 的 ID 是 100愛達荷州的 ID 是 3所以關係"Lucy 出生在愛達荷州"表示為 `born_in(100, 3)`
{{< figure id="fig_datalog_triples" title="示例 3-11. [圖 3-6](/tw/ch3#fig_datamodels_graph) 中資料的子集,表示為 Datalog 事實" class="w-full my-4" >}}
@ -805,7 +805,7 @@ GraphQL 是一種查詢語言,從設計上講,它比我們在本章中看到
GraphQL 的靈活性是有代價的。採用 GraphQL 的組織通常需要工具將 GraphQL 查詢轉換為對內部服務的請求,這些服務通常使用 REST 或 gRPC參見 [第 5 章](/tw/ch5#ch_encoding))。授權、速率限制和效能挑戰是額外的關注點 [^61]。GraphQL 的查詢語言也受到限制,因為 GraphQL 來自不受信任的來源。該語言不允許任何可能執行成本高昂的操作否則使用者可能透過執行大量昂貴的查詢對伺服器執行拒絕服務攻擊。特別是GraphQL 不允許遞迴查詢(與 Cypher、SPARQL、SQL 或 Datalog 不同),並且不允許任意搜尋條件,如"查詢在美國出生並現在居住在歐洲的人"(除非服務所有者特別選擇提供此類搜尋功能)。
儘管如此GraphQL 還是很有用的。[示例 3-13](/tw/ch3#fig_graphql_query) 顯示如何使用 GraphQL 實現 Discord 或 Slack 等群聊應用程式。查詢請求使用者有權訪問的所有頻道,包括頻道名稱和每個頻道中的 50 條最新訊息。對於每條訊息,它請求時間戳、訊息內容以及訊息傳送者的姓名和個人資料圖片 URL。此外如果訊息是對另一條訊息的回覆查詢還會請求傳送者姓名和它所回覆的訊息內容可能以較小的字型呈現在回覆上方以提供一些上下文
儘管如此GraphQL 還是很有用的。[示例 3-13](/tw/ch3#fig_graphql_query) 顯示如何使用 GraphQL 實現 Discord 或 Slack 等群聊應用程式。查詢請求使用者有權訪問的所有頻道,包括頻道名稱和每個頻道中的 50 條最新訊息。對於每條訊息,它請求時間戳、訊息內容以及訊息傳送者的姓名和個人資料圖片 URL。此外如果訊息是對另一條訊息的回覆查詢還會請求傳送者姓名和它所回覆的訊息內容可能以較小的字型呈現在回覆上方以提供一些上下文
{{< figure id="fig_graphql_query" title="示例 3-13. 群聊應用程式的示例 GraphQL 查詢" class="w-full my-4" >}}

View File

@ -131,7 +131,7 @@ JSON 比 XML 更簡潔,但與二進位制格式相比,兩者仍然使用大
}
```
讓我們看一個 MessagePack 的例子,它是 JSON 的二進位制編碼。[圖 5-2](/tw/ch5#fig_encoding_messagepack) 顯示如果你使用 MessagePack 編碼 [示例 5-2](/tw/ch5#fig_encoding_json) 中的 JSON 文件所得到的位元組序列。前幾個位元組如下:
讓我們看一個 MessagePack 的例子,它是 JSON 的二進位制編碼。[圖 5-2](/tw/ch5#fig_encoding_messagepack) 顯示如果你使用 MessagePack 編碼 [示例 5-2](/tw/ch5#fig_encoding_json) 中的 JSON 文件所得到的位元組序列。前幾個位元組如下:
1. 第一個位元組 `0x83` 表示接下來是一個物件(前四位 = `0x80`),有三個欄位(後四位 = `0x03`)。(如果你想知道如果物件有超過 15 個欄位會發生什麼,以至於欄位數無法裝入四位,那麼它會獲得不同的型別指示符,欄位數會以兩個或四個位元組編碼。)
2. 第二個位元組 `0xa8` 表示接下來是一個字串(前四位 = `0xa0`),長度為八個位元組(後四位 = `0x08`)。

View File

@ -128,7 +128,7 @@ ACID 意義上的*隔離性*意味著同時執行的事務彼此隔離:它們
### 單物件與多物件操作 {#sec_transactions_multi_object}
回顧一下,在 ACID 中,原子性和隔離性描述如果客戶端在同一事務中進行多次寫入,資料庫應該做什麼:
回顧一下,在 ACID 中,原子性和隔離性描述如果客戶端在同一事務中進行多次寫入,資料庫應該做什麼:
原子性
: 如果在寫入序列的中途發生錯誤,事務應該被中止,並且到該點為止所做的寫入應該被丟棄。換句話說,資料庫讓你免於擔心部分失敗,透過提供全有或全無的保證。
@ -689,7 +689,7 @@ VoltDB 還使用儲存過程進行復制:它不是將事務的寫入從一個
* 如果事務首先讀取然後寫入物件,它可以將其共享鎖升級為獨佔鎖。升級的工作方式與直接獲取獨佔鎖相同。
* 獲取鎖後,事務必須繼續持有鎖直到事務結束(提交或中止)。這就是"兩階段"名稱的來源:第一階段(事務執行時)是獲取鎖,第二階段(事務結束時)是釋放所有鎖。
由於使用如此多的鎖,很容易發生事務 A 等待事務 B 釋放其鎖,反之亦然的情況。這種情況稱為*死鎖*。資料庫自動檢測事務之間的死鎖並中止其中一個,以便其他事務可以取得進展。中止的事務需要由應用程式重試。
由於使用如此多的鎖,很容易發生事務 A 等待事務 B 釋放其鎖,反之亦然的情況。這種情況稱為*死鎖*。資料庫自動檢測事務之間的死鎖並中止其中一個,以便其他事務可以取得進展。中止的事務需要由應用程式重試。
#### 兩階段鎖定的效能 {#performance-of-two-phase-locking}

View File

@ -456,7 +456,7 @@ MapReduce 作業的輸出處理遵循同樣的原理。透過將輸入視為不
### Hadoop與分散式資料庫的對比
正如我們所看到的Hadoop 有點像 Unix 的分散式版本,其中 HDFS 是檔案系統,而 MapReduce 是 Unix 程序的怪異實現(總是在 Map 階段和 Reduce 階段執行 `sort` 工具)。我們瞭解如何在這些原語的基礎上實現各種連線和分組操作。
正如我們所看到的Hadoop 有點像 Unix 的分散式版本,其中 HDFS 是檔案系統,而 MapReduce 是 Unix 程序的怪異實現(總是在 Map 階段和 Reduce 階段執行 `sort` 工具)。我們瞭解如何在這些原語的基礎上實現各種連線和分組操作。
當 MapReduce 論文發表時【1】它從某種意義上來說 —— 並不新鮮。我們在前幾節中討論的所有處理和並行連線演算法已經在十多年前所謂的 **大規模並行處理MPPmassively parallel processing** 資料庫中實現了【3,40】。比如 Gamma database machine、Teradata 和 Tandem NonStop SQL 就是這方面的先驅【52】。
@ -472,7 +472,7 @@ MapReduce 作業的輸出處理遵循同樣的原理。透過將輸入視為不
這個想法與資料倉庫類似(請參閱 “[資料倉庫](/v1_tw/ch3#資料倉庫)”將大型組織的各個部分的資料集中在一起是很有價值的因為它可以跨越以前相互分離的資料集進行連線。MPP 資料庫所要求的謹慎模式設計拖慢了集中式資料收集速度;以原始形式收集資料,稍後再操心模式的設計,能使資料收集速度加快(有時被稱為 “**資料湖data lake**” 或 “**企業資料中心enterprise data hub**”【55】
不加區分的資料轉儲轉移解釋資料的負擔:資料集的生產者不再需要強制將其轉化為標準格式,資料的解釋成為消費者的問題(**讀時模式** 方法【56】請參閱 “[文件模型中的模式靈活性](/v1_tw/ch2#文件模型中的模式靈活性)”)。如果生產者和消費者是不同優先順序的不同團隊,這可能是一種優勢。甚至可能不存在一個理想的資料模型,對於不同目的有不同的合適視角。以原始形式簡單地轉儲資料,可以允許多種這樣的轉換。這種方法被稱為 **壽司原則sushi principle**“原始資料更好”【57】。
不加區分的資料轉儲轉移解釋資料的負擔:資料集的生產者不再需要強制將其轉化為標準格式,資料的解釋成為消費者的問題(**讀時模式** 方法【56】請參閱 “[文件模型中的模式靈活性](/v1_tw/ch2#文件模型中的模式靈活性)”)。如果生產者和消費者是不同優先順序的不同團隊,這可能是一種優勢。甚至可能不存在一個理想的資料模型,對於不同目的有不同的合適視角。以原始形式簡單地轉儲資料,可以允許多種這樣的轉換。這種方法被稱為 **壽司原則sushi principle**“原始資料更好”【57】。
因此Hadoop 經常被用於實現 ETL 過程(請參閱 “[資料倉庫](/v1_tw/ch3#資料倉庫)”):事務處理系統中的資料以某種原始形式轉儲到分散式檔案系統中,然後編寫 MapReduce 作業來清理資料,將其轉換為關係形式,並將其匯入 MPP 資料倉庫以進行分析。資料建模仍然在進行,但它在一個單獨的步驟中進行,與資料收集相解耦。這種解耦是可行的,因為分散式檔案系統支援以任何格式編碼的資料。

View File

@ -44,7 +44,7 @@ breadcrumbs: false
例如,你可能會首先將資料寫入 **記錄系統** 資料庫,捕獲對該資料庫所做的變更(請參閱 “[變更資料捕獲](/v1_tw/ch11#變更資料捕獲)”然後將變更以相同的順序應用於搜尋索引。如果變更資料捕獲CDC是更新索引的唯一方式則可以確定該索引完全派生自記錄系統因此與其保持一致除軟體錯誤外。寫入資料庫是向該系統提供新輸入的唯一方式。
允許應用程式直接寫入搜尋索引和資料庫引入如 [圖 11-4](/v1/ddia_1104.png) 所示的問題,其中兩個客戶端同時傳送衝突的寫入,且兩個儲存系統按不同順序處理它們。在這種情況下,既不是資料庫說了算,也不是搜尋索引說了算,所以它們做出了相反的決定,進入彼此間永續性的不一致狀態。
允許應用程式直接寫入搜尋索引和資料庫引入如 [圖 11-4](/v1/ddia_1104.png) 所示的問題,其中兩個客戶端同時傳送衝突的寫入,且兩個儲存系統按不同順序處理它們。在這種情況下,既不是資料庫說了算,也不是搜尋索引說了算,所以它們做出了相反的決定,進入彼此間永續性的不一致狀態。
如果你可以透過單個系統來提供所有使用者輸入,從而決定所有寫入的排序,則透過按相同順序處理寫入,可以更容易地衍生出其他資料表示。這是狀態機複製方法的一個應用,我們在 “[全序廣播](/v1_tw/ch9#全序廣播)” 中看到。無論你使用變更資料捕獲還是事件溯源日誌,都不如簡單的基於全序的決策原則更重要。
@ -177,7 +177,7 @@ Unix 和關係資料庫以非常不同的哲學來處理資訊管理問題。Uni
* 複製日誌,保持其他節點上資料的副本最新(請參閱 “[複製日誌的實現](/v1_tw/ch5#複製日誌的實現)”)
* 全文搜尋索引,允許在文字中進行關鍵字搜尋(請參閱 “[全文搜尋和模糊索引](/v1_tw/ch3#全文搜尋和模糊索引)”也內置於某些關係資料庫【1】
在 [第十章](/v1_tw/ch10) 和 [第十一章](/v1_tw/ch11) 中,出現了類似的主題。我們討論了如何構建全文搜尋索引(請參閱 “[批處理工作流的輸出](/v1_tw/ch10#批處理工作流的輸出)”),瞭解如何維護物化檢視(請參閱 “[維護物化檢視](/v1_tw/ch11#維護物化檢視)”)以及如何將變更從資料庫複製到衍生資料系統(請參閱 “[變更資料捕獲](/v1_tw/ch11#變更資料捕獲)”)。
在 [第十章](/v1_tw/ch10) 和 [第十一章](/v1_tw/ch11) 中,出現了類似的主題。我們討論了如何構建全文搜尋索引(請參閱 “[批處理工作流的輸出](/v1_tw/ch10#批處理工作流的輸出)”),瞭解如何維護物化檢視(請參閱 “[維護物化檢視](/v1_tw/ch11#維護物化檢視)”)以及如何將變更從資料庫複製到衍生資料系統(請參閱 “[變更資料捕獲](/v1_tw/ch11#變更資料捕獲)”)。
資料庫中內建的功能與人們用批處理和流處理器構建的衍生資料系統似乎有相似之處。

View File

@ -71,7 +71,7 @@ breadcrumbs: false
**圖 2-1 使用關係型模式來表示領英簡介**
例如,[圖 2-1](/v1/ddia_0201.png) 展示如何在關係模式中表示簡歷(一個 LinkedIn 簡介)。整個簡介可以透過一個唯一的識別符號 `user_id` 來標識。像 `first_name``last_name` 這樣的欄位每個使用者只出現一次,所以可以在 User 表上將其建模為列。但是,大多數人在職業生涯中擁有多於一份的工作,人們可能有不同樣的教育階段和任意數量的聯絡資訊。從使用者到這些專案之間存在一對多的關係,可以用多種方式來表示:
例如,[圖 2-1](/v1/ddia_0201.png) 展示如何在關係模式中表示簡歷(一個 LinkedIn 簡介)。整個簡介可以透過一個唯一的識別符號 `user_id` 來標識。像 `first_name``last_name` 這樣的欄位每個使用者只出現一次,所以可以在 User 表上將其建模為列。但是,大多數人在職業生涯中擁有多於一份的工作,人們可能有不同樣的教育階段和任意數量的聯絡資訊。從使用者到這些專案之間存在一對多的關係,可以用多種方式來表示:
* 傳統 SQL 模型SQL1999 之前)中,最常見的規範化表示形式是將職位,教育和聯絡資訊放在單獨的表中,對 User 表提供外部索引鍵引用,如 [圖 2-1](/v1/ddia_0201.png) 所示。
* 後續的 SQL 標準增加了對結構化資料型別和 XML 資料的支援;這允許將多值資料儲存在單行內,並支援在這些文件內查詢和索引。這些功能在 OracleIBM DB2MS SQL Server 和 PostgreSQL 中都有不同程度的支援【6,7】。JSON 資料型別也得到多個數據庫的支援,包括 IBM DB2MySQL 和 PostgreSQL 【8】。
@ -167,7 +167,7 @@ JSON 表示比 [圖 2-1](/v1/ddia_0201.png) 中的多表模式具有更好的 **
**圖 2-3 公司名不僅是字串還是一個指向公司實體的連結LinkedIn 截圖)**
[圖 2-4](/v1/ddia_0204.png) 闡明這些新功能需要如何使用多對多關係。每個虛線矩形內的資料可以分組成一個文件,但是對單位,學校和其他使用者的引用需要表示成引用,並且在查詢時需要連線。
[圖 2-4](/v1/ddia_0204.png) 闡明這些新功能需要如何使用多對多關係。每個虛線矩形內的資料可以分組成一個文件,但是對單位,學校和其他使用者的引用需要表示成引用,並且在查詢時需要連線。
![](/v1/ddia_0204.png)
@ -376,7 +376,7 @@ li.selected > p {
}
```
這裡的 CSS 選擇器 `li.selected > p` 聲明我們想要應用藍色樣式的元素的模式:即其直接父元素是具有 CSS 類 `selected``<li>` 元素的所有 `<p>` 元素。示例中的元素 `<p>Sharks</p>` 匹配此模式,但 `<p>Whales</p>` 不匹配,因為其 `<li>` 父元素缺少 `class="selected"`
這裡的 CSS 選擇器 `li.selected > p` 聲明我們想要應用藍色樣式的元素的模式:即其直接父元素是具有 CSS 類 `selected``<li>` 元素的所有 `<p>` 元素。示例中的元素 `<p>Sharks</p>` 匹配此模式,但 `<p>Whales</p>` 不匹配,因為其 `<li>` 父元素缺少 `class="selected"`
如果使用 XSL 而不是 CSS你可以做類似的事情
@ -608,7 +608,7 @@ CREATE
當 [圖 2-5](/v1/ddia_0205.png) 的所有頂點和邊被新增到資料庫後,讓我們提些有趣的問題:例如,找到所有從美國移民到歐洲的人的名字。更確切地說,這裡我們想要找到符合下面條件的所有頂點,並且返回這些頂點的 `name` 屬性:該頂點擁有一條連到美國任一位置的 `BORN_IN` 邊,和一條連到歐洲的任一位置的 `LIVING_IN` 邊。
[例 2-4]() 展示如何在 Cypher 中表達這個查詢。在 MATCH 子句中使用相同的箭頭符號來查詢圖中的模式:`(person) -[:BORN_IN]-> ()` 可以匹配 `BORN_IN` 邊的任意兩個頂點。該邊的尾節點被綁定了變數 `person`,頭節點則未被繫結。
[例 2-4]() 展示如何在 Cypher 中表達這個查詢。在 MATCH 子句中使用相同的箭頭符號來查詢圖中的模式:`(person) -[:BORN_IN]-> ()` 可以匹配 `BORN_IN` 邊的任意兩個頂點。該邊的尾節點被綁定了變數 `person`,頭節點則未被繫結。
**例 2-4 查詢所有從美國移民到歐洲的人的 Cypher 查詢:**
@ -842,7 +842,7 @@ SPARQL 是一種很好的查詢語言 —— 儘管它構想的語義網從未
[^viii]: Datomic 和 Cascalog 使用 Datalog 的 Clojure S 表示式語法。在下面的例子中使用了一個更容易閱讀的 Prolog 語法,但兩者沒有任何功能差異。
Datalog 的資料模型類似於三元組模式,但進行了一點泛化。把三元組寫成 **謂語****主語,賓語**),而不是寫三元語(**主語,謂語,賓語**)。[例 2-10]() 顯示如何用 Datalog 寫入我們的例子中的資料。
Datalog 的資料模型類似於三元組模式,但進行了一點泛化。把三元組寫成 **謂語****主語,賓語**),而不是寫三元語(**主語,謂語,賓語**)。[例 2-10]() 顯示如何用 Datalog 寫入我們的例子中的資料。
**例 2-10 用 Datalog 來表示圖 2-5 中的資料子集**

View File

@ -83,7 +83,7 @@ $ cat database
鍵值儲存與在大多數程式語言中可以找到的 **字典dictionary** 型別非常相似,通常字典都是用 **雜湊對映hash map****散列表hash table** 實現的。雜湊對映在許多演算法教科書中都有描述【1,2】所以這裡我們不會討論它的工作細節。既然我們已經可以用雜湊對映來表示 **記憶體中** 的資料結構,為什麼不使用它來索引 **硬碟上** 的資料呢?
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣,那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到資料檔案中的一個位元組偏移量,指明可以找到對應值的位置,如 [圖 3-1](/v1/ddia_0301.png) 所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找seek** 該位置並讀取該值即可。
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣,那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到資料檔案中的一個位元組偏移量,指明可以找到對應值的位置,如 [圖 3-1](/v1/ddia_0301.png) 所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找seek** 該位置並讀取該值即可。
![](/v1/ddia_0301.png)

View File

@ -100,7 +100,7 @@ JSON 比 XML 簡潔,但與二進位制格式相比還是太佔空間。這一
}
```
我們來看一個 MessagePack 的例子,它是一個 JSON 的二進位制編碼。圖 4-1 顯示如果使用 MessagePack 【14】對 [例 4-1]() 中的 JSON 文件進行編碼,則得到的位元組序列。前幾個位元組如下:
我們來看一個 MessagePack 的例子,它是一個 JSON 的二進位制編碼。圖 4-1 顯示如果使用 MessagePack 【14】對 [例 4-1]() 中的 JSON 文件進行編碼,則得到的位元組序列。前幾個位元組如下:
1. 第一個位元組 `0x83` 表示接下來是 **3** 個欄位(低四位 = `0x03`)的 **物件 object**(高四位 = `0x80`)。(如果想知道如果一個物件有 15 個以上的欄位會發生什麼情況,欄位的數量塞不進 4 個 bit 裡,那麼它會用另一個不同的型別識別符號,欄位的數量被編碼兩個或四個位元組)。
2. 第二個位元組 `0xa8` 表示接下來是 **8** 位元組長(低四位 = `0x08`)的字串(高四位 = `0x0a`)。

View File

@ -129,7 +129,7 @@ Cassandra 採取了折衷的策略【11, 12, 13】。Cassandra 中的表可以
假設你正在經營一個銷售二手車的網站(如 [圖 6-4](/v1/ddia_0604.png) 所示)。每個列表都有一個唯一的 ID—— 稱之為文件 ID—— 並且用文件 ID 對資料庫進行分割槽(例如,分割槽 0 中的 ID 0 到 499分割槽 1 中的 ID 500 到 999 等)。
你想讓使用者搜尋汽車,允許他們透過顏色和廠商過濾,所以需要一個在顏色和廠商上的次級索引(文件資料庫中這些是 **欄位field**,關係資料庫中這些是 **列column** )。如果你聲明索引,則資料庫可以自動執行索引 [^ii]。例如,無論何時將紅色汽車新增到資料庫,資料庫分割槽都會自動將其新增到索引條目 `color:red` 的文件 ID 列表中。
你想讓使用者搜尋汽車,允許他們透過顏色和廠商過濾,所以需要一個在顏色和廠商上的次級索引(文件資料庫中這些是 **欄位field**,關係資料庫中這些是 **列column** )。如果你聲明索引,則資料庫可以自動執行索引 [^ii]。例如,無論何時將紅色汽車新增到資料庫,資料庫分割槽都會自動將其新增到索引條目 `color:red` 的文件 ID 列表中。
[^ii]: 如果資料庫僅支援鍵值模型,則你可能會嘗試在應用程式程式碼中建立從值到文件 ID 的對映來實現次級索引。如果沿著這條路線走下去,請萬分小心,確保你的索引與底層資料保持一致。競爭條件和間歇性寫入失敗(其中一些更改已儲存,但其他更改未儲存)很容易導致資料不同步 - 請參閱 “[多物件事務的需求](/v1_tw/ch7#多物件事務的需求)”。
@ -278,7 +278,7 @@ Cassandra 和 Ketama 使用的第三種方法是使分割槽數與節點數成
**圖 6-8 使用 ZooKeeper 跟蹤分割槽分配給節點。**
例如LinkedIn的Espresso使用Helix 【31】進行叢集管理依靠ZooKeeper實現如[圖6-8](/v1/ddia_0608.png)所示的路由層。HBase、SolrCloud和Kafka也使用ZooKeeper來跟蹤分割槽分配。MongoDB具有類似的體系結構但它依賴於自己的**配置伺服器config server** 實現和mongos守護程序作為路由層。
例如LinkedIn的Espresso使用Helix 【31】進行叢集管理依靠ZooKeeper實現如[圖6-8](/v1/ddia_0608.png)所示的路由層。HBase、SolrCloud和Kafka也使用ZooKeeper來跟蹤分割槽分配。MongoDB具有類似的體系結構但它依賴於自己的**配置伺服器config server** 實現和mongos守護程序作為路由層。
Cassandra 和 Riak 採取不同的方法:他們在節點之間使用 **流言協議gossip protocol** 來傳播叢集狀態的變化。請求可以傳送到任意節點,該節點會轉發到包含所請求的分割槽的適當節點([圖 6-7](/v1/ddia_0607.png) 中的方法 1。這個模型在資料庫節點中增加了更多的複雜性但是避免了對像 ZooKeeper 這樣的外部協調服務的依賴。

View File

@ -347,7 +347,7 @@ CAP 定理的正式定義僅限於很狹隘的範圍【30】它只考慮了
因此,根據這個定義,在線性一致的資料儲存中是不存在併發操作的:必須有且僅有一條時間線,所有的操作都在這條時間線上,構成一個全序關係。可能有幾個請求在等待處理,但是資料儲存確保了每個請求都是在唯一時間線上的某個時間點自動處理的,不存在任何併發。
併發意味著時間線會分岔然後合併 —— 在這種情況下,不同分支上的操作是無法比較的(即併發操作)。在 [第五章](/v1_tw/ch5) 中我們看到了這種現象:例如,[圖 5-14](/v1/ddia_0514.png) 並不是一條直線的全序關係,而是一堆不同的操作併發進行。圖中的箭頭指明因果依賴 —— 操作的偏序。
併發意味著時間線會分岔然後合併 —— 在這種情況下,不同分支上的操作是無法比較的(即併發操作)。在 [第五章](/v1_tw/ch5) 中我們看到了這種現象:例如,[圖 5-14](/v1/ddia_0514.png) 並不是一條直線的全序關係,而是一堆不同的操作併發進行。圖中的箭頭指明因果依賴 —— 操作的偏序。
如果你熟悉像 Git 這樣的分散式版本控制系統,那麼其版本歷史與因果關係圖極其相似。通常,一個 **提交Commit** 發生在另一個提交之後,在一條直線上。但是有時你會遇到分支(當多個人同時在一個專案上工作時),**合併Merge** 會在這些併發建立的提交相融合時建立。
@ -518,7 +518,7 @@ CAP 定理的正式定義僅限於很狹隘的範圍【30】它只考慮了
#### 使用線性一致性儲存實現全序廣播
上一節介紹如何從全序廣播構建一個線性一致的 CAS 操作。我們也可以把它反過來,假設我們有線性一致的儲存,接下來會展示如何在此基礎上構建全序廣播。
上一節介紹如何從全序廣播構建一個線性一致的 CAS 操作。我們也可以把它反過來,假設我們有線性一致的儲存,接下來會展示如何在此基礎上構建全序廣播。
最簡單的方法是假設你有一個線性一致的暫存器來儲存一個整數,並且有一個原子 **自增並返回** 操作【28】。或者原子 CAS 操作也可以完成這項工作。
@ -628,7 +628,7 @@ CAP 定理的正式定義僅限於很狹隘的範圍【30】它只考慮了
因此,該協議包含兩個關鍵的 “不歸路” 點:當參與者投票 “是” 時,它承諾它稍後肯定能夠提交(儘管協調者可能仍然選擇放棄);以及一旦協調者做出決定,這一決定是不可撤銷的。這些承諾保證了 2PC 的原子性(單節點原子提交將這兩個事件合為了一體:將提交記錄寫入事務日誌)。
回到婚姻的比喻,在說 “我願意” 之前,你和你的新娘 / 新郎有中止這個事務的自由,只要回覆 “沒門!” 就行(或者有類似效果的話)。然而在說了 “我願意” 之後,你就不能撤回那個聲明。如果你說 “我願意” 後暈倒了,沒有聽到司儀說 “你們現在是夫妻了”,那也並不會改變事務已經提交的現實。當你稍後恢復意識時,可以透過查詢司儀的全域性事務 ID 狀態來確定你是否已經成婚,或者你可以等待司儀重試下一次提交請求(因為重試將在你無意識期間一直持續)。
回到婚姻的比喻,在說 “我願意” 之前,你和你的新娘 / 新郎有中止這個事務的自由,只要回覆 “沒門!” 就行(或者有類似效果的話)。然而在說了 “我願意” 之後,你就不能撤回那個聲明。如果你說 “我願意” 後暈倒了,沒有聽到司儀說 “你們現在是夫妻了”,那也並不會改變事務已經提交的現實。當你稍後恢復意識時,可以透過查詢司儀的全域性事務 ID 狀態來確定你是否已經成婚,或者你可以等待司儀重試下一次提交請求(因為重試將在你無意識期間一直持續)。
#### 協調者失效
@ -731,7 +731,7 @@ XA 事務解決了保持多個參與者(資料系統)相互一致的現實
非正式地,共識意味著讓幾個節點就某事達成一致。例如,如果有幾個人 **同時concurrently** 嘗試預訂飛機上的最後一個座位,或劇院中的同一個座位,或者嘗試使用相同的使用者名稱註冊一個帳戶。共識演算法可以用來確定這些 **互不相容mutually incompatible** 的操作中,哪一個才是贏家。
共識問題通常形式化如下:一個或多個節點可以 **提議propose** 某些值,而共識演算法 **決定decides** 採用其中的某個值。在座位預訂的例子中,當幾個顧客同時試圖訂購最後一個座位時,處理顧客請求的每個節點可以 **提議** 將要服務的顧客的 ID**決定** 指明哪個顧客獲得了座位。
共識問題通常形式化如下:一個或多個節點可以 **提議propose** 某些值,而共識演算法 **決定decides** 採用其中的某個值。在座位預訂的例子中,當幾個顧客同時試圖訂購最後一個座位時,處理顧客請求的每個節點可以 **提議** 將要服務的顧客的 ID**決定** 指明哪個顧客獲得了座位。
在這種形式下共識演算法必須滿足以下性質【25】[^xiii]