mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
merge calibration and update zh-tw content
This commit is contained in:
parent
b9d3f4e854
commit
4e4193a40a
17
README.md
17
README.md
@ -2,12 +2,15 @@
|
||||
|
||||
- 作者: [Martin Kleppmann](https://martin.kleppmann.com)
|
||||
- 原名:[《Designing Data-Intensive Applications》](http://shop.oreilly.com/product/0636920032175.do)
|
||||
- 译者:[冯若航]( https://vonng.com) (rh@vonng.com )
|
||||
- 使用 [Typora](https://www.typora.io)、[Gitbook](https://vonng.gitbooks.io/ddia-cn/content/),[Github Pages](https://vonng.github.io/ddia)以获取最佳阅读体验。
|
||||
- 繁体:[繁體中文版本](zh-tw/README.md)
|
||||
- 本地:若无合适的Markdown编辑器,您可在项目根目录中执行`make`,并通过浏览器阅读( Powered by [Docsify](https://docsify.js.org/#/zh-cn/) )。
|
||||
- 译者:[冯若航](https://vonng.com) ([@Vonng](https://vonng.com/en/))
|
||||
- 校订: [@yingang](https://github.com/yingang)
|
||||
- 繁体:[繁體中文版本](zh-tw/README.md) by [@afunTW](https://github.com/afunTW)
|
||||
|
||||
|
||||
> 使用 [Typora](https://www.typora.io)、[Gitbook](https://vonng.gitbooks.io/ddia-cn/content/),[Github Pages](https://vonng.github.io/ddia)以获取最佳阅读体验。
|
||||
>
|
||||
> 本地:您可在项目根目录中执行`make`,并通过浏览器阅读([在线预览](http://ddia.vonng.com/#/))。
|
||||
|
||||
## 译序
|
||||
|
||||
> 不懂数据库的全栈工程师不是好架构师
|
||||
@ -133,23 +136,23 @@
|
||||
|
||||
本译文只供学习研究参考之用,不得公开传播发行或用于商业用途。有能力阅读英文书籍者请购买正版支持。
|
||||
|
||||
|
||||
## 贡献
|
||||
|
||||
0. 全文校订 by [@yingang](https://github.com/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) 与[第10章的初翻](https://github.com/Vonng/ddia/commit/9de8dbd1bfe6fbb03b3bf6c1a1aa2291aed2490e) by @[MuAlex](https://github.com/Vonng/ddia/commits?author=MuAlex)
|
||||
4. [第一部分](part-i.md)前言,[ch2](ch2.md)校正 by [@jiajiadebug](https://github.com/Vonng/ddia/commits?author=jiajiadebug)
|
||||
5. [词汇表](glossary.md)、[后记]()关于野猪的部分 by @[Chowss](https://github.com/Vonng/ddia/commits?author=Chowss)
|
||||
6. [繁體中文](https://github.com/Vonng/ddia/pulls)版本与转换脚本 by [@afunTW](https://github.com/afunTW)
|
||||
7. [对各章进行大量翻译更正润色](https://github.com/Vonng/ddia/pull/118) by [@yingang](https://github.com/yingang)
|
||||
8. 感谢所有作出贡献,提出意见的朋友们:
|
||||
7. 感谢所有作出贡献,提出意见的朋友们:
|
||||
|
||||
<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 |
|
||||
| ----------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| [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() |
|
||||
|
@ -1,6 +0,0 @@
|
||||
|
||||
|
||||
# 设计数据密集型应用 <small></small>
|
||||
|
||||
> <b>Designing Data Intensive Applications</b>
|
||||
|
@ -27,7 +27,7 @@
|
||||
name: '设计数据密集型应用',
|
||||
repo: 'Vonng/ddia',
|
||||
auto2top: true,
|
||||
coverpage: true,
|
||||
coverpage: false,
|
||||
executeScript: true,
|
||||
loadSidebar: true,
|
||||
loadNavbar: true,
|
||||
|
132
zh-tw/README.md
132
zh-tw/README.md
@ -90,29 +90,111 @@
|
||||
|
||||
|
||||
|
||||
* [第一章:可靠性、可伸縮性、可維護性](ch1.md)
|
||||
* [第一章:可靠性、可伸縮性、可維護性](ch1.md)
|
||||
|
||||
* [關於資料系統的思考](ch1.md#關於資料系統的思考)
|
||||
|
||||
* [可靠性](ch1.md#可靠性)
|
||||
|
||||
* [可伸縮性](ch1.md#可伸縮性)
|
||||
|
||||
* [可維護性](ch1.md#可維護性)
|
||||
|
||||
* [本章小結](ch1.md#本章小結)
|
||||
|
||||
* [第二章:資料模型與查詢語言](ch2.md)
|
||||
|
||||
* [第三章:儲存與檢索](ch3.md)
|
||||
* [關係模型與文件模型](ch2.md#關係模型與文件模型)
|
||||
|
||||
* [資料查詢語言](ch2.md#資料查詢語言)
|
||||
|
||||
* [圖資料模型](ch2.md#圖資料模型)
|
||||
|
||||
* [本章小結](ch2.md#本章小結)
|
||||
|
||||
* [第三章:儲存與檢索](ch3.md)
|
||||
|
||||
* [驅動資料庫的資料結構](ch3.md#驅動資料庫的資料結構)
|
||||
|
||||
* [事務處理還是分析?](ch3.md#事務處理還是分析?)
|
||||
|
||||
* [列儲存](ch3.md#列儲存)
|
||||
|
||||
* [本章小結](ch3.md#本章小結)
|
||||
|
||||
* [第四章:編碼與演化](ch4.md)
|
||||
|
||||
* [編碼資料的格式](ch4.md#編碼資料的格式)
|
||||
|
||||
* [資料流的型別](ch4.md#資料流的型別)
|
||||
|
||||
* [本章小結](ch4.md#本章小結)
|
||||
|
||||
|
||||
|
||||
### [第二部分:分散式資料](part-ii.md)
|
||||
|
||||
|
||||
|
||||
* [第五章:複製](ch5.md)
|
||||
* [第五章:複製](ch5.md)
|
||||
|
||||
* [第六章:分割槽](ch6.md)
|
||||
* [領導者與追隨者](ch5.md#領導者與追隨者)
|
||||
|
||||
* [第七章:事務](ch7.md)
|
||||
* [複製延遲問題](ch5.md#複製延遲問題)
|
||||
|
||||
* [第八章:分散式系統的麻煩](ch8.md)
|
||||
* [多主複製](ch5.md#多主複製)
|
||||
|
||||
* [第九章:一致性與共識](ch9.md)
|
||||
* [無主複製](ch5.md#無主複製)
|
||||
|
||||
* [本章小結](ch5.md#本章小結)
|
||||
|
||||
* [第六章:分割槽](ch6.md)
|
||||
|
||||
* [分割槽與複製](ch6.md#分割槽與複製)
|
||||
|
||||
* [鍵值資料的分割槽](ch6.md#鍵值資料的分割槽)
|
||||
|
||||
* [分割槽與次級索引](ch6.md#分割槽與次級索引)
|
||||
|
||||
* [分割槽再平衡](ch6.md#分割槽再平衡)
|
||||
|
||||
* [請求路由](ch6.md#請求路由)
|
||||
|
||||
* [本章小結](ch6.md#本章小結)
|
||||
|
||||
* [第七章:事務](ch7.md)
|
||||
|
||||
* [事務的棘手概念](ch7.md#事務的棘手概念)
|
||||
|
||||
* [弱隔離級別](ch7.md#弱隔離級別)
|
||||
|
||||
* [可序列化](ch7.md#可序列化)
|
||||
|
||||
* [本章小結](ch7.md#本章小結)
|
||||
|
||||
* [第八章:分散式系統的麻煩](ch8.md)
|
||||
|
||||
* [故障與部分失效](ch8.md#故障與部分失效)
|
||||
|
||||
* [不可靠的網路](ch8.md#不可靠的網路)
|
||||
|
||||
* [不可靠的時鐘](ch8.md#不可靠的時鐘)
|
||||
|
||||
* [知識、真相與謊言](ch8.md#知識、真相與謊言)
|
||||
|
||||
* [本章小結](ch8.md#本章小結)
|
||||
|
||||
* [第九章:一致性與共識](ch9.md)
|
||||
|
||||
* [一致性保證](ch9.md#一致性保證)
|
||||
|
||||
* [線性一致性](ch9.md#線性一致性)
|
||||
|
||||
* [順序保證](ch9.md#順序保證)
|
||||
|
||||
* [分散式事務與共識](ch9.md#分散式事務與共識)
|
||||
|
||||
* [本章小結](ch9.md#本章小結)
|
||||
|
||||
|
||||
|
||||
@ -120,11 +202,37 @@
|
||||
|
||||
|
||||
|
||||
* [第十章:批處理](ch10.md)
|
||||
* [第十章:批處理](ch10.md)
|
||||
|
||||
* [第十一章:流處理](ch11.md)
|
||||
* [使用Unix工具的批處理](ch10.md#使用Unix工具的批處理)
|
||||
|
||||
* [第十二章:資料系統的未來](ch12.md)
|
||||
* [MapReduce和分散式檔案系統](ch10.md#MapReduce和分散式檔案系統)
|
||||
|
||||
* [MapReduce之後](ch10.md#MapReduce之後)
|
||||
|
||||
* [本章小結](ch10.md#本章小結)
|
||||
|
||||
* [第十一章:流處理](ch11.md)
|
||||
|
||||
* [傳遞事件流](ch11.md#傳遞事件流)
|
||||
|
||||
* [流與資料庫](ch11.md#流與資料庫)
|
||||
|
||||
* [流處理](ch11.md#流處理)
|
||||
|
||||
* [本章小結](ch11.md#本章小結)
|
||||
|
||||
* [第十二章:資料系統的未來](ch12.md)
|
||||
|
||||
* [資料整合](ch12.md#資料整合)
|
||||
|
||||
* [分拆資料庫](ch12.md#分拆資料庫)
|
||||
|
||||
* [將事情做正確](ch12.md#將事情做正確)
|
||||
|
||||
* [做正確的事情](ch12.md#做正確的事情)
|
||||
|
||||
* [本章小結](ch12.md#本章小結)
|
||||
|
||||
|
||||
|
||||
@ -178,7 +286,7 @@
|
||||
|
||||
6. [繁體中文](https://github.com/Vonng/ddia/pulls)版本與轉換指令碼 by [@afunTW](https://github.com/afunTW)
|
||||
|
||||
7. [對第各章進行大量翻譯更正潤色](https://github.com/Vonng/ddia/pull/118) by [@yingang](https://github.com/yingang)
|
||||
7. [對各章進行大量翻譯更正潤色](https://github.com/Vonng/ddia/pull/118) by [@yingang](https://github.com/yingang)
|
||||
|
||||
8. 感謝所有作出貢獻,提出意見的朋友們:
|
||||
|
||||
@ -200,7 +308,7 @@
|
||||
|
||||
| [119](https://github.com/Vonng/ddia/pull/119) | [@cclauss](https://github.com/cclauss) | Streamline file operations in convert() |
|
||||
|
||||
| [118](https://github.com/Vonng/ddia/pull/118) | [@yingang](https://github.com/yingang) | translation updates (chapter 2 and 3) |
|
||||
| [118](https://github.com/Vonng/ddia/pull/118) | [@yingang](https://github.com/yingang) | translation updates (chapter 2 and 4) |
|
||||
|
||||
| [117](https://github.com/Vonng/ddia/pull/117) | [@feeeei](https://github.com/feeeei) | 統一每章的標題格式 |
|
||||
|
||||
|
26
zh-tw/ch1.md
26
zh-tw/ch1.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch1.png)
|
||||
![](img/ch1.png)
|
||||
|
||||
|
||||
|
||||
@ -82,11 +82,11 @@
|
||||
|
||||
|
||||
|
||||
例如,如果將快取(應用管理的快取層,Memcached或同類產品)和全文搜尋(全文搜尋伺服器,例如Elasticsearch或Solr)功能從主資料庫剝離出來,那麼使快取/索引與主資料庫保持同步通常是應用程式碼的責任。[圖1-1](../img/fig1-1.png) 給出了這種架構可能的樣子(細節將在後面的章節中詳細介紹)。
|
||||
例如,如果將快取(應用管理的快取層,Memcached或同類產品)和全文搜尋(全文搜尋伺服器,例如Elasticsearch或Solr)功能從主資料庫剝離出來,那麼使快取/索引與主資料庫保持同步通常是應用程式碼的責任。[圖1-1](img/fig1-1.png) 給出了這種架構可能的樣子(細節將在後面的章節中詳細介紹)。
|
||||
|
||||
|
||||
|
||||
![](../img/fig1-1.png)
|
||||
![](img/fig1-1.png)
|
||||
|
||||
|
||||
|
||||
@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
1. 釋出推文時,只需將新推文插入全域性推文集合即可。當一個使用者請求自己的主頁時間線時,首先查詢他關注的所有人,查詢這些被關注使用者釋出的推文並按時間順序合併。在如[圖1-2](../img/fig1-2.png)所示的關係型資料庫中,可以編寫這樣的查詢:
|
||||
1. 釋出推文時,只需將新推文插入全域性推文集合即可。當一個使用者請求自己的主頁時間線時,首先查詢他關注的所有人,查詢這些被關注使用者釋出的推文並按時間順序合併。在如[圖1-2](img/fig1-2.png)所示的關係型資料庫中,可以編寫這樣的查詢:
|
||||
|
||||
|
||||
|
||||
@ -368,7 +368,7 @@
|
||||
|
||||
```
|
||||
|
||||
![](../img/fig1-2.png)
|
||||
![](img/fig1-2.png)
|
||||
|
||||
|
||||
|
||||
@ -376,11 +376,11 @@
|
||||
|
||||
|
||||
|
||||
2. 為每個使用者的主頁時間線維護一個快取,就像每個使用者的推文收件箱([圖1-3](../img/fig1-3.png))。 當一個使用者釋出推文時,查詢所有關注該使用者的人,並將新的推文插入到每個主頁時間線快取中。 因此讀取主頁時間線的請求開銷很小,因為結果已經提前計算好了。
|
||||
2. 為每個使用者的主頁時間線維護一個快取,就像每個使用者的推文收件箱([圖1-3](img/fig1-3.png))。 當一個使用者釋出推文時,查詢所有關注該使用者的人,並將新的推文插入到每個主頁時間線快取中。 因此讀取主頁時間線的請求開銷很小,因為結果已經提前計算好了。
|
||||
|
||||
|
||||
|
||||
![](../img/fig1-3.png)
|
||||
![](img/fig1-3.png)
|
||||
|
||||
|
||||
|
||||
@ -442,11 +442,11 @@
|
||||
|
||||
|
||||
|
||||
在[圖1-4](../img/fig1-4.png)中,每個灰條代表一次對服務的請求,其高度表示請求花費了多長時間。大多數請求是相當快的,但偶爾會出現需要更長的時間的異常值。這也許是因為緩慢的請求實質上開銷更大,例如它們可能會處理更多的資料。但即使(你認為)所有請求都花費相同時間的情況下,隨機的附加延遲也會導致結果變化,例如:上下文切換到後臺程序,網路資料包丟失與TCP重傳,垃圾收集暫停,強制從磁碟讀取的頁面錯誤,伺服器機架中的震動【18】,還有很多其他原因。
|
||||
在[圖1-4](img/fig1-4.png)中,每個灰條代表一次對服務的請求,其高度表示請求花費了多長時間。大多數請求是相當快的,但偶爾會出現需要更長的時間的異常值。這也許是因為緩慢的請求實質上開銷更大,例如它們可能會處理更多的資料。但即使(你認為)所有請求都花費相同時間的情況下,隨機的附加延遲也會導致結果變化,例如:上下文切換到後臺程序,網路資料包丟失與TCP重傳,垃圾收集暫停,強制從磁碟讀取的頁面錯誤,伺服器機架中的震動【18】,還有很多其他原因。
|
||||
|
||||
|
||||
|
||||
![](../img/fig1-4.png)
|
||||
![](img/fig1-4.png)
|
||||
|
||||
|
||||
|
||||
@ -466,7 +466,7 @@
|
||||
|
||||
|
||||
|
||||
為了弄清異常值有多糟糕,可以看看更高的百分位點,例如第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)所示。
|
||||
為了弄清異常值有多糟糕,可以看看更高的百分位點,例如第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)所示。
|
||||
|
||||
|
||||
|
||||
@ -494,7 +494,7 @@
|
||||
|
||||
>
|
||||
|
||||
> 在多重呼叫的後端服務裡,高百分位數變得特別重要。即使並行呼叫,終端使用者請求仍然需要等待最慢的並行呼叫完成。如[圖1-5](../img/fig1-5.png)所示,只需要一個緩慢的呼叫就可以使整個終端使用者請求變慢。即使只有一小部分後端呼叫速度較慢,如果終端使用者請求需要多個後端呼叫,則獲得較慢呼叫的機會也會增加,因此較高比例的終端使用者請求速度會變慢(效果稱為尾部延遲放大【24】)。
|
||||
> 在多重呼叫的後端服務裡,高百分位數變得特別重要。即使並行呼叫,終端使用者請求仍然需要等待最慢的並行呼叫完成。如[圖1-5](img/fig1-5.png)所示,只需要一個緩慢的呼叫就可以使整個終端使用者請求變慢。即使只有一小部分後端呼叫速度較慢,如果終端使用者請求需要多個後端呼叫,則獲得較慢呼叫的機會也會增加,因此較高比例的終端使用者請求速度會變慢(效果稱為尾部延遲放大【24】)。
|
||||
|
||||
>
|
||||
|
||||
@ -506,7 +506,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig1-5.png)
|
||||
![](img/fig1-5.png)
|
||||
|
||||
|
||||
|
||||
@ -754,7 +754,7 @@
|
||||
|
||||
|
||||
|
||||
在本書後面的[第三部分](part-iii.md)中,我們將看到一種模式:幾個元件協同工作以構成一個完整的系統(如[圖1-1](../img/fig1-1.png)中的例子)
|
||||
在本書後面的[第三部分](part-iii.md)中,我們將看到一種模式:幾個元件協同工作以構成一個完整的系統(如[圖1-1](img/fig1-1.png)中的例子)
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch10.png)
|
||||
![](img/ch10.png)
|
||||
|
||||
|
||||
|
||||
@ -496,7 +496,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
[圖10-1]()顯示了Hadoop MapReduce作業中的資料流。其並行化基於分割槽(參見[第6章](ch6.md)):作業的輸入通常是HDFS中的一個目錄,輸入目錄中的每個檔案或檔案塊都被認為是一個單獨的分割槽,可以單獨處理map任務([圖10-1](../img/fig10-1.png)中的m1,m2和m3標記)。
|
||||
[圖10-1]()顯示了Hadoop MapReduce作業中的資料流。其並行化基於分割槽(參見[第6章](ch6.md)):作業的輸入通常是HDFS中的一個目錄,輸入目錄中的每個檔案或檔案塊都被認為是一個單獨的分割槽,可以單獨處理map任務([圖10-1](img/fig10-1.png)中的m1,m2和m3標記)。
|
||||
|
||||
|
||||
|
||||
@ -504,7 +504,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
![](../img/fig10-1.png)
|
||||
![](img/fig10-1.png)
|
||||
|
||||
|
||||
|
||||
@ -596,11 +596,11 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
[圖10-2](../img/fig10-2.png)給出了一個批處理作業中連線的典型例子。左側是事件日誌,描述登入使用者在網站上做的事情(稱為**活動事件(activity events)**或**點選流資料(clickstream data)**),右側是使用者資料庫。 你可以將此示例看作是星型模式的一部分(參閱“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”):事件日誌是事實表,使用者資料庫是其中的一個維度。
|
||||
[圖10-2](img/fig10-2.png)給出了一個批處理作業中連線的典型例子。左側是事件日誌,描述登入使用者在網站上做的事情(稱為**活動事件(activity events)**或**點選流資料(clickstream data)**),右側是使用者資料庫。 你可以將此示例看作是星型模式的一部分(參閱“[星型和雪花型:分析的模式](ch3.md#星型和雪花型:分析的模式)”):事件日誌是事實表,使用者資料庫是其中的一個維度。
|
||||
|
||||
|
||||
|
||||
![](../img/fig10-2.png)
|
||||
![](img/fig10-2.png)
|
||||
|
||||
|
||||
|
||||
@ -628,11 +628,11 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
回想一下,Mapper的目的是從每個輸入記錄中提取一對鍵值。在[圖10-2](../img/fig10-2.png)的情況下,這個鍵就是使用者ID:一組Mapper會掃過活動事件(提取使用者ID作為鍵,活動事件作為值),而另一組Mapper將會掃過使用者資料庫(提取使用者ID作為鍵,使用者的出生日期作為值)。這個過程如[圖10-3](../img/fig10-3.png)所示。
|
||||
回想一下,Mapper的目的是從每個輸入記錄中提取一對鍵值。在[圖10-2](img/fig10-2.png)的情況下,這個鍵就是使用者ID:一組Mapper會掃過活動事件(提取使用者ID作為鍵,活動事件作為值),而另一組Mapper將會掃過使用者資料庫(提取使用者ID作為鍵,使用者的出生日期作為值)。這個過程如[圖10-3](img/fig10-3.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig10-3.png)
|
||||
![](img/fig10-3.png)
|
||||
|
||||
|
||||
|
||||
@ -696,7 +696,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
#### 處理傾斜
|
||||
#### 處理偏斜
|
||||
|
||||
|
||||
|
||||
@ -752,7 +752,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
例如,假設在[圖10-2](../img/fig10-2.png)的情況下,使用者資料庫小到足以放進記憶體中。在這種情況下,當Mapper啟動時,它可以首先將使用者資料庫從分散式檔案系統讀取到記憶體中的雜湊中。完成此操作後,Map程式可以掃描使用者活動事件,並簡單地在散列表中查詢每個事件的使用者ID[^vi]。
|
||||
例如,假設在[圖10-2](img/fig10-2.png)的情況下,使用者資料庫小到足以放進記憶體中。在這種情況下,當Mapper啟動時,它可以首先將使用者資料庫從分散式檔案系統讀取到記憶體中的雜湊中。完成此操作後,Map程式可以掃描使用者活動事件,並簡單地在散列表中查詢每個事件的使用者ID[^vi]。
|
||||
|
||||
|
||||
|
||||
@ -760,7 +760,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
參與連線的較大輸入的每個檔案塊各有一個Mapper(在[圖10-2](../img/fig10-2.png)的例子中活動事件是較大的輸入)。每個Mapper都會將較小輸入整個載入到記憶體中。
|
||||
參與連線的較大輸入的每個檔案塊各有一個Mapper(在[圖10-2](img/fig10-2.png)的例子中活動事件是較大的輸入)。每個Mapper都會將較小輸入整個載入到記憶體中。
|
||||
|
||||
|
||||
|
||||
@ -776,7 +776,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
如果Map端連線的輸入以相同的方式進行分割槽,則雜湊連線方法可以獨立應用於每個分割槽。在[圖10-2](../img/fig10-2.png)的情況中,你可以根據使用者ID的最後一位十進位制數字來對活動事件和使用者資料庫進行分割槽(因此連線兩側各有10個分割槽)。例如,Mapper3首先將所有具有以3結尾的ID的使用者載入到散列表中,然後掃描ID為3的每個使用者的所有活動事件。
|
||||
如果Map端連線的輸入以相同的方式進行分割槽,則雜湊連線方法可以獨立應用於每個分割槽。在[圖10-2](img/fig10-2.png)的情況中,你可以根據使用者ID的最後一位十進位制數字來對活動事件和使用者資料庫進行分割槽(因此連線兩側各有10個分割槽)。例如,Mapper3首先將所有具有以3結尾的ID的使用者載入到散列表中,然後掃描ID為3的每個使用者的所有活動事件。
|
||||
|
||||
|
||||
|
||||
@ -1228,7 +1228,7 @@ top5.each{|count, url| puts "#{count} #{url}" } # 5
|
||||
|
||||
|
||||
|
||||
許多圖演算法是透過一次遍歷一條邊來表示的,將一個頂點與近鄰的頂點連線起來,以傳播一些資訊,並不斷重複,直到滿足一些條件為止 —— 例如,直到沒有更多的邊要跟進,或直到一些指標收斂。我們在[圖2-6](../img/fig2-6.png)中看到一個例子,它透過重複跟進標明地點歸屬關係的邊,生成了資料庫中北美包含的所有地點列表(這種演算法被稱為**閉包傳遞(transitive closure)**)。
|
||||
許多圖演算法是透過一次遍歷一條邊來表示的,將一個頂點與近鄰的頂點連線起來,以傳播一些資訊,並不斷重複,直到滿足一些條件為止 —— 例如,直到沒有更多的邊要跟進,或直到一些指標收斂。我們在[圖2-6](img/fig2-6.png)中看到一個例子,它透過重複跟進標明地點歸屬關係的邊,生成了資料庫中北美包含的所有地點列表(這種演算法被稱為**閉包傳遞(transitive closure)**)。
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch11.png)
|
||||
![](img/ch11.png)
|
||||
|
||||
|
||||
|
||||
@ -190,7 +190,7 @@
|
||||
|
||||
|
||||
|
||||
當多個消費者從同一主題中讀取訊息時,有使用兩種主要的訊息傳遞模式,如[圖11-1](../img/fig11-1.png)所示:
|
||||
當多個消費者從同一主題中讀取訊息時,有使用兩種主要的訊息傳遞模式,如[圖11-1](img/fig11-1.png)所示:
|
||||
|
||||
|
||||
|
||||
@ -210,7 +210,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig11-1.png)
|
||||
![](img/fig11-1.png)
|
||||
|
||||
|
||||
|
||||
@ -234,11 +234,11 @@
|
||||
|
||||
|
||||
|
||||
當與負載均衡相結合時,這種重傳行為對訊息的順序有種有趣的影響。在[圖11-2](../img/fig11-2.png)中,消費者通常按照生產者傳送的順序處理訊息。然而消費者2在處理訊息m3時崩潰,與此同時消費者1正在處理訊息m4。未確認的訊息m3隨後被重新發送給消費者1,結果消費者1按照m4,m3,m5的順序處理訊息。因此m3和m4的交付順序與以生產者1的傳送順序不同。
|
||||
當與負載均衡相結合時,這種重傳行為對訊息的順序有種有趣的影響。在[圖11-2](img/fig11-2.png)中,消費者通常按照生產者傳送的順序處理訊息。然而消費者2在處理訊息m3時崩潰,與此同時消費者1正在處理訊息m4。未確認的訊息m3隨後被重新發送給消費者1,結果消費者1按照m4,m3,m5的順序處理訊息。因此m3和m4的交付順序與以生產者1的傳送順序不同。
|
||||
|
||||
|
||||
|
||||
![](../img/fig11-2.png)
|
||||
![](img/fig11-2.png)
|
||||
|
||||
|
||||
|
||||
@ -286,15 +286,15 @@
|
||||
|
||||
|
||||
|
||||
為了伸縮超出單個磁碟所能提供的更高吞吐量,可以對日誌進行**分割槽**(在[第6章](ch6.md)的意義上)。不同的分割槽可以託管在不同的機器上,且每個分割槽都拆分出一份能獨立於其他分割槽進行讀寫的日誌。一個主題可以定義為一組攜帶相同型別訊息的分割槽。這種方法如[圖11-3](../img/fig11-3.png)所示。
|
||||
為了伸縮超出單個磁碟所能提供的更高吞吐量,可以對日誌進行**分割槽**(在[第6章](ch6.md)的意義上)。不同的分割槽可以託管在不同的機器上,且每個分割槽都拆分出一份能獨立於其他分割槽進行讀寫的日誌。一個主題可以定義為一組攜帶相同型別訊息的分割槽。這種方法如[圖11-3](img/fig11-3.png)所示。
|
||||
|
||||
|
||||
|
||||
在每個分割槽內,代理為每個訊息分配一個單調遞增的序列號或**偏移量(offset)**(在[圖11-3](../img/fig11-3.png)中,框中的數字是訊息偏移量)。這種序列號是有意義的,因為分割槽是僅追加寫入的,所以分割槽內的訊息是完全有序的。沒有跨不同分割槽的順序保證。
|
||||
在每個分割槽內,代理為每個訊息分配一個單調遞增的序列號或**偏移量(offset)**(在[圖11-3](img/fig11-3.png)中,框中的數字是訊息偏移量)。這種序列號是有意義的,因為分割槽是僅追加寫入的,所以分割槽內的訊息是完全有序的。沒有跨不同分割槽的順序保證。
|
||||
|
||||
|
||||
|
||||
![](../img/fig11-3.png)
|
||||
![](img/fig11-3.png)
|
||||
|
||||
|
||||
|
||||
@ -448,11 +448,11 @@
|
||||
|
||||
|
||||
|
||||
但是,雙寫有一些嚴重的問題,其中一個是競爭條件,如[圖11-4](../img/fig11-4.png)所示。在這個例子中,兩個客戶端同時想要更新一個專案X:客戶端1想要將值設定為A,客戶端2想要將其設定為B。兩個客戶端首先將新值寫入資料庫,然後將其寫入到搜尋索引。因為運氣不好,這些請求的時序是交錯的:資料庫首先看到來自客戶端1的寫入將值設定為A,然後來自客戶端2的寫入將值設定為B,因此資料庫中的最終值為B。搜尋索引首先看到來自客戶端2的寫入,然後是客戶端1的寫入,所以搜尋索引中的最終值是A。即使沒發生錯誤,這兩個系統現在也永久地不一致了。
|
||||
但是,雙寫有一些嚴重的問題,其中一個是競爭條件,如[圖11-4](img/fig11-4.png)所示。在這個例子中,兩個客戶端同時想要更新一個專案X:客戶端1想要將值設定為A,客戶端2想要將其設定為B。兩個客戶端首先將新值寫入資料庫,然後將其寫入到搜尋索引。因為運氣不好,這些請求的時序是交錯的:資料庫首先看到來自客戶端1的寫入將值設定為A,然後來自客戶端2的寫入將值設定為B,因此資料庫中的最終值為B。搜尋索引首先看到來自客戶端2的寫入,然後是客戶端1的寫入,所以搜尋索引中的最終值是A。即使沒發生錯誤,這兩個系統現在也永久地不一致了。
|
||||
|
||||
|
||||
|
||||
![](../img/fig11-4.png)
|
||||
![](img/fig11-4.png)
|
||||
|
||||
|
||||
|
||||
@ -468,7 +468,7 @@
|
||||
|
||||
|
||||
|
||||
如果你只有一個單領導者複製的資料庫,那麼這個領導者決定了寫入順序,而狀態機複製方法可以在資料庫副本上工作。然而,在[圖11-4](../img/fig11-4.png)中,沒有單個主庫:資料庫可能有一個領導者,搜尋索引也可能有一個領導者,但是兩者都不追隨對方,所以可能會發生衝突(參見“[多領導者複製](ch5.md#多領導者複製)“)。
|
||||
如果你只有一個單領導者複製的資料庫,那麼這個領導者決定了寫入順序,而狀態機複製方法可以在資料庫副本上工作。然而,在[圖11-4](img/fig11-4.png)中,沒有單個主庫:資料庫可能有一個領導者,搜尋索引也可能有一個領導者,但是兩者都不追隨對方,所以可能會發生衝突(參見“[多領導者複製](ch5.md#多領導者複製)“)。
|
||||
|
||||
|
||||
|
||||
@ -492,11 +492,11 @@
|
||||
|
||||
|
||||
|
||||
例如,你可以捕獲資料庫中的變更,並不斷將相同的變更應用至搜尋索引。如果變更日誌以相同的順序應用,則可以預期搜尋索引中的資料與資料庫中的資料是匹配的。搜尋索引和任何其他衍生資料系統只是變更流的消費者,如[圖11-5](../img/fig11-5.png)所示。
|
||||
例如,你可以捕獲資料庫中的變更,並不斷將相同的變更應用至搜尋索引。如果變更日誌以相同的順序應用,則可以預期搜尋索引中的資料與資料庫中的資料是匹配的。搜尋索引和任何其他衍生資料系統只是變更流的消費者,如[圖11-5](img/fig11-5.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig11-5.png)
|
||||
![](img/fig11-5.png)
|
||||
|
||||
|
||||
|
||||
@ -512,7 +512,7 @@
|
||||
|
||||
|
||||
|
||||
從本質上說,變更資料捕獲使得一個數據庫成為領導者(被捕獲變化的資料庫),並將其他元件變為追隨者。基於日誌的訊息代理非常適合從源資料庫傳輸變更事件,因為它保留了訊息的順序(避免了[圖11-2](../img/fig11-2.png)的重新排序問題)。
|
||||
從本質上說,變更資料捕獲使得一個數據庫成為領導者(被捕獲變化的資料庫),並將其他元件變為追隨者。基於日誌的訊息代理非常適合從源資料庫傳輸變更事件,因為它保留了訊息的順序(避免了[圖11-2](img/fig11-2.png)的重新排序問題)。
|
||||
|
||||
|
||||
|
||||
@ -552,7 +552,7 @@
|
||||
|
||||
|
||||
|
||||
我們之前在日誌結構儲存引擎的上下文中討論了“[Hash索引](ch3.md#Hash索引)”中的日誌壓縮(參見[圖3-2](../img/fig3-2.png)的示例)。原理很簡單:儲存引擎定期在日誌中查詢具有相同鍵的記錄,丟掉所有重複的內容,並只保留每個鍵的最新更新。這個壓縮與合併過程在後臺執行。
|
||||
我們之前在日誌結構儲存引擎的上下文中討論了“[Hash索引](ch3.md#Hash索引)”中的日誌壓縮(參見[圖3-2](img/fig3-2.png)的示例)。原理很簡單:儲存引擎定期在日誌中查詢具有相同鍵的記錄,丟掉所有重複的內容,並只保留每個鍵的最新更新。這個壓縮與合併過程在後臺執行。
|
||||
|
||||
|
||||
|
||||
@ -600,7 +600,7 @@
|
||||
|
||||
|
||||
|
||||
* 在變更資料捕獲中,應用以**可變方式(mutable way)** 使用資料庫,任意更新和刪除記錄。變更日誌是從資料庫的底層提取的(例如,透過解析複製日誌),從而確保從資料庫中提取的寫入順序與實際寫入的順序相匹配,從而避免[圖11-4](../img/fig11-4.png)中的競態條件。寫入資料庫的應用不需要知道CDC的存在。
|
||||
* 在變更資料捕獲中,應用以**可變方式(mutable way)** 使用資料庫,任意更新和刪除記錄。變更日誌是從資料庫的底層提取的(例如,透過解析複製日誌),從而確保從資料庫中提取的寫入順序與實際寫入的順序相匹配,從而避免[圖11-4](img/fig11-4.png)中的競態條件。寫入資料庫的應用不需要知道CDC的存在。
|
||||
|
||||
* 在事件溯源中,應用邏輯顯式構建在寫入事件日誌的不可變事件之上。在這種情況下,事件儲存是僅追加寫入的,更新與刪除是不鼓勵的或禁止的。事件被設計為旨在反映應用層面發生的事情,而不是底層的狀態變更。
|
||||
|
||||
@ -668,11 +668,11 @@
|
||||
|
||||
|
||||
|
||||
或者,預訂座位的使用者請求可以拆分為兩個事件:第一個是暫時預約,第二個是驗證預約後的獨立的確認事件(如“[使用全序廣播實現線性一致儲存](ch9.md#使用全序廣播實現線性一致儲存)”中所述) 。這種分割方式允許驗證發生在一個非同步的過程中。
|
||||
或者,預訂座位的使用者請求可以拆分為兩個事件:第一個是暫時預約,第二個是驗證預約後的獨立的確認事件(如“[使用全序廣播實現線性一致的儲存](ch9.md#使用全序廣播實現線性一致的儲存)”中所述) 。這種分割方式允許驗證發生在一個非同步的過程中。
|
||||
|
||||
|
||||
|
||||
#### 狀態,流和不變性
|
||||
### 狀態,流和不變性
|
||||
|
||||
|
||||
|
||||
@ -692,7 +692,7 @@
|
||||
|
||||
|
||||
|
||||
如果你傾向於數學表示,那麼你可能會說,應用狀態是事件流對時間求積分得到的結果,而變更流是狀態對時間求微分的結果,如[圖11-6](../img/fig11-6.png)所示【49,50,51】。這個比喻有一些侷限性(例如,狀態的二階導似乎沒有意義),但這是考慮資料的一個實用出發點。
|
||||
如果你傾向於數學表示,那麼你可能會說,應用狀態是事件流對時間求積分得到的結果,而變更流是狀態對時間求微分的結果,如[圖11-6](img/fig11-6.png)所示【49,50,51】。這個比喻有一些侷限性(例如,狀態的二階導似乎沒有意義),但這是考慮資料的一個實用出發點。
|
||||
|
||||
$$
|
||||
|
||||
@ -702,7 +702,7 @@ stream(t) = \frac{d\ state(t)}{dt}
|
||||
|
||||
$$
|
||||
|
||||
![](../img/fig11-6.png)
|
||||
![](img/fig11-6.png)
|
||||
|
||||
|
||||
|
||||
@ -746,7 +746,7 @@ $$
|
||||
|
||||
|
||||
|
||||
此外,透過從不變的事件日誌中分離出可變的狀態,你可以針對不同的讀取方式,從相同的事件日誌中衍生出幾種不同的表現形式。效果就像一個流的多個消費者一樣([圖11-5](../img/fig11-5.png)):例如,分析型資料庫Druid使用這種方式直接從Kafka攝取資料【55】,Pistachio是一個分散式的鍵值儲存,使用Kafka作為提交日誌【56】,Kafka Connect能將來自Kafka的資料匯出到各種不同的資料庫與索引【41】。這對於許多其他儲存和索引系統(如搜尋伺服器)來說是很有意義的,當系統要從分散式日誌中獲取輸入時亦然(參閱“[保持系統同步](#保持系統同步)”)。
|
||||
此外,透過從不變的事件日誌中分離出可變的狀態,你可以針對不同的讀取方式,從相同的事件日誌中衍生出幾種不同的表現形式。效果就像一個流的多個消費者一樣([圖11-5](img/fig11-5.png)):例如,分析型資料庫Druid使用這種方式直接從Kafka攝取資料【55】,Pistachio是一個分散式的鍵值儲存,使用Kafka作為提交日誌【56】,Kafka Connect能將來自Kafka的資料匯出到各種不同的資料庫與索引【41】。這對於許多其他儲存和索引系統(如搜尋伺服器)來說是很有意義的,當系統要從分散式日誌中獲取輸入時亦然(參閱“[保持系統同步](#保持系統同步)”)。
|
||||
|
||||
|
||||
|
||||
@ -774,7 +774,7 @@ $$
|
||||
|
||||
|
||||
|
||||
一種解決方案是將事件附加到日誌時同步執行讀取檢視的更新。而將這些寫入操作合併為一個原子單元需要**事務**,所以要麼將事件日誌和讀取檢視儲存在同一個儲存系統中,要麼就需要跨不同系統進行分散式事務。或者,你也可以使用在“[使用全序廣播實現線性化儲存](ch9.md#使用全序廣播實現線性化儲存)”中討論的方法。
|
||||
一種解決方案是將事件附加到日誌時同步執行讀取檢視的更新。而將這些寫入操作合併為一個原子單元需要**事務**,所以要麼將事件日誌和讀取檢視儲存在同一個儲存系統中,要麼就需要跨不同系統進行分散式事務。或者,你也可以使用在“[使用全序廣播實現線性一致的儲存](ch9.md#使用全序廣播實現線性一致的儲存)”中討論的方法。
|
||||
|
||||
|
||||
|
||||
@ -826,7 +826,7 @@ $$
|
||||
|
||||
|
||||
|
||||
1. 你可以將事件中的資料寫入資料庫,快取,搜尋索引或類似的儲存系統,然後能被其他客戶端查詢。如[圖11-5](../img/fig11-5.png)所示,這是資料庫與系統其他部分發生變更保持同步的好方法 —— 特別是當流消費者是寫入資料庫的唯一客戶端時。如“[批處理工作流的輸出](ch10.md#批處理工作流的輸出)”中所討論的,它是寫入儲存系統的流等價物。
|
||||
1. 你可以將事件中的資料寫入資料庫,快取,搜尋索引或類似的儲存系統,然後能被其他客戶端查詢。如[圖11-5](img/fig11-5.png)所示,這是資料庫與系統其他部分發生變更保持同步的好方法 —— 特別是當流消費者是寫入資料庫的唯一客戶端時。如“[批處理工作流的輸出](ch10.md#批處理工作流的輸出)”中所討論的,它是寫入儲存系統的流等價物。
|
||||
|
||||
2. 你能以某種方式將事件推送給使用者,例如傳送報警郵件或推送通知,或將事件流式傳輸到可實時顯示的儀表板上。在這種情況下,人是流的最終消費者。
|
||||
|
||||
@ -1016,11 +1016,11 @@ $$
|
||||
|
||||
|
||||
|
||||
將事件時間和處理時間搞混會導致錯誤的資料。例如,假設你有一個流處理器用於測量請求速率(計算每秒請求數)。如果你重新部署流處理器,它可能會停止一分鐘,並在恢復之後處理積壓的事件。如果你按處理時間來衡量速率,那麼在處理積壓日誌時,請求速率看上去就像有一個異常的突發尖峰,而實際上請求速率是穩定的([圖11-7](../img/fig11-7.png))。
|
||||
將事件時間和處理時間搞混會導致錯誤的資料。例如,假設你有一個流處理器用於測量請求速率(計算每秒請求數)。如果你重新部署流處理器,它可能會停止一分鐘,並在恢復之後處理積壓的事件。如果你按處理時間來衡量速率,那麼在處理積壓日誌時,請求速率看上去就像有一個異常的突發尖峰,而實際上請求速率是穩定的([圖11-7](img/fig11-7.png))。
|
||||
|
||||
|
||||
|
||||
![](../img/fig11-7.png)
|
||||
![](img/fig11-7.png)
|
||||
|
||||
|
||||
|
||||
@ -1162,7 +1162,7 @@ $$
|
||||
|
||||
|
||||
|
||||
在“[示例:使用者活動事件分析](ch10.md#示例:使用者活動事件分析)”([圖10-2](../img/fig10-2.png))中,我們看到了連線兩個資料集的批處理作業示例:一組使用者活動事件和一個使用者檔案資料庫。將使用者活動事件視為流,並在流處理器中連續執行相同的連線是很自然的想法:輸入是包含使用者ID的活動事件流,而輸出還是活動事件流,但其中使用者ID已經被擴充套件為使用者的檔案資訊。這個過程有時被稱為 使用資料庫的資訊來**擴充(enriching)** 活動事件。
|
||||
在“[示例:使用者活動事件分析](ch10.md#示例:使用者活動事件分析)”([圖10-2](img/fig10-2.png))中,我們看到了連線兩個資料集的批處理作業示例:一組使用者活動事件和一個使用者檔案資料庫。將使用者活動事件視為流,並在流處理器中連續執行相同的連線是很自然的想法:輸入是包含使用者ID的活動事件流,而輸出還是活動事件流,但其中使用者ID已經被擴充套件為使用者的檔案資訊。這個過程有時被稱為 使用資料庫的資訊來**擴充(enriching)** 活動事件。
|
||||
|
||||
|
||||
|
||||
@ -1232,7 +1232,7 @@ GROUP BY follows.follower_id
|
||||
|
||||
|
||||
|
||||
[^iii]: 如果你將流視作表的衍生物,如[圖11-6](../img/fig11-6.png)所示,而把一個連線看作是兩個表的乘法u·v,那麼會發生一些有趣的事情:物化連線的變化流遵循乘積法則:(u·v)'= u'v + uv'(u·v)'= u'v + uv'。 換句話說,任何推文的變化量都與當前的關注聯絡在一起,任何關注的變化量都與當前的推文相連線【49,50】。
|
||||
[^iii]: 如果你將流視作表的衍生物,如[圖11-6](img/fig11-6.png)所示,而把一個連線看作是兩個表的乘法u·v,那麼會發生一些有趣的事情:物化連線的變化流遵循乘積法則:(u·v)'= u'v + uv'(u·v)'= u'v + uv'。 換句話說,任何推文的變化量都與當前的關注聯絡在一起,任何關注的變化量都與當前的推文相連線【49,50】。
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch12.png)
|
||||
![](img/ch12.png)
|
||||
|
||||
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
|
||||
|
||||
|
||||
允許應用程式直接寫入搜尋索引和資料庫引入瞭如[圖11-4](../img/fig11-4.png)所示的問題,其中兩個客戶端同時傳送衝突的寫入,且兩個儲存系統按不同順序處理它們。在這種情況下,既不是資料庫說了算,也不是搜尋索引說了算,所以它們做出了相反的決定,進入彼此間永續性的不一致狀態。
|
||||
允許應用程式直接寫入搜尋索引和資料庫引入瞭如[圖11-4](img/fig11-4.png)所示的問題,其中兩個客戶端同時傳送衝突的寫入,且兩個儲存系統按不同順序處理它們。在這種情況下,既不是資料庫說了算,也不是搜尋索引說了算,所以它們做出了相反的決定,進入彼此間永續性的不一致狀態。
|
||||
|
||||
|
||||
|
||||
@ -658,11 +658,11 @@
|
||||
|
||||
|
||||
|
||||
在抽象層面,上一節討論的資料流系統提供了建立衍生資料集(例如搜尋索引,物化檢視和預測模型)並使其保持更新的過程。我們將這個過程稱為**寫路徑(write path)**:只要某些資訊被寫入系統,它可能會經歷批處理與流處理的多個階段,而最終每個衍生資料集都會被更新,以適配寫入的資料。[圖12-1](../img/fig12-1.png)顯示了一個更新搜尋索引的例子。
|
||||
在抽象層面,上一節討論的資料流系統提供了建立衍生資料集(例如搜尋索引,物化檢視和預測模型)並使其保持更新的過程。我們將這個過程稱為**寫路徑(write path)**:只要某些資訊被寫入系統,它可能會經歷批處理與流處理的多個階段,而最終每個衍生資料集都會被更新,以適配寫入的資料。[圖12-1](img/fig12-1.png)顯示了一個更新搜尋索引的例子。
|
||||
|
||||
|
||||
|
||||
![](../img/fig12-1.png)
|
||||
![](img/fig12-1.png)
|
||||
|
||||
|
||||
|
||||
@ -678,7 +678,7 @@
|
||||
|
||||
|
||||
|
||||
如[圖12-1](../img/fig12-1.png)所示,衍生資料集是寫路徑和讀路徑相遇的地方。它代表了在寫入時需要完成的工作量與在讀取時需要完成的工作量之間的權衡。
|
||||
如[圖12-1](img/fig12-1.png)所示,衍生資料集是寫路徑和讀路徑相遇的地方。它代表了在寫入時需要完成的工作量與在讀取時需要完成的工作量之間的權衡。
|
||||
|
||||
|
||||
|
||||
@ -910,7 +910,7 @@
|
||||
|
||||
|
||||
|
||||
但是,這種重複抑制僅適用於單條TCP連線的場景中。假設TCP連線是一個客戶端與資料庫的連線,並且它正在執行[例12-1]()中的事務。在許多資料庫中,事務是繫結在客戶端連線上的(如果客戶端傳送了多個查詢,資料庫就知道它們屬於同一個事務,因為它們是在同一個TCP連線上傳送的)。如果客戶端在傳送`COMMIT`之後但在從資料庫伺服器收到響應之前遇到網路中斷與連線超時,客戶端是不知道事務是否已經被提交的([圖8-1](../img/fig8-1.png))。
|
||||
但是,這種重複抑制僅適用於單條TCP連線的場景中。假設TCP連線是一個客戶端與資料庫的連線,並且它正在執行[例12-1]()中的事務。在許多資料庫中,事務是繫結在客戶端連線上的(如果客戶端傳送了多個查詢,資料庫就知道它們屬於同一個事務,因為它們是在同一個TCP連線上傳送的)。如果客戶端在傳送`COMMIT`之後但在從資料庫伺服器收到響應之前遇到網路中斷與連線超時,客戶端是不知道事務是否已經被提交的([圖8-1](img/fig8-1.png))。
|
||||
|
||||
|
||||
|
||||
@ -984,7 +984,7 @@ COMMIT;
|
||||
|
||||
|
||||
|
||||
[例12-2]()依賴於`request_id`列上的唯一約束。如果一個事務嘗試插入一個已經存在的ID,那麼`INSERT`失敗,事務被中止,使其無法生效兩次。即使在較弱的隔離級別下,關係資料庫也能正確地維護唯一性約束(而在“[寫入偏差與幻讀](ch7.md#寫入偏差與幻讀)”中討論過,應用級別的**檢查-然後-插入**可能會在不可序列化的隔離下失敗)。
|
||||
[例12-2]()依賴於`request_id`列上的唯一約束。如果一個事務嘗試插入一個已經存在的ID,那麼`INSERT`失敗,事務被中止,使其無法生效兩次。即使在較弱的隔離級別下,關係資料庫也能正確地維護唯一性約束(而在“[寫入偏斜與幻讀](ch7.md#寫入偏斜與幻讀)”中討論過,應用級別的**檢查-然後-插入**可能會在不可序列化的隔離下失敗)。
|
||||
|
||||
|
||||
|
||||
@ -1108,7 +1108,7 @@ COMMIT;
|
||||
|
||||
|
||||
|
||||
該方法不僅適用於唯一性約束,而且適用於許多其他型別的約束。其基本原理是,任何可能衝突的寫入都會路由到相同的分割槽並按順序處理。正如“[什麼是衝突?](ch5.md#什麼是衝突?)”與“[寫入偏差與幻讀](ch7.md#寫入偏差與幻讀)”中所述,衝突的定義可能取決於應用,但流處理器可以使用任意邏輯來驗證請求。這個想法與Bayou在90年代開創的方法類似【58】。
|
||||
該方法不僅適用於唯一性約束,而且適用於許多其他型別的約束。其基本原理是,任何可能衝突的寫入都會路由到相同的分割槽並按順序處理。正如“[什麼是衝突?](ch5.md#什麼是衝突?)”與“[寫入偏斜與幻讀](ch7.md#寫入偏斜與幻讀)”中所述,衝突的定義可能取決於應用,但流處理器可以使用任意邏輯來驗證請求。這個想法與Bayou在90年代開創的方法類似【58】。
|
||||
|
||||
|
||||
|
||||
|
44
zh-tw/ch2.md
44
zh-tw/ch2.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch2.png)
|
||||
![](img/ch2.png)
|
||||
|
||||
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig2-1.png)
|
||||
![](img/fig2-1.png)
|
||||
|
||||
|
||||
|
||||
@ -140,11 +140,11 @@
|
||||
|
||||
|
||||
|
||||
例如,[圖2-1](../img/fig2-1.png)展示瞭如何在關係模式中表示簡歷(一個LinkedIn簡介)。整個簡介可以透過一個唯一的識別符號`user_id`來標識。像`first_name`和`last_name`這樣的欄位每個使用者只出現一次,所以可以在User表上將其建模為列。但是,大多數人在職業生涯中擁有多於一份的工作,人們可能有不同樣的教育階段和任意數量的聯絡資訊。從使用者到這些專案之間存在一對多的關係,可以用多種方式來表示:
|
||||
例如,[圖2-1](img/fig2-1.png)展示瞭如何在關係模式中表示簡歷(一個LinkedIn簡介)。整個簡介可以透過一個唯一的識別符號`user_id`來標識。像`first_name`和`last_name`這樣的欄位每個使用者只出現一次,所以可以在User表上將其建模為列。但是,大多數人在職業生涯中擁有多於一份的工作,人們可能有不同樣的教育階段和任意數量的聯絡資訊。從使用者到這些專案之間存在一對多的關係,可以用多種方式來表示:
|
||||
|
||||
|
||||
|
||||
* 傳統SQL模型(SQL:1999之前)中,最常見的規範化表示形式是將職位,教育和聯絡資訊放在單獨的表中,對User表提供外來鍵引用,如[圖2-1](../img/fig2-1.png)所示。
|
||||
* 傳統SQL模型(SQL:1999之前)中,最常見的規範化表示形式是將職位,教育和聯絡資訊放在單獨的表中,對User表提供外來鍵引用,如[圖2-1](img/fig2-1.png)所示。
|
||||
|
||||
* 後續的SQL標準增加了對結構化資料型別和XML資料的支援;這允許將多值資料儲存在單行內,並支援在這些文件內查詢和索引。這些功能在Oracle,IBM DB2,MS SQL Server和PostgreSQL中都有不同程度的支援【6,7】。JSON資料型別也得到多個數據庫的支援,包括IBM DB2,MySQL和PostgreSQL 【8】。
|
||||
|
||||
@ -240,15 +240,15 @@
|
||||
|
||||
|
||||
|
||||
JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區域性性(locality)**。如果在前面的關係型示例中獲取簡介,那需要執行多個查詢(透過`user_id`查詢每個表),或者在User表與其下屬表之間混亂地執行多路連線。而在JSON表示中,所有相關資訊都在同一個地方,一個查詢就足夠了。
|
||||
JSON表示比[圖2-1](img/fig2-1.png)中的多表模式具有更好的**區域性性(locality)**。如果在前面的關係型示例中獲取簡介,那需要執行多個查詢(透過`user_id`查詢每個表),或者在User表與其下屬表之間混亂地執行多路連線。而在JSON表示中,所有相關資訊都在同一個地方,一個查詢就足夠了。
|
||||
|
||||
|
||||
|
||||
從使用者簡介檔案到使用者職位,教育歷史和聯絡資訊,這種一對多關係隱含了資料中的一個樹狀結構,而JSON表示使得這個樹狀結構變得明確(見[圖2-2](../img/fig2-2.png))。
|
||||
從使用者簡介檔案到使用者職位,教育歷史和聯絡資訊,這種一對多關係隱含了資料中的一個樹狀結構,而JSON表示使得這個樹狀結構變得明確(見[圖2-2](img/fig2-2.png))。
|
||||
|
||||
|
||||
|
||||
![](../img/fig2-2.png)
|
||||
![](img/fig2-2.png)
|
||||
|
||||
|
||||
|
||||
@ -316,7 +316,7 @@ JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區
|
||||
|
||||
|
||||
|
||||
在前面的描述中,`organization`(使用者工作的公司)和`school_name`(他們學習的地方)只是字串。也許他們應該是對實體的引用呢?然後,每個組織,學校或大學都可以擁有自己的網頁(標識,新聞提要等)。每個簡歷可以連結到它所提到的組織和學校,並且包括他們的圖示和其他資訊(參見[圖2-3](../img/fig2-3.png),來自LinkedIn的一個例子)。
|
||||
在前面的描述中,`organization`(使用者工作的公司)和`school_name`(他們學習的地方)只是字串。也許他們應該是對實體的引用呢?然後,每個組織,學校或大學都可以擁有自己的網頁(標識,新聞提要等)。每個簡歷可以連結到它所提到的組織和學校,並且包括他們的圖示和其他資訊(參見[圖2-3](img/fig2-3.png),來自LinkedIn的一個例子)。
|
||||
|
||||
|
||||
|
||||
@ -326,7 +326,7 @@ JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區
|
||||
|
||||
假設你想新增一個新的功能:一個使用者可以為另一個使用者寫一個推薦。在使用者的簡歷上顯示推薦,並附上推薦使用者的姓名和照片。如果推薦人更新他們的照片,那他們寫的任何推薦都需要顯示新的照片。因此,推薦應該擁有作者個人簡介的引用。
|
||||
|
||||
![](../img/fig2-3.png)
|
||||
![](img/fig2-3.png)
|
||||
|
||||
|
||||
|
||||
@ -334,11 +334,11 @@ JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區
|
||||
|
||||
|
||||
|
||||
[圖2-4](../img/fig2-4.png)闡明瞭這些新功能需要如何使用多對多關係。每個虛線矩形內的資料可以分組成一個文件,但是對單位,學校和其他使用者的引用需要表示成引用,並且在查詢時需要連線。
|
||||
[圖2-4](img/fig2-4.png)闡明瞭這些新功能需要如何使用多對多關係。每個虛線矩形內的資料可以分組成一個文件,但是對單位,學校和其他使用者的引用需要表示成引用,並且在查詢時需要連線。
|
||||
|
||||
|
||||
|
||||
![](../img/fig2-4.png)
|
||||
![](img/fig2-4.png)
|
||||
|
||||
|
||||
|
||||
@ -358,7 +358,7 @@ JSON表示比[圖2-1](../img/fig2-1.png)中的多表模式具有更好的**區
|
||||
|
||||
|
||||
|
||||
IMS的設計中使用了一個相當簡單的資料模型,稱為**層次模型(hierarchical model)**,它與文件資料庫使用的JSON模型有一些驚人的相似之處【2】。它將所有資料表示為巢狀在記錄中的記錄樹,這很像[圖2-2](../img/fig2-2.png)的JSON結構。
|
||||
IMS的設計中使用了一個相當簡單的資料模型,稱為**層次模型(hierarchical model)**,它與文件資料庫使用的JSON模型有一些驚人的相似之處【2】。它將所有資料表示為巢狀在記錄中的記錄樹,這很像[圖2-2](img/fig2-2.png)的JSON結構。
|
||||
|
||||
|
||||
|
||||
@ -430,7 +430,7 @@ CODASYL中的查詢是透過利用遍歷記錄列和跟隨訪問路徑表在資
|
||||
|
||||
|
||||
|
||||
在一個方面,文件資料庫還原為層次模型:在其父記錄中儲存巢狀記錄([圖2-1](../img/fig2-1.png)中的一對多關係,如`positions`,`education`和`contact_info`),而不是在單獨的表中。
|
||||
在一個方面,文件資料庫還原為層次模型:在其父記錄中儲存巢狀記錄([圖2-1](img/fig2-1.png)中的一對多關係,如`positions`,`education`和`contact_info`),而不是在單獨的表中。
|
||||
|
||||
|
||||
|
||||
@ -454,7 +454,7 @@ CODASYL中的查詢是透過利用遍歷記錄列和跟隨訪問路徑表在資
|
||||
|
||||
|
||||
|
||||
如果應用程式中的資料具有類似文件的結構(即,一對多關係樹,通常一次性載入整個樹),那麼使用文件模型可能是一個好主意。將類似文件的結構分解成多個表(如[圖2-1](../img/fig2-1.png)中的`positions`,`education`和`contact_info`)的關係技術可能導致繁瑣的模式和不必要的複雜的應用程式程式碼。
|
||||
如果應用程式中的資料具有類似文件的結構(即,一對多關係樹,通常一次性載入整個樹),那麼使用文件模型可能是一個好主意。將類似文件的結構分解成多個表(如[圖2-1](img/fig2-1.png)中的`positions`,`education`和`contact_info`)的關係技術可能導致繁瑣的模式和不必要的複雜的應用程式程式碼。
|
||||
|
||||
|
||||
|
||||
@ -550,7 +550,7 @@ UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
|
||||
|
||||
|
||||
|
||||
文件通常以單個連續字串形式進行儲存,編碼為JSON,XML或其二進位制變體(如MongoDB的BSON)。如果應用程式經常需要訪問整個文件(例如,將其渲染至網頁),那麼儲存區域性性會帶來效能優勢。如果將資料分割到多個表中(如[圖2-1](../img/fig2-1.png)所示),則需要進行多次索引查詢才能將其全部檢索出來,這可能需要更多的磁碟查詢並花費更多的時間。
|
||||
文件通常以單個連續字串形式進行儲存,編碼為JSON,XML或其二進位制變體(如MongoDB的BSON)。如果應用程式經常需要訪問整個文件(例如,將其渲染至網頁),那麼儲存區域性性會帶來效能優勢。如果將資料分割到多個表中(如[圖2-1](img/fig2-1.png)所示),則需要進行多次索引查詢才能將其全部檢索出來,這可能需要更多的磁碟查詢並花費更多的時間。
|
||||
|
||||
|
||||
|
||||
@ -1068,11 +1068,11 @@ db.observations.aggregate([
|
||||
|
||||
|
||||
|
||||
在本節中,我們將使用[圖2-5](../img/fig2-5.png)所示的示例。它可以從社交網路或系譜資料庫中獲得:它顯示了兩個人,來自愛達荷州的Lucy和來自法國Beaune的Alain。他們已婚,住在倫敦。
|
||||
在本節中,我們將使用[圖2-5](img/fig2-5.png)所示的示例。它可以從社交網路或系譜資料庫中獲得:它顯示了兩個人,來自愛達荷州的Lucy和來自法國Beaune的Alain。他們已婚,住在倫敦。
|
||||
|
||||
|
||||
|
||||
![](../img/fig2-5.png)
|
||||
![](img/fig2-5.png)
|
||||
|
||||
|
||||
|
||||
@ -1174,7 +1174,7 @@ CREATE INDEX edges_heads ON edges (head_vertex);
|
||||
|
||||
|
||||
|
||||
這些特性為資料建模提供了很大的靈活性,如[圖2-5](../img/fig2-5.png)所示。圖中顯示了一些傳統關係模式難以表達的事情,例如不同國家的不同地區結構(法國有省和州,美國有不同的州和州),國中國的怪事(先忽略主權國家和國家錯綜複雜的爛攤子),不同的資料粒度(Lucy現在的住所被指定為一個城市,而她的出生地點只是在一個州的級別)。
|
||||
這些特性為資料建模提供了很大的靈活性,如[圖2-5](img/fig2-5.png)所示。圖中顯示了一些傳統關係模式難以表達的事情,例如不同國家的不同地區結構(法國有省和州,美國有不同的州和州),國中國的怪事(先忽略主權國家和國家錯綜複雜的爛攤子),不同的資料粒度(Lucy現在的住所被指定為一個城市,而她的出生地點只是在一個州的級別)。
|
||||
|
||||
|
||||
|
||||
@ -1190,7 +1190,7 @@ Cypher是屬性圖的宣告式查詢語言,為Neo4j圖形資料庫而發明【
|
||||
|
||||
|
||||
|
||||
[例2-3]()顯示了將[圖2-5](../img/fig2-5.png)的左邊部分插入圖形資料庫的Cypher查詢。可以類似地新增圖的其餘部分,為了便於閱讀而省略。每個頂點都有一個像`USA`或`Idaho`這樣的符號名稱,查詢的其他部分可以使用這些名稱在頂點之間建立邊,使用箭頭符號:`(Idaho) - [:WITHIN] ->(USA)`建立一條標記為`WITHIN`的邊,`Idaho`為尾節點,`USA`為頭節點。
|
||||
[例2-3]()顯示了將[圖2-5](img/fig2-5.png)的左邊部分插入圖形資料庫的Cypher查詢。可以類似地新增圖的其餘部分,為了便於閱讀而省略。每個頂點都有一個像`USA`或`Idaho`這樣的符號名稱,查詢的其他部分可以使用這些名稱在頂點之間建立邊,使用箭頭符號:`(Idaho) - [:WITHIN] ->(USA)`建立一條標記為`WITHIN`的邊,`Idaho`為尾節點,`USA`為頭節點。
|
||||
|
||||
|
||||
|
||||
@ -1218,7 +1218,7 @@ CREATE
|
||||
|
||||
|
||||
|
||||
當[圖2-5](../img/fig2-5.png)的所有頂點和邊被新增到資料庫後,讓我們提些有趣的問題:例如,找到所有從美國移民到歐洲的人的名字。更確切地說,這裡我們想要找到符合下麵條件的所有頂點,並且返回這些頂點的`name`屬性:該頂點擁有一條連到美國任一位置的`BORN_IN`邊,和一條連到歐洲的任一位置的`LIVING_IN`邊。
|
||||
當[圖2-5](img/fig2-5.png)的所有頂點和邊被新增到資料庫後,讓我們提些有趣的問題:例如,找到所有從美國移民到歐洲的人的名字。更確切地說,這裡我們想要找到符合下麵條件的所有頂點,並且返回這些頂點的`name`屬性:該頂點擁有一條連到美國任一位置的`BORN_IN`邊,和一條連到歐洲的任一位置的`LIVING_IN`邊。
|
||||
|
||||
|
||||
|
||||
@ -1794,11 +1794,11 @@ Cypher和SPARQL使用SELECT立即跳轉,但是Datalog一次只進行一小步
|
||||
|
||||
|
||||
|
||||
透過重複應用規則1和2,`within_recursive`謂語可以告訴我們在資料庫中包含北美(或任何其他位置名稱)的所有位置。這個過程如[圖2-6](../img/fig2-6.png)所示。
|
||||
透過重複應用規則1和2,`within_recursive`謂語可以告訴我們在資料庫中包含北美(或任何其他位置名稱)的所有位置。這個過程如[圖2-6](img/fig2-6.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig2-6.png)
|
||||
![](img/fig2-6.png)
|
||||
|
||||
|
||||
|
||||
|
70
zh-tw/ch3.md
70
zh-tw/ch3.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch3.png)
|
||||
![](img/ch3.png)
|
||||
|
||||
|
||||
|
||||
@ -168,11 +168,11 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣。那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到一個數據檔案中的位元組偏移量,指明瞭可以找到對應值的位置,如[圖3-1](../img/fig3-1.png)所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找(seek)** 該位置並讀取該值。
|
||||
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣。那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到一個數據檔案中的位元組偏移量,指明瞭可以找到對應值的位置,如[圖3-1](img/fig3-1.png)所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找(seek)** 該位置並讀取該值。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-1.png)
|
||||
![](img/fig3-1.png)
|
||||
|
||||
|
||||
|
||||
@ -188,11 +188,11 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
直到現在,我們只是追加寫入一個檔案 —— 所以如何避免最終用完磁碟空間?一種好的解決方案是,將日誌分為特定大小的段,當日志增長到特定尺寸時關閉當前段檔案,並開始寫入一個新的段檔案。然後,我們就可以對這些段進行**壓縮(compaction)**,如[圖3-2](../img/fig3-2.png)所示。壓縮意味著在日誌中丟棄重複的鍵,只保留每個鍵的最近更新。
|
||||
直到現在,我們只是追加寫入一個檔案 —— 所以如何避免最終用完磁碟空間?一種好的解決方案是,將日誌分為特定大小的段,當日志增長到特定尺寸時關閉當前段檔案,並開始寫入一個新的段檔案。然後,我們就可以對這些段進行**壓縮(compaction)**,如[圖3-2](img/fig3-2.png)所示。壓縮意味著在日誌中丟棄重複的鍵,只保留每個鍵的最近更新。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-2.png)
|
||||
![](img/fig3-2.png)
|
||||
|
||||
|
||||
|
||||
@ -200,11 +200,11 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
而且,由於壓縮經常會使得段變得很小(假設在一個段內鍵被平均重寫了好幾次),我們也可以在執行壓縮的同時將多個段合併在一起,如[圖3-3](../img/fig3-3.png)所示。段被寫入後永遠不會被修改,所以合併的段被寫入一個新的檔案。凍結段的合併和壓縮可以在後臺執行緒中完成,在進行時,我們仍然可以繼續使用舊的段檔案來正常提供讀寫請求。合併過程完成後,我們將讀取請求轉換為使用新的合併段而不是舊段 —— 然後可以簡單地刪除舊的段檔案。
|
||||
而且,由於壓縮經常會使得段變得很小(假設在一個段內鍵被平均重寫了好幾次),我們也可以在執行壓縮的同時將多個段合併在一起,如[圖3-3](img/fig3-3.png)所示。段被寫入後永遠不會被修改,所以合併的段被寫入一個新的檔案。凍結段的合併和壓縮可以在後臺執行緒中完成,在進行時,我們仍然可以繼續使用舊的段檔案來正常提供讀寫請求。合併過程完成後,我們將讀取請求轉換為使用新的合併段而不是舊段 —— 然後可以簡單地刪除舊的段檔案。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-3.png)
|
||||
![](img/fig3-3.png)
|
||||
|
||||
|
||||
|
||||
@ -298,7 +298,7 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
在[圖3-3](../img/fig3-3.png)中,每個日誌結構儲存段都是一系列鍵值對。這些對按照它們寫入的順序出現,日誌中稍後的值優先於日誌中較早的相同鍵的值。除此之外,檔案中鍵值對的順序並不重要。
|
||||
在[圖3-3](img/fig3-3.png)中,每個日誌結構儲存段都是一系列鍵值對。這些對按照它們寫入的順序出現,日誌中稍後的值優先於日誌中較早的相同鍵的值。除此之外,檔案中鍵值對的順序並不重要。
|
||||
|
||||
|
||||
|
||||
@ -310,15 +310,15 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
1. 合併段是簡單而高效的,即使檔案大於可用記憶體。這種方法就像歸併排序演算法中使用的方法一樣,如[圖3-4](../img/fig3-4.png)所示:您開始並排讀取輸入檔案,檢視每個檔案中的第一個鍵,複製最低鍵(根據排序順序)到輸出檔案,並重復。這產生一個新的合併段檔案,也按鍵排序。
|
||||
1. 合併段是簡單而高效的,即使檔案大於可用記憶體。這種方法就像歸併排序演算法中使用的方法一樣,如[圖3-4](img/fig3-4.png)所示:您開始並排讀取輸入檔案,檢視每個檔案中的第一個鍵,複製最低鍵(根據排序順序)到輸出檔案,並重復。這產生一個新的合併段檔案,也按鍵排序。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-4.png)
|
||||
![](img/fig3-4.png)
|
||||
|
||||
|
||||
|
||||
##### 圖3-4 合併幾個SSTable段,只保留每個鍵的最新值
|
||||
**圖3-4 合併幾個SSTable段,只保留每個鍵的最新值**
|
||||
|
||||
|
||||
|
||||
@ -326,11 +326,11 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
2. 為了在檔案中找到一個特定的鍵,你不再需要儲存記憶體中所有鍵的索引。以[圖3-5](../img/fig3-5.png)為例:假設你正在記憶體中尋找鍵 `handiwork`,但是你不知道段檔案中該關鍵字的確切偏移量。然而,你知道 `handbag` 和 `handsome` 的偏移,而且由於排序特性,你知道 `handiwork` 必須出現在這兩者之間。這意味著您可以跳到 `handbag` 的偏移位置並從那裡掃描,直到您找到 `handiwork`(或沒找到,如果該檔案中沒有該鍵)。
|
||||
2. 為了在檔案中找到一個特定的鍵,你不再需要儲存記憶體中所有鍵的索引。以[圖3-5](img/fig3-5.png)為例:假設你正在記憶體中尋找鍵 `handiwork`,但是你不知道段檔案中該關鍵字的確切偏移量。然而,你知道 `handbag` 和 `handsome` 的偏移,而且由於排序特性,你知道 `handiwork` 必須出現在這兩者之間。這意味著您可以跳到 `handbag` 的偏移位置並從那裡掃描,直到您找到 `handiwork`(或沒找到,如果該檔案中沒有該鍵)。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-5.png)
|
||||
![](img/fig3-5.png)
|
||||
|
||||
|
||||
|
||||
@ -344,7 +344,7 @@ $ cat database
|
||||
|
||||
|
||||
|
||||
3. 由於讀取請求無論如何都需要掃描所請求範圍內的多個鍵值對,因此可以將這些記錄分組到塊中,並在將其寫入磁碟之前對其進行壓縮(如[圖3-5](../img/fig3-5.png)中的陰影區域所示) 。稀疏記憶體中索引的每個條目都指向壓縮塊的開始處。除了節省磁碟空間之外,壓縮還可以減少IO頻寬的使用。
|
||||
3. 由於讀取請求無論如何都需要掃描所請求範圍內的多個鍵值對,因此可以將這些記錄分組到塊中,並在將其寫入磁碟之前對其進行壓縮(如[圖3-5](img/fig3-5.png)中的陰影區域所示) 。稀疏記憶體中索引的每個條目都指向壓縮塊的開始處。除了節省磁碟空間之外,壓縮還可以減少IO頻寬的使用。
|
||||
|
||||
|
||||
|
||||
@ -436,11 +436,11 @@ Lucene是Elasticsearch和Solr使用的一種全文搜尋的索引引擎,它使
|
||||
|
||||
|
||||
|
||||
每個頁面都可以使用地址或位置來標識,這允許一個頁面引用另一個頁面 —— 類似於指標,但在磁碟而不是在記憶體中。我們可以使用這些頁面引用來構建一個頁面樹,如[圖3-6](../img/fig3-6.png)所示。
|
||||
每個頁面都可以使用地址或位置來標識,這允許一個頁面引用另一個頁面 —— 類似於指標,但在磁碟而不是在記憶體中。我們可以使用這些頁面引用來構建一個頁面樹,如[圖3-6](img/fig3-6.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-6.png)
|
||||
![](img/fig3-6.png)
|
||||
|
||||
|
||||
|
||||
@ -452,7 +452,7 @@ Lucene是Elasticsearch和Solr使用的一種全文搜尋的索引引擎,它使
|
||||
|
||||
|
||||
|
||||
在[圖3-6](../img/fig3-6.png)的例子中,我們正在尋找關鍵字 251 ,所以我們知道我們需要遵循邊界 200 和 300 之間的頁面引用。這將我們帶到一個類似的頁面,進一步打破了200 - 300到子範圍。
|
||||
在[圖3-6](img/fig3-6.png)的例子中,我們正在尋找關鍵字 251 ,所以我們知道我們需要遵循邊界 200 和 300 之間的頁面引用。這將我們帶到一個類似的頁面,進一步打破了200 - 300到子範圍。
|
||||
|
||||
|
||||
|
||||
@ -460,11 +460,11 @@ Lucene是Elasticsearch和Solr使用的一種全文搜尋的索引引擎,它使
|
||||
|
||||
|
||||
|
||||
在B樹的一個頁面中對子頁面的引用的數量稱為分支因子。例如,在[圖3-6](../img/fig3-6.png)中,分支因子是 6 。在實踐中,分支因子取決於儲存頁面參考和範圍邊界所需的空間量,但通常是幾百個。
|
||||
在B樹的一個頁面中對子頁面的引用的數量稱為分支因子。例如,在[圖3-6](img/fig3-6.png)中,分支因子是 6 。在實踐中,分支因子取決於儲存頁面參考和範圍邊界所需的空間量,但通常是幾百個。
|
||||
|
||||
|
||||
|
||||
如果要更新B樹中現有鍵的值,則搜尋包含該鍵的葉頁,更改該頁中的值,並將該頁寫回到磁碟(對該頁的任何引用保持有效) 。如果你想新增一個新的鍵,你需要找到其範圍包含新鍵的頁面,並將其新增到該頁面。如果頁面中沒有足夠的可用空間容納新鍵,則將其分成兩個半滿頁面,並更新父頁面以解釋鍵範圍的新分割槽,如[圖3-7](../img/fig3-7.png)所示[^ii]。
|
||||
如果要更新B樹中現有鍵的值,則搜尋包含該鍵的葉頁,更改該頁中的值,並將該頁寫回到磁碟(對該頁的任何引用保持有效) 。如果你想新增一個新的鍵,你需要找到其範圍包含新鍵的頁面,並將其新增到該頁面。如果頁面中沒有足夠的可用空間容納新鍵,則將其分成兩個半滿頁面,並更新父頁面以解釋鍵範圍的新分割槽,如[圖3-7](img/fig3-7.png)所示[^ii]。
|
||||
|
||||
|
||||
|
||||
@ -472,7 +472,7 @@ Lucene是Elasticsearch和Solr使用的一種全文搜尋的索引引擎,它使
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-7.png)
|
||||
![](img/fig3-7.png)
|
||||
|
||||
|
||||
|
||||
@ -600,7 +600,7 @@ B樹在資料庫體系結構中是非常根深蒂固的,為許多工作負載
|
||||
|
||||
|
||||
|
||||
有二級索引也很常見。在關係資料庫中,您可以使用 `CREATE INDEX` 命令在同一個表上建立多個二級索引,而且這些索引通常對於有效地執行聯接而言至關重要。例如,在[第2章](ch2.md)中的[圖2-1](../img/fig2-1.png)中,很可能在 `user_id` 列上有一個二級索引,以便您可以在每個表中找到屬於同一使用者的所有行。
|
||||
有二級索引也很常見。在關係資料庫中,您可以使用 `CREATE INDEX` 命令在同一個表上建立多個二級索引,而且這些索引通常對於有效地執行聯接而言至關重要。例如,在[第2章](ch2.md)中的[圖2-1](img/fig2-1.png)中,很可能在 `user_id` 列上有一個二級索引,以便您可以在每個表中找到屬於同一使用者的所有行。
|
||||
|
||||
|
||||
|
||||
@ -800,11 +800,11 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
||||
|
||||
|
||||
|
||||
相比之下,資料倉庫是一個獨立的資料庫,分析人員可以查詢他們想要的內容而不影響OLTP操作【48】。資料倉庫包含公司各種OLTP系統中所有的只讀資料副本。從OLTP資料庫中提取資料(使用定期的資料轉儲或連續的更新流),轉換成適合分析的模式,清理並載入到資料倉庫中。將資料存入倉庫的過程稱為“**抽取-轉換-載入(ETL)**”,如[圖3-8](../img/fig3-8)所示。
|
||||
相比之下,資料倉庫是一個獨立的資料庫,分析人員可以查詢他們想要的內容而不影響OLTP操作【48】。資料倉庫包含公司各種OLTP系統中所有的只讀資料副本。從OLTP資料庫中提取資料(使用定期的資料轉儲或連續的更新流),轉換成適合分析的模式,清理並載入到資料倉庫中。將資料存入倉庫的過程稱為“**抽取-轉換-載入(ETL)**”,如[圖3-8](img/fig3-8.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-8.png)
|
||||
![](img/fig3-8.png)
|
||||
|
||||
|
||||
|
||||
@ -852,7 +852,7 @@ Teradata,Vertica,SAP HANA和ParAccel等資料倉庫供應商通常使用昂
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-9.png)
|
||||
![](img/fig3-9.png)
|
||||
|
||||
|
||||
|
||||
@ -868,7 +868,7 @@ Teradata,Vertica,SAP HANA和ParAccel等資料倉庫供應商通常使用昂
|
||||
|
||||
|
||||
|
||||
例如,在[圖3-9](../img/fig3-9.md)中,其中一個維度是已售出的產品。 `dim_product` 表中的每一行代表一種待售產品,包括**庫存單位(SKU)**,說明,品牌名稱,類別,脂肪含量,包裝尺寸等。`fact_sales` 表中的每一行都使用外部表明在特定交易中銷售了哪些產品。 (為了簡單起見,如果客戶一次購買幾種不同的產品,則它們在事實表中被表示為單獨的行)。
|
||||
例如,在[圖3-9](img/fig3-9.md)中,其中一個維度是已售出的產品。 `dim_product` 表中的每一行代表一種待售產品,包括**庫存單位(SKU)**,說明,品牌名稱,類別,脂肪含量,包裝尺寸等。`fact_sales` 表中的每一行都使用外部表明在特定交易中銷售了哪些產品。 (為了簡單起見,如果客戶一次購買幾種不同的產品,則它們在事實表中被表示為單獨的行)。
|
||||
|
||||
|
||||
|
||||
@ -942,7 +942,7 @@ GROUP BY
|
||||
|
||||
|
||||
|
||||
在大多數OLTP資料庫中,儲存都是以面向行的方式進行佈局的:表格的一行中的所有值都相鄰儲存。文件資料庫是相似的:整個文件通常儲存為一個連續的位元組序列。你可以在[圖3-1](../img/fig3-1.png)的CSV例子中看到這個。
|
||||
在大多數OLTP資料庫中,儲存都是以面向行的方式進行佈局的:表格的一行中的所有值都相鄰儲存。文件資料庫是相似的:整個文件通常儲存為一個連續的位元組序列。你可以在[圖3-1](img/fig3-1.png)的CSV例子中看到這個。
|
||||
|
||||
|
||||
|
||||
@ -950,11 +950,11 @@ GROUP BY
|
||||
|
||||
|
||||
|
||||
面向列的儲存背後的想法很簡單:不要將所有來自一行的值儲存在一起,而是將來自每一列的所有值儲存在一起。如果每個列儲存在一個單獨的檔案中,查詢只需要讀取和解析查詢中使用的那些列,這可以節省大量的工作。這個原理如[圖3-10](../img/fig3-10.png)所示。
|
||||
面向列的儲存背後的想法很簡單:不要將所有來自一行的值儲存在一起,而是將來自每一列的所有值儲存在一起。如果每個列儲存在一個單獨的檔案中,查詢只需要讀取和解析查詢中使用的那些列,這可以節省大量的工作。這個原理如[圖3-10](img/fig3-10.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-10.png)
|
||||
![](img/fig3-10.png)
|
||||
|
||||
|
||||
|
||||
@ -982,11 +982,11 @@ GROUP BY
|
||||
|
||||
|
||||
|
||||
看看[圖3-10](../img/fig3-10.png)中每一列的值序列:它們通常看起來是相當重複的,這是壓縮的好兆頭。根據列中的資料,可以使用不同的壓縮技術。在資料倉庫中特別有效的一種技術是點陣圖編碼,如[圖3-11](../img/fig3-11.png)所示。
|
||||
看看[圖3-10](img/fig3-10.png)中每一列的值序列:它們通常看起來是相當重複的,這是壓縮的好兆頭。根據列中的資料,可以使用不同的壓縮技術。在資料倉庫中特別有效的一種技術是點陣圖編碼,如[圖3-11](img/fig3-11.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-11.png)
|
||||
![](img/fig3-11.png)
|
||||
|
||||
|
||||
|
||||
@ -1076,11 +1076,11 @@ WHERE product_sk = 31 AND store_sk = 3
|
||||
|
||||
|
||||
|
||||
第二列可以確定第一列中具有相同值的任何行的排序順序。例如,如果 `date_key` 是[圖3-10](../img/fig3-10.png)中的第一個排序關鍵字,那麼 `product_sk` 可能是第二個排序關鍵字,因此同一天的同一產品的所有銷售都將在儲存中組合在一起。這將有助於需要在特定日期範圍內按產品對銷售進行分組或過濾的查詢。
|
||||
第二列可以確定第一列中具有相同值的任何行的排序順序。例如,如果 `date_key` 是[圖3-10](img/fig3-10.png)中的第一個排序關鍵字,那麼 `product_sk` 可能是第二個排序關鍵字,因此同一天的同一產品的所有銷售都將在儲存中組合在一起。這將有助於需要在特定日期範圍內按產品對銷售進行分組或過濾的查詢。
|
||||
|
||||
|
||||
|
||||
排序順序的另一個好處是它可以幫助壓縮列。如果主要排序列沒有多個不同的值,那麼在排序之後,它將具有很長的序列,其中相同的值連續重複多次。一個簡單的遊程編碼(就像我們用於[圖3-11](../img/fig3-11.png)中的點陣圖一樣)可以將該列壓縮到幾千位元組 —— 即使表中有數十億行。
|
||||
排序順序的另一個好處是它可以幫助壓縮列。如果主要排序列沒有多個不同的值,那麼在排序之後,它將具有很長的序列,其中相同的值連續重複多次。一個簡單的遊程編碼(就像我們用於[圖3-11](img/fig3-11.png)中的點陣圖一樣)可以將該列壓縮到幾千位元組 —— 即使表中有數十億行。
|
||||
|
||||
|
||||
|
||||
@ -1140,11 +1140,11 @@ WHERE product_sk = 31 AND store_sk = 3
|
||||
|
||||
|
||||
|
||||
物化檢視的常見特例稱為資料立方體或OLAP立方【64】。它是按不同維度分組的聚合網格。[圖3-12](../img/fig3-12.png)顯示了一個例子。
|
||||
物化檢視的常見特例稱為資料立方體或OLAP立方【64】。它是按不同維度分組的聚合網格。[圖3-12](img/fig3-12.png)顯示了一個例子。
|
||||
|
||||
|
||||
|
||||
![](../img/fig3-12.png)
|
||||
![](img/fig3-12.png)
|
||||
|
||||
|
||||
|
||||
@ -1152,7 +1152,7 @@ WHERE product_sk = 31 AND store_sk = 3
|
||||
|
||||
|
||||
|
||||
想象一下,現在每個事實都只有兩個維度表的外來鍵——在[圖3-12](../img/fig-3-12.png)中,這些是日期和產品。您現在可以繪製一個二維表格,一個軸線上是日期,另一個軸線上是產品。每個單元包含具有該日期 - 產品組合的所有事實的屬性(例如,`net_price`)的聚集(例如,`SUM`)。然後,您可以沿著每行或每列應用相同的彙總,並獲得一個維度減少的彙總(按產品的銷售額,無論日期,還是按日期銷售,無論產品如何)。
|
||||
想象一下,現在每個事實都只有兩個維度表的外來鍵——在[圖3-12](img/fig-3-12.png)中,這些是日期和產品。您現在可以繪製一個二維表格,一個軸線上是日期,另一個軸線上是產品。每個單元包含具有該日期 - 產品組合的所有事實的屬性(例如,`net_price`)的聚集(例如,`SUM`)。然後,您可以沿著每行或每列應用相同的彙總,並獲得一個維度減少的彙總(按產品的銷售額,無論日期,還是按日期銷售,無論產品如何)。
|
||||
|
||||
|
||||
|
||||
|
34
zh-tw/ch4.md
34
zh-tw/ch4.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch4.png)
|
||||
![](img/ch4.png)
|
||||
|
||||
|
||||
|
||||
@ -230,7 +230,7 @@ JSON比XML簡潔,但與二進位制格式相比還是太佔空間。這一事
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-1.png)
|
||||
![](img/fig4-1.png)
|
||||
|
||||
|
||||
|
||||
@ -286,11 +286,11 @@ message Person {
|
||||
|
||||
Thrift和Protocol Buffers每一個都帶有一個程式碼生成工具,它採用了類似於這裡所示的模式定義,並且生成了以各種程式語言實現模式的類【18】。您的應用程式程式碼可以呼叫此生成的程式碼來對模式的記錄進行編碼或解碼。
|
||||
|
||||
用這個模式編碼的資料是什麼樣的?令人困惑的是,Thrift有兩種不同的二進位制編碼格式[^iii],分別稱為BinaryProtocol和CompactProtocol。先來看看BinaryProtocol。使用這種格式的編碼來編碼[例4-1]()中的訊息只需要59個位元組,如[圖4-2](../img/fig4-2.png)所示【19】。
|
||||
用這個模式編碼的資料是什麼樣的?令人困惑的是,Thrift有兩種不同的二進位制編碼格式[^iii],分別稱為BinaryProtocol和CompactProtocol。先來看看BinaryProtocol。使用這種格式的編碼來編碼[例4-1]()中的訊息只需要59個位元組,如[圖4-2](img/fig4-2.png)所示【19】。
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-2.png)
|
||||
![](img/fig4-2.png)
|
||||
|
||||
|
||||
|
||||
@ -306,15 +306,15 @@ Thrift和Protocol Buffers每一個都帶有一個程式碼生成工具,它採
|
||||
|
||||
|
||||
|
||||
與[圖4-1](../img/fig4-1.png)相比,最大的區別是沒有欄位名`(userName, favoriteNumber, interest)`。相反,編碼資料包含欄位標籤,它們是數字`(1, 2和3)`。這些是模式定義中出現的數字。欄位標記就像欄位的別名 - 它們是說我們正在談論的欄位的一種緊湊的方式,而不必拼出欄位名稱。
|
||||
與[圖4-1](img/fig4-1.png)相比,最大的區別是沒有欄位名`(userName, favoriteNumber, interest)`。相反,編碼資料包含欄位標籤,它們是數字`(1, 2和3)`。這些是模式定義中出現的數字。欄位標記就像欄位的別名 - 它們是說我們正在談論的欄位的一種緊湊的方式,而不必拼出欄位名稱。
|
||||
|
||||
|
||||
|
||||
Thrift CompactProtocol編碼在語義上等同於BinaryProtocol,但是如[圖4-3](../img/fig4-3.png)所示,它只將相同的資訊打包成只有34個位元組。它透過將欄位型別和標籤號打包到單個位元組中,並使用可變長度整數來實現。數字1337不是使用全部八個位元組,而是用兩個位元組編碼,每個位元組的最高位用來指示是否還有更多的位元組來。這意味著-64到63之間的數字被編碼為一個位元組,-8192和8191之間的數字以兩個位元組編碼,等等。較大的數字使用更多的位元組。
|
||||
Thrift CompactProtocol編碼在語義上等同於BinaryProtocol,但是如[圖4-3](img/fig4-3.png)所示,它只將相同的資訊打包成只有34個位元組。它透過將欄位型別和標籤號打包到單個位元組中,並使用可變長度整數來實現。數字1337不是使用全部八個位元組,而是用兩個位元組編碼,每個位元組的最高位用來指示是否還有更多的位元組來。這意味著-64到63之間的數字被編碼為一個位元組,-8192和8191之間的數字以兩個位元組編碼,等等。較大的數字使用更多的位元組。
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-3.png)
|
||||
![](img/fig4-3.png)
|
||||
|
||||
|
||||
|
||||
@ -322,11 +322,11 @@ Thrift CompactProtocol編碼在語義上等同於BinaryProtocol,但是如[圖4
|
||||
|
||||
|
||||
|
||||
最後,Protocol Buffers(只有一種二進位制編碼格式)對相同的資料進行編碼,如[圖4-4](../img/fig4-4.png)所示。 它的打包方式稍有不同,但與Thrift的CompactProtocol非常相似。 Protobuf將同樣的記錄塞進了33個位元組中。
|
||||
最後,Protocol Buffers(只有一種二進位制編碼格式)對相同的資料進行編碼,如[圖4-4](img/fig4-4.png)所示。 它的打包方式稍有不同,但與Thrift的CompactProtocol非常相似。 Protobuf將同樣的記錄塞進了33個位元組中。
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-4.png)
|
||||
![](img/fig4-4.png)
|
||||
|
||||
|
||||
|
||||
@ -370,7 +370,7 @@ Thrift CompactProtocol編碼在語義上等同於BinaryProtocol,但是如[圖4
|
||||
|
||||
|
||||
|
||||
Protobuf的一個奇怪的細節是,它沒有列表或陣列資料型別,而是有一個欄位的重複標記(這是除必需和可選之外的第三個選項)。如[圖4-4](../img/fig4-4.png)所示,重複欄位的編碼正如它所說的那樣:同一個欄位標記只是簡單地出現在記錄中。這具有很好的效果,可以將可選(單值)欄位更改為重複(多值)欄位。讀取舊資料的新程式碼會看到一個包含零個或一個元素的列表(取決於該欄位是否存在)。讀取新資料的舊程式碼只能看到列表的最後一個元素。
|
||||
Protobuf的一個奇怪的細節是,它沒有列表或陣列資料型別,而是有一個欄位的重複標記(這是除必需和可選之外的第三個選項)。如[圖4-4](img/fig4-4.png)所示,重複欄位的編碼正如它所說的那樣:同一個欄位標記只是簡單地出現在記錄中。這具有很好的效果,可以將可選(單值)欄位更改為重複(多值)欄位。讀取舊資料的新程式碼會看到一個包含零個或一個元素的列表(取決於該欄位是否存在)。讀取新資料的舊程式碼只能看到列表的最後一個元素。
|
||||
|
||||
|
||||
|
||||
@ -438,7 +438,7 @@ record Person {
|
||||
|
||||
|
||||
|
||||
首先,請注意模式中沒有標籤號碼。 如果我們使用這個模式編碼我們的例子記錄([例4-1]()),Avro二進位制編碼只有32個位元組長,這是我們所見過的所有編碼中最緊湊的。 編碼位元組序列的分解如[圖4-5](../img/fig4-5.png)所示。
|
||||
首先,請注意模式中沒有標籤號碼。 如果我們使用這個模式編碼我們的例子記錄([例4-1]()),Avro二進位制編碼只有32個位元組長,這是我們所見過的所有編碼中最緊湊的。 編碼位元組序列的分解如[圖4-5](img/fig4-5.png)所示。
|
||||
|
||||
|
||||
|
||||
@ -446,7 +446,7 @@ record Person {
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-5.png)
|
||||
![](img/fig4-5.png)
|
||||
|
||||
|
||||
|
||||
@ -474,7 +474,7 @@ record Person {
|
||||
|
||||
|
||||
|
||||
Avro的關鍵思想是Writer模式和Reader模式不必是相同的 - 他們只需要相容。當資料解碼(讀取)時,Avro庫透過並排檢視Writer模式和Reader模式並將資料從Writer模式轉換到Reader模式來解決差異。 Avro規範【20】確切地定義了這種解析的工作原理,如[圖4-6](../img/fig4-6.png)所示。
|
||||
Avro的關鍵思想是Writer模式和Reader模式不必是相同的 - 他們只需要相容。當資料解碼(讀取)時,Avro庫透過並排檢視Writer模式和Reader模式並將資料從Writer模式轉換到Reader模式來解決差異。 Avro規範【20】確切地定義了這種解析的工作原理,如[圖4-6](img/fig4-6.png)所示。
|
||||
|
||||
|
||||
|
||||
@ -482,7 +482,7 @@ Avro的關鍵思想是Writer模式和Reader模式不必是相同的 - 他們只
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-6.png)
|
||||
![](img/fig4-6.png)
|
||||
|
||||
|
||||
|
||||
@ -518,7 +518,7 @@ Avro的關鍵思想是Writer模式和Reader模式不必是相同的 - 他們只
|
||||
|
||||
|
||||
|
||||
##### 但Writer模式到底是什麼?
|
||||
#### 但Writer模式到底是什麼?
|
||||
|
||||
|
||||
|
||||
@ -692,7 +692,7 @@ Avro為靜態型別程式語言提供了可選的程式碼生成功能,但是
|
||||
|
||||
|
||||
|
||||
![](../img/fig4-7.png)
|
||||
![](img/fig4-7.png)
|
||||
|
||||
|
||||
|
||||
@ -964,7 +964,7 @@ RPC方案的前後向相容性屬性從它使用的編碼方式中繼承:
|
||||
|
||||
|
||||
|
||||
如果消費者重新發布訊息到另一個主題,則可能需要小心保留未知欄位,以防止前面在資料庫環境中描述的問題([圖4-7](../img/fig4-7.png))。
|
||||
如果消費者重新發布訊息到另一個主題,則可能需要小心保留未知欄位,以防止前面在資料庫環境中描述的問題([圖4-7](img/fig4-7.png))。
|
||||
|
||||
|
||||
|
||||
|
88
zh-tw/ch5.md
88
zh-tw/ch5.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch5.png)
|
||||
![](img/ch5.png)
|
||||
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-1.png)
|
||||
![](img/fig5-1.png)
|
||||
|
||||
**圖5-1 基於領導者(主-從)的複製**
|
||||
|
||||
@ -96,17 +96,17 @@
|
||||
|
||||
|
||||
|
||||
[圖5-2](../img/fig5-2.png)顯示了系統各個元件之間的通訊:使用者客戶端,主庫和兩個從庫。時間從左到右流動。請求或響應訊息用粗箭頭表示。
|
||||
[圖5-2](img/fig5-2.png)顯示了系統各個元件之間的通訊:使用者客戶端,主庫和兩個從庫。時間從左到右流動。請求或響應訊息用粗箭頭表示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-2.png)
|
||||
![](img/fig5-2.png)
|
||||
|
||||
**圖5-2 基於領導者的複製:一個同步從庫和一個非同步從庫**
|
||||
|
||||
|
||||
|
||||
在[圖5-2]()的示例中,從庫1的複製是同步的:在向用戶報告寫入成功,並使結果對其他使用者可見之前,主庫需要等待從庫1的確認,確保從庫1已經收到寫入操作。以及在使寫入對其他客戶端可見之前接收到寫入。跟隨者2的複製是非同步的:主庫傳送訊息,但不等待從庫的響應。
|
||||
在[圖5-2](img/fig5-2.png)的示例中,從庫1的複製是同步的:在向用戶報告寫入成功,並使結果對其他使用者可見之前,主庫需要等待從庫1的確認,確保從庫1已經收到寫入操作。以及在使寫入對其他客戶端可見之前接收到寫入。跟隨者2的複製是非同步的:主庫傳送訊息,但不等待從庫的響應。
|
||||
|
||||
|
||||
|
||||
@ -412,7 +412,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-3.png)
|
||||
![](img/fig5-3.png)
|
||||
|
||||
|
||||
|
||||
@ -474,11 +474,11 @@
|
||||
|
||||
|
||||
|
||||
如果使用者從不同從庫進行多次讀取,就可能發生這種情況。例如,[圖5-4](../img/fig5-4.png)顯示了使用者2345兩次進行相同的查詢,首先查詢了一個延遲很小的從庫,然後是一個延遲較大的從庫。 (如果使用者重新整理網頁,而每個請求被路由到一個隨機的伺服器,這種情況是很有可能的。)第一個查詢返回最近由使用者1234新增的評論,但是第二個查詢不返回任何東西,因為滯後的從庫還沒有拉取寫入內容。在效果上相比第一個查詢,第二個查詢是在更早的時間點來觀察系統。如果第一個查詢沒有返回任何內容,那問題並不大,因為使用者2345可能不知道使用者1234最近添加了評論。但如果使用者2345先看見使用者1234的評論,然後又看到它消失,那麼對於使用者2345,就很讓人頭大了。
|
||||
如果使用者從不同從庫進行多次讀取,就可能發生這種情況。例如,[圖5-4](img/fig5-4.png)顯示了使用者2345兩次進行相同的查詢,首先查詢了一個延遲很小的從庫,然後是一個延遲較大的從庫。 (如果使用者重新整理網頁,而每個請求被路由到一個隨機的伺服器,這種情況是很有可能的。)第一個查詢返回最近由使用者1234新增的評論,但是第二個查詢不返回任何東西,因為滯後的從庫還沒有拉取寫入內容。在效果上相比第一個查詢,第二個查詢是在更早的時間點來觀察系統。如果第一個查詢沒有返回任何內容,那問題並不大,因為使用者2345可能不知道使用者1234最近添加了評論。但如果使用者2345先看見使用者1234的評論,然後又看到它消失,那麼對於使用者2345,就很讓人頭大了。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-4.png)
|
||||
![](img/fig5-4.png)
|
||||
|
||||
|
||||
|
||||
@ -522,7 +522,7 @@
|
||||
|
||||
|
||||
|
||||
現在,想象第三個人正在透過從庫來聽這個對話。 Cake夫人說的內容是從一個延遲很低的從庫讀取的,但Poons先生所說的內容,從庫的延遲要大的多(見[圖5-5](../img/fig5-5.png))。 於是,這個觀察者會聽到以下內容:
|
||||
現在,想象第三個人正在透過從庫來聽這個對話。 Cake夫人說的內容是從一個延遲很低的從庫讀取的,但Poons先生所說的內容,從庫的延遲要大的多(見[圖5-5](img/fig5-5.png))。 於是,這個觀察者會聽到以下內容:
|
||||
|
||||
|
||||
|
||||
@ -544,7 +544,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-5.png)
|
||||
![](img/fig5-5.png)
|
||||
|
||||
|
||||
|
||||
@ -556,7 +556,7 @@
|
||||
|
||||
|
||||
|
||||
這是**分割槽(partitioned)**(**分片(sharded)**)資料庫中的一個特殊問題,將在第6章中討論。如果資料庫總是以相同的順序應用寫入,則讀取總是會看到一致的字首,所以這種異常不會發生。但是在許多分散式資料庫中,不同的分割槽獨立執行,因此不存在**全域性寫入順序**:當用戶從資料庫中讀取資料時,可能會看到資料庫的某些部分處於較舊的狀態,而某些處於較新的狀態。
|
||||
這是**分割槽(partitioned)**(**分片(sharded)**)資料庫中的一個特殊問題,將在[第6章](ch6.md)中討論。如果資料庫總是以相同的順序應用寫入,則讀取總是會看到一致的字首,所以這種異常不會發生。但是在許多分散式資料庫中,不同的分割槽獨立執行,因此不存在**全域性寫入順序**:當用戶從資料庫中讀取資料時,可能會看到資料庫的某些部分處於較舊的狀態,而某些處於較新的狀態。
|
||||
|
||||
|
||||
|
||||
@ -624,11 +624,11 @@
|
||||
|
||||
|
||||
|
||||
多領導者配置中可以在每個資料中心都有主庫。 [圖5-6](../img/fig5-6.png)展示了這個架構的樣子。 在每個資料中心內使用常規的主從複製;在資料中心之間,每個資料中心的主庫都會將其更改複製到其他資料中心的主庫中。
|
||||
多領導者配置中可以在每個資料中心都有主庫。 [圖5-6](img/fig5-6.png)展示了這個架構的樣子。 在每個資料中心內使用常規的主從複製;在資料中心之間,每個資料中心的主庫都會將其更改複製到其他資料中心的主庫中。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-6.png)
|
||||
![](img/fig5-6.png)
|
||||
|
||||
|
||||
|
||||
@ -668,7 +668,7 @@
|
||||
|
||||
|
||||
|
||||
儘管多主複製有這些優勢,但也有一個很大的缺點:兩個不同的資料中心可能會同時修改相同的資料,寫衝突是必須解決的(如[圖5-6](../img/fig5-6.png)中“衝突解決(conflict resolution)”)。本書將在“[處理寫入衝突](#處理寫入衝突)”中詳細討論這個問題。
|
||||
儘管多主複製有這些優勢,但也有一個很大的缺點:兩個不同的資料中心可能會同時修改相同的資料,寫衝突是必須解決的(如[圖5-6](img/fig5-6.png)中“衝突解決(conflict resolution)”)。本書將在“[處理寫入衝突](#處理寫入衝突)”中詳細討論這個問題。
|
||||
|
||||
|
||||
|
||||
@ -724,11 +724,11 @@
|
||||
|
||||
|
||||
|
||||
例如,考慮一個由兩個使用者同時編輯的維基頁面,如[圖5-7](../img/fig5-7.png)所示。使用者1將頁面的標題從A更改為B,並且使用者2同時將標題從A更改為C。每個使用者的更改已成功應用到其本地主庫。但當非同步複製時,會發現衝突【33】。單主資料庫中不會出現此問題。
|
||||
例如,考慮一個由兩個使用者同時編輯的維基頁面,如[圖5-7](img/fig5-7.png)所示。使用者1將頁面的標題從A更改為B,並且使用者2同時將標題從A更改為C。每個使用者的更改已成功應用到其本地主庫。但當非同步複製時,會發現衝突【33】。單主資料庫中不會出現此問題。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-7.png)
|
||||
![](img/fig5-7.png)
|
||||
|
||||
|
||||
|
||||
@ -772,7 +772,7 @@
|
||||
|
||||
|
||||
|
||||
在多主配置中,沒有明確的寫入順序,所以最終值應該是什麼並不清楚。在[圖5-7](../img/fig5-7.png)中,在主庫1中標題首先更新為B而後更新為C;在主庫2中,首先更新為C,然後更新為B。兩個順序都不是“更正確”的。
|
||||
在多主配置中,沒有明確的寫入順序,所以最終值應該是什麼並不清楚。在[圖5-7](img/fig5-7.png)中,在主庫1中標題首先更新為B而後更新為C;在主庫2中,首先更新為C,然後更新為B。兩個順序都不是“更正確”的。
|
||||
|
||||
|
||||
|
||||
@ -788,7 +788,7 @@
|
||||
|
||||
* 為每個副本分配一個唯一的ID,ID編號更高的寫入具有更高的優先順序。這種方法也意味著資料丟失。
|
||||
|
||||
* 以某種方式將這些值合併在一起 - 例如,按字母順序排序,然後連線它們(在[圖5-7](../img/fig5-7.png)中,合併的標題可能類似於“B/C”)。
|
||||
* 以某種方式將這些值合併在一起 - 例如,按字母順序排序,然後連線它們(在[圖5-7](img/fig5-7.png)中,合併的標題可能類似於“B/C”)。
|
||||
|
||||
* 用一種可保留所有資訊的顯式資料結構來記錄衝突,並編寫解決衝突的應用程式程式碼(也許透過提示使用者的方式)。
|
||||
|
||||
@ -864,7 +864,7 @@
|
||||
|
||||
|
||||
|
||||
有些衝突是顯而易見的。在[圖5-7](../img/fig5-7.png)的例子中,兩個寫操作併發地修改了同一條記錄中的同一個欄位,並將其設定為兩個不同的值。毫無疑問這是一個衝突。
|
||||
有些衝突是顯而易見的。在[圖5-7](img/fig5-7.png)的例子中,兩個寫操作併發地修改了同一條記錄中的同一個欄位,並將其設定為兩個不同的值。毫無疑問這是一個衝突。
|
||||
|
||||
|
||||
|
||||
@ -884,11 +884,11 @@
|
||||
|
||||
|
||||
|
||||
**複製拓撲**(replication topology)描述寫入從一個節點傳播到另一個節點的通訊路徑。如果你有兩個領導者,如[圖5-7](../img/fig5-7.png)所示,只有一個合理的拓撲結構:領導者1必須把他所有的寫到領導者2,反之亦然。當有兩個以上的領導,各種不同的拓撲是可能的。[圖5-8](../img/fig5-8.png)舉例說明了一些例子。
|
||||
**複製拓撲**(replication topology)描述寫入從一個節點傳播到另一個節點的通訊路徑。如果你有兩個領導者,如[圖5-7](img/fig5-7.png)所示,只有一個合理的拓撲結構:領導者1必須把他所有的寫到領導者2,反之亦然。當有兩個以上的領導,各種不同的拓撲是可能的。[圖5-8](img/fig5-8.png)舉例說明了一些例子。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-8.png)
|
||||
![](img/fig5-8.png)
|
||||
|
||||
|
||||
|
||||
@ -896,7 +896,7 @@
|
||||
|
||||
|
||||
|
||||
最普遍的拓撲是全部到全部([圖5-8 (c)](../img/fig5-8.png)),其中每個領導者將其寫入每個其他領導。但是,也會使用更多受限制的拓撲:例如,預設情況下,MySQL僅支援**環形拓撲(circular topology)**【34】,其中每個節點接收來自一個節點的寫入,並將這些寫入(加上自己的任何寫入)轉發給另一個節點。另一種流行的拓撲結構具有星形的形狀[^v]。一個指定的根節點將寫入轉發給所有其他節點。星型拓撲可以推廣到樹。
|
||||
最普遍的拓撲是全部到全部([圖5-8 (c)](img/fig5-8.png)),其中每個領導者將其寫入每個其他領導。但是,也會使用更多受限制的拓撲:例如,預設情況下,MySQL僅支援**環形拓撲(circular topology)**【34】,其中每個節點接收來自一個節點的寫入,並將這些寫入(加上自己的任何寫入)轉發給另一個節點。另一種流行的拓撲結構具有星形的形狀[^v]。一個指定的根節點將寫入轉發給所有其他節點。星型拓撲可以推廣到樹。
|
||||
|
||||
|
||||
|
||||
@ -912,11 +912,11 @@
|
||||
|
||||
|
||||
|
||||
另一方面,全部到全部的拓撲也可能有問題。特別是,一些網路連結可能比其他網路連結更快(例如,由於網路擁塞),結果是一些複製訊息可能“超過”其他複製訊息,如[圖5-9](../img/fig5-9.png)所示。
|
||||
另一方面,全部到全部的拓撲也可能有問題。特別是,一些網路連結可能比其他網路連結更快(例如,由於網路擁塞),結果是一些複製訊息可能“超過”其他複製訊息,如[圖5-9](img/fig5-9.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-9.png)
|
||||
![](img/fig5-9.png)
|
||||
|
||||
|
||||
|
||||
@ -924,7 +924,7 @@
|
||||
|
||||
|
||||
|
||||
在[圖5-9](../img/fig5-9.png)中,客戶端A向主庫1的表中插入一行,客戶端B在主庫3上更新該行。然而,主庫2可以以不同的順序接收寫入:它可以首先接收更新(從它的角度來看,是對資料庫中不存在的行的更新),並且僅在稍後接收到相應的插入(其應該在更新之前)。
|
||||
在[圖5-9](img/fig5-9.png)中,客戶端A向主庫1的表中插入一行,客戶端B在主庫3上更新該行。然而,主庫2可以以不同的順序接收寫入:它可以首先接收更新(從它的角度來看,是對資料庫中不存在的行的更新),並且僅在稍後接收到相應的插入(其應該在更新之前)。
|
||||
|
||||
|
||||
|
||||
@ -972,11 +972,11 @@
|
||||
|
||||
|
||||
|
||||
另一方面,在無領導配置中,故障切換不存在。[圖5-10](../img/fig5-10.png)顯示了發生了什麼事情:客戶端(使用者1234)並行傳送寫入到所有三個副本,並且兩個可用副本接受寫入,但是不可用副本錯過了它。假設三個副本中的兩個承認寫入是足夠的:在使用者1234已經收到兩個確定的響應之後,我們認為寫入成功。客戶簡單地忽略了其中一個副本錯過了寫入的事實。
|
||||
另一方面,在無領導配置中,故障切換不存在。[圖5-10](img/fig5-10.png)顯示了發生了什麼事情:客戶端(使用者1234)並行傳送寫入到所有三個副本,並且兩個可用副本接受寫入,但是不可用副本錯過了它。假設三個副本中的兩個承認寫入是足夠的:在使用者1234已經收到兩個確定的響應之後,我們認為寫入成功。客戶簡單地忽略了其中一個副本錯過了寫入的事實。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-10.png)
|
||||
![](img/fig5-10.png)
|
||||
|
||||
|
||||
|
||||
@ -1008,7 +1008,7 @@
|
||||
|
||||
|
||||
|
||||
當客戶端並行讀取多個節點時,它可以檢測到任何陳舊的響應。例如,在[圖5-10](../img/fig5-10.png)中,使用者2345獲得了來自副本3的版本6值和來自副本1和2的版本7值。客戶端發現副本3具有陳舊值,並將新值寫回到該副本。這種方法適用於讀頻繁的值。
|
||||
當客戶端並行讀取多個節點時,它可以檢測到任何陳舊的響應。例如,在[圖5-10](img/fig5-10.png)中,使用者2345獲得了來自副本3的版本6值和來自副本1和2的版本7值。客戶端發現副本3具有陳舊值,並將新值寫回到該副本。這種方法適用於讀頻繁的值。
|
||||
|
||||
|
||||
|
||||
@ -1028,7 +1028,7 @@
|
||||
|
||||
|
||||
|
||||
在[圖5-10](../img/fig5-10.png)的示例中,我們認為即使僅在三個副本中的兩個上進行處理,寫入仍然是成功的。如果三個副本中只有一個接受了寫入,會怎樣?我們能推多遠呢?
|
||||
在[圖5-10](img/fig5-10.png)的示例中,我們認為即使僅在三個副本中的兩個上進行處理,寫入仍然是成功的。如果三個副本中只有一個接受了寫入,會怎樣?我們能推多遠呢?
|
||||
|
||||
|
||||
|
||||
@ -1064,13 +1064,13 @@
|
||||
|
||||
* 對於$n = 3,w = 2,r = 2$,我們可以容忍一個不可用的節點。
|
||||
|
||||
* 對於$n = 5,w = 3,r = 3$,我們可以容忍兩個不可用的節點。 這個案例如[圖5-11](../img/fig5-11.png)所示。
|
||||
* 對於$n = 5,w = 3,r = 3$,我們可以容忍兩個不可用的節點。 這個案例如[圖5-11](img/fig5-11.png)所示。
|
||||
|
||||
* 通常,讀取和寫入操作始終並行傳送到所有n個副本。 引數w和r決定我們等待多少個節點,即在我們認為讀或寫成功之前,有多少個節點需要報告成功。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-11.png)
|
||||
![](img/fig5-11.png)
|
||||
|
||||
|
||||
|
||||
@ -1090,7 +1090,7 @@
|
||||
|
||||
|
||||
|
||||
如果你有n個副本,並且你選擇w和r,使得$w + r> n$,你通常可以期望每個鍵的讀取都能返回最近寫入的值。情況就是這樣,因為你寫入的節點集合和你讀取的節點集合必須重疊。也就是說,您讀取的節點中必須至少有一個具有最新值的節點(如[圖5-11](../img/fig5-11.png)所示)。
|
||||
如果你有n個副本,並且你選擇w和r,使得$w + r> n$,你通常可以期望每個鍵的讀取都能返回最近寫入的值。情況就是這樣,因為你寫入的節點集合和你讀取的節點集合必須重疊。也就是說,您讀取的節點中必須至少有一個具有最新值的節點(如[圖5-11](img/fig5-11.png)所示)。
|
||||
|
||||
|
||||
|
||||
@ -1218,7 +1218,7 @@
|
||||
|
||||
|
||||
|
||||
問題在於,由於可變的網路延遲和部分故障,事件可能在不同的節點以不同的順序到達。例如,[圖5-12](../img/fig5-12.png)顯示了兩個客戶機A和B同時寫入三節點資料儲存區中的鍵X:
|
||||
問題在於,由於可變的網路延遲和部分故障,事件可能在不同的節點以不同的順序到達。例如,[圖5-12](img/fig5-12.png)顯示了兩個客戶機A和B同時寫入三節點資料儲存區中的鍵X:
|
||||
|
||||
|
||||
|
||||
@ -1230,7 +1230,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-12.png)
|
||||
![](img/fig5-12.png)
|
||||
|
||||
|
||||
|
||||
@ -1238,7 +1238,7 @@
|
||||
|
||||
|
||||
|
||||
如果每個節點只要接收到來自客戶端的寫入請求就簡單地覆蓋了某個鍵的值,那麼節點就會永久地不一致,如[圖5-12](../img/fig5-12.png)中的最終獲取請求所示:節點2認為 X 的最終值是 B,而其他節點認為值是 A 。
|
||||
如果每個節點只要接收到來自客戶端的寫入請求就簡單地覆蓋了某個鍵的值,那麼節點就會永久地不一致,如[圖5-12](img/fig5-12.png)中的最終獲取請求所示:節點2認為 X 的最終值是 B,而其他節點認為值是 A 。
|
||||
|
||||
|
||||
|
||||
@ -1258,7 +1258,7 @@
|
||||
|
||||
|
||||
|
||||
正如 **“最近”** 的引號所表明的,這個想法其實頗具誤導性。在[圖5-12](../img/fig5-12.png)的例子中,當客戶端向資料庫節點發送寫入請求時,客戶端都不知道另一個客戶端,因此不清楚哪一個先發生了。事實上,說“發生”是沒有意義的:我們說寫入是 **併發(concurrent)** 的,所以它們的順序是不確定的。
|
||||
正如 **“最近”** 的引號所表明的,這個想法其實頗具誤導性。在[圖5-12](img/fig5-12.png)的例子中,當客戶端向資料庫節點發送寫入請求時,客戶端都不知道另一個客戶端,因此不清楚哪一個先發生了。事實上,說“發生”是沒有意義的:我們說寫入是 **併發(concurrent)** 的,所以它們的順序是不確定的。
|
||||
|
||||
|
||||
|
||||
@ -1332,7 +1332,7 @@
|
||||
|
||||
|
||||
|
||||
[圖5-13](../img/fig5-13.png)顯示了兩個客戶端同時向同一購物車新增專案。 (如果這樣的例子讓你覺得太麻煩了,那麼可以想象,兩個空中交通管制員同時把飛機新增到他們正在跟蹤的區域)最初,購物車是空的。在它們之間,客戶端向資料庫發出五次寫入:
|
||||
[圖5-13](img/fig5-13.png)顯示了兩個客戶端同時向同一購物車新增專案。 (如果這樣的例子讓你覺得太麻煩了,那麼可以想象,兩個空中交通管制員同時把飛機新增到他們正在跟蹤的區域)最初,購物車是空的。在它們之間,客戶端向資料庫發出五次寫入:
|
||||
|
||||
|
||||
|
||||
@ -1348,7 +1348,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-13.png)
|
||||
![](img/fig5-13.png)
|
||||
|
||||
|
||||
|
||||
@ -1356,11 +1356,11 @@
|
||||
|
||||
|
||||
|
||||
[圖5-13](../img/fig5-13.png)中的操作之間的資料流如[圖5-14](../img/fig5-14.png)所示。 箭頭表示哪個操作發生在其他操作之前,意味著後面的操作知道或依賴於較早的操作。 在這個例子中,客戶端永遠不會完全掌握伺服器上的資料,因為總是有另一個操作同時進行。 但是,舊版本的值最終會被覆蓋,並且不會丟失任何寫入。
|
||||
[圖5-13](img/fig5-13.png)中的操作之間的資料流如[圖5-14](img/fig5-14.png)所示。 箭頭表示哪個操作發生在其他操作之前,意味著後面的操作知道或依賴於較早的操作。 在這個例子中,客戶端永遠不會完全掌握伺服器上的資料,因為總是有另一個操作同時進行。 但是,舊版本的值最終會被覆蓋,並且不會丟失任何寫入。
|
||||
|
||||
|
||||
|
||||
![](../img/fig5-14.png)
|
||||
![](img/fig5-14.png)
|
||||
|
||||
|
||||
|
||||
@ -1398,7 +1398,7 @@
|
||||
|
||||
|
||||
|
||||
以購物車為例,一種合理的合併兄弟方法就是集合求並集。在[圖5-14](../img/fig5-14.png)中,最後的兩個兄弟是[牛奶,麵粉,雞蛋,燻肉]和[雞蛋,牛奶,火腿]。注意牛奶和雞蛋同時出現在兩個兄弟裡,即使他們每個只被寫過一次。合併的值可以是[牛奶,麵粉,雞蛋,培根,火腿],沒有重複。
|
||||
以購物車為例,一種合理的合併兄弟方法就是集合求並集。在[圖5-14](img/fig5-14.png)中,最後的兩個兄弟是[牛奶,麵粉,雞蛋,燻肉]和[雞蛋,牛奶,火腿]。注意牛奶和雞蛋同時出現在兩個兄弟裡,即使他們每個只被寫過一次。合併的值可以是[牛奶,麵粉,雞蛋,培根,火腿],沒有重複。
|
||||
|
||||
|
||||
|
||||
@ -1414,11 +1414,11 @@
|
||||
|
||||
|
||||
|
||||
[圖5-13](../img/fig5-13.png)中的示例只使用一個副本。當有多個副本但沒有領導者時,演算法如何修改?
|
||||
[圖5-13](img/fig5-13.png)中的示例只使用一個副本。當有多個副本但沒有領導者時,演算法如何修改?
|
||||
|
||||
|
||||
|
||||
[圖5-13](../img/fig5-13.png)使用單個版本號來捕獲操作之間的依賴關係,但是當多個副本併發接受寫入時,這是不夠的。相反,除了對每個鍵使用版本號之外,還需要在**每個副本**中使用版本號。每個副本在處理寫入時增加自己的版本號,並且跟蹤從其他副本中看到的版本號。這個資訊指出了要覆蓋哪些值,以及保留哪些值作為兄弟。
|
||||
[圖5-13](img/fig5-13.png)使用單個版本號來捕獲操作之間的依賴關係,但是當多個副本併發接受寫入時,這是不夠的。相反,除了對每個鍵使用版本號之外,還需要在**每個副本**中使用版本號。每個副本在處理寫入時增加自己的版本號,並且跟蹤從其他副本中看到的版本號。這個資訊指出了要覆蓋哪些值,以及保留哪些值作為兄弟。
|
||||
|
||||
|
||||
|
||||
@ -1426,7 +1426,7 @@
|
||||
|
||||
|
||||
|
||||
與[圖5-13](../img/fig5-13.png)中的版本號一樣,當讀取值時,版本向量會從資料庫副本傳送到客戶端,並且隨後寫入值時需要將其傳送回資料庫。(Riak將版本向量編碼為一個字串,它稱為**因果上下文(causal context)**)。版本向量允許資料庫區分覆蓋寫入和併發寫入。
|
||||
與[圖5-13](img/fig5-13.png)中的版本號一樣,當讀取值時,版本向量會從資料庫副本傳送到客戶端,並且隨後寫入值時需要將其傳送回資料庫。(Riak將版本向量編碼為一個字串,它稱為**因果上下文(causal context)**)。版本向量允許資料庫區分覆蓋寫入和併發寫入。
|
||||
|
||||
|
||||
|
||||
|
44
zh-tw/ch6.md
44
zh-tw/ch6.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch6.png)
|
||||
![](img/ch6.png)
|
||||
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
|
||||
|
||||
> ##### 術語澄清
|
||||
> #### 術語澄清
|
||||
|
||||
>
|
||||
|
||||
@ -70,13 +70,13 @@
|
||||
|
||||
|
||||
|
||||
一個節點可能儲存多個分割槽。 如果使用主從複製模型,則分割槽和複製的組合如[圖6-1](../img/fig6-1.png)所示。 每個分割槽領導者(主)被分配給一個節點,追隨者(從)被分配給其他節點。 每個節點可能是某些分割槽的領導者,同時是其他分割槽的追隨者。
|
||||
一個節點可能儲存多個分割槽。 如果使用主從複製模型,則分割槽和複製的組合如[圖6-1](img/fig6-1.png)所示。 每個分割槽領導者(主)被分配給一個節點,追隨者(從)被分配給其他節點。 每個節點可能是某些分割槽的領導者,同時是其他分割槽的追隨者。
|
||||
|
||||
我們在[第5章](ch5.md)討論的關於資料庫複製的所有內容同樣適用於分割槽的複製。 大多數情況下,分割槽方案的選擇與複製方案的選擇是獨立的,為簡單起見,本章中將忽略複製。
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-1.png)
|
||||
![](img/fig6-1.png)
|
||||
|
||||
|
||||
|
||||
@ -112,11 +112,11 @@
|
||||
|
||||
|
||||
|
||||
一種分割槽的方法是為每個分割槽指定一塊連續的鍵範圍(從最小值到最大值),如紙質百科全書的卷([圖6-2]())。如果知道範圍之間的邊界,則可以輕鬆確定哪個分割槽包含某個值。如果您還知道分割槽所在的節點,那麼可以直接向相應的節點發出請求(對於百科全書而言,就像從書架上選取正確的書籍)。
|
||||
一種分割槽的方法是為每個分割槽指定一塊連續的鍵範圍(從最小值到最大值),如紙質百科全書的卷([圖6-2](img/fig6-2.png))。如果知道範圍之間的邊界,則可以輕鬆確定哪個分割槽包含某個值。如果您還知道分割槽所在的節點,那麼可以直接向相應的節點發出請求(對於百科全書而言,就像從書架上選取正確的書籍)。
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-2.png)
|
||||
![](img/fig6-2.png)
|
||||
|
||||
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
|
||||
|
||||
|
||||
鍵的範圍不一定均勻分佈,因為資料也很可能不均勻分佈。例如在[圖6-2](../img/fig6-2.png)中,第1捲包含以A和B開頭的單詞,但第12卷則包含以T,U,V,X,Y和Z開頭的單詞。只是簡單的規定每個捲包含兩個字母會導致一些卷比其他卷大。為了均勻分配資料,分割槽邊界需要依據資料調整。
|
||||
鍵的範圍不一定均勻分佈,因為資料也很可能不均勻分佈。例如在[圖6-2](img/fig6-2.png)中,第1捲包含以A和B開頭的單詞,但第12卷則包含以T,U,V,X,Y和Z開頭的單詞。只是簡單的規定每個捲包含兩個字母會導致一些卷比其他卷大。為了均勻分配資料,分割槽邊界需要依據資料調整。
|
||||
|
||||
|
||||
|
||||
@ -160,11 +160,11 @@
|
||||
|
||||
|
||||
|
||||
一旦你有一個合適的鍵雜湊函式,你可以為每個分割槽分配一個雜湊範圍(而不是鍵的範圍),每個透過雜湊雜湊落在分割槽範圍內的鍵將被儲存在該分割槽中。如[圖6-3](../img/fig6-3.png)所示。
|
||||
一旦你有一個合適的鍵雜湊函式,你可以為每個分割槽分配一個雜湊範圍(而不是鍵的範圍),每個透過雜湊雜湊落在分割槽範圍內的鍵將被儲存在該分割槽中。如[圖6-3](img/fig6-3.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-3.png)
|
||||
![](img/fig6-3.png)
|
||||
|
||||
|
||||
|
||||
@ -252,7 +252,7 @@
|
||||
|
||||
|
||||
|
||||
假設你正在經營一個銷售二手車的網站(如[圖6-4](../img/fig6-4.png)所示)。 每個列表都有一個唯一的ID——稱之為文件ID——並且用文件ID對資料庫進行分割槽(例如,分割槽0中的ID 0到499,分割槽1中的ID 500到999等)。
|
||||
假設你正在經營一個銷售二手車的網站(如[圖6-4](img/fig6-4.png)所示)。 每個列表都有一個唯一的ID——稱之為文件ID——並且用文件ID對資料庫進行分割槽(例如,分割槽0中的ID 0到499,分割槽1中的ID 500到999等)。
|
||||
|
||||
|
||||
|
||||
@ -264,7 +264,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-4.png)
|
||||
![](img/fig6-4.png)
|
||||
|
||||
|
||||
|
||||
@ -276,7 +276,7 @@
|
||||
|
||||
|
||||
|
||||
但是,從文件分割槽索引中讀取需要注意:除非您對文件ID做了特別的處理,否則沒有理由將所有具有特定顏色或特定品牌的汽車放在同一個分割槽中。在[圖6-4](../img/fig6-4.png)中,紅色汽車出現在分割槽0和分割槽1中。因此,如果要搜尋紅色汽車,則需要將查詢傳送到所有分割槽,併合並所有返回的結果。
|
||||
但是,從文件分割槽索引中讀取需要注意:除非您對文件ID做了特別的處理,否則沒有理由將所有具有特定顏色或特定品牌的汽車放在同一個分割槽中。在[圖6-4](img/fig6-4.png)中,紅色汽車出現在分割槽0和分割槽1中。因此,如果要搜尋紅色汽車,則需要將查詢傳送到所有分割槽,併合並所有返回的結果。
|
||||
|
||||
|
||||
|
||||
@ -296,11 +296,11 @@
|
||||
|
||||
|
||||
|
||||
[圖6-5](../img/fig6-5.png)描述了這可能是什麼樣子:來自所有分割槽的紅色汽車在紅色索引中,並且索引是分割槽的,首字母從`a`到`r`的顏色在分割槽0中,`s`到`z`的在分割槽1。汽車製造商的索引也與之類似(分割槽邊界在`f`和`h`之間)。
|
||||
[圖6-5](img/fig6-5.png)描述了這可能是什麼樣子:來自所有分割槽的紅色汽車在紅色索引中,並且索引是分割槽的,首字母從`a`到`r`的顏色在分割槽0中,`s`到`z`的在分割槽1。汽車製造商的索引也與之類似(分割槽邊界在`f`和`h`之間)。
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-5.png)
|
||||
![](img/fig6-5.png)
|
||||
|
||||
|
||||
|
||||
@ -378,7 +378,7 @@
|
||||
|
||||
|
||||
|
||||
我們在前面說過([圖6-3](../img/fig6-3.png)),最好將可能的雜湊分成不同的範圍,並將每個範圍分配給一個分割槽(例如,如果$0≤hash(key)<b_0$,則將鍵分配給分割槽0,如果$b_0 ≤ hash(key) <b_1$,則分配給分割槽1)
|
||||
我們在前面說過([圖6-3](img/fig6-3.png)),最好將可能的雜湊分成不同的範圍,並將每個範圍分配給一個分割槽(例如,如果$0≤hash(key)<b_0$,則將鍵分配給分割槽0,如果$b_0 ≤ hash(key) <b_1$,則分配給分割槽1)
|
||||
|
||||
|
||||
|
||||
@ -402,7 +402,7 @@
|
||||
|
||||
|
||||
|
||||
現在,如果一個節點被新增到叢集中,新節點可以從當前每個節點中**竊取**一些分割槽,直到分割槽再次公平分配。這個過程如[圖6-6](../img/fig6-6.png)所示。如果從叢集中刪除一個節點,則會發生相反的情況。
|
||||
現在,如果一個節點被新增到叢集中,新節點可以從當前每個節點中**竊取**一些分割槽,直到分割槽再次公平分配。這個過程如[圖6-6](img/fig6-6.png)所示。如果從叢集中刪除一個節點,則會發生相反的情況。
|
||||
|
||||
|
||||
|
||||
@ -410,7 +410,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-6.png)
|
||||
![](img/fig6-6.png)
|
||||
|
||||
|
||||
|
||||
@ -530,7 +530,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-7.png)
|
||||
![](img/fig6-7.png)
|
||||
|
||||
|
||||
|
||||
@ -542,11 +542,11 @@
|
||||
|
||||
|
||||
|
||||
許多分散式資料系統都依賴於一個獨立的協調服務,比如ZooKeeper來跟蹤叢集元資料,如[圖6-8](../img/fig6-8.png)所示。 每個節點在ZooKeeper中註冊自己,ZooKeeper維護分割槽到節點的可靠對映。 其他參與者(如路由層或分割槽感知客戶端)可以在ZooKeeper中訂閱此資訊。 只要分割槽分配發生了改變,或者叢集中新增或刪除了一個節點,ZooKeeper就會通知路由層使路由資訊保持最新狀態。
|
||||
許多分散式資料系統都依賴於一個獨立的協調服務,比如ZooKeeper來跟蹤叢集元資料,如[圖6-8](img/fig6-8.png)所示。 每個節點在ZooKeeper中註冊自己,ZooKeeper維護分割槽到節點的可靠對映。 其他參與者(如路由層或分割槽感知客戶端)可以在ZooKeeper中訂閱此資訊。 只要分割槽分配發生了改變,或者叢集中新增或刪除了一個節點,ZooKeeper就會通知路由層使路由資訊保持最新狀態。
|
||||
|
||||
|
||||
|
||||
![](../img/fig6-8.png)
|
||||
![](img/fig6-8.png)
|
||||
|
||||
|
||||
|
||||
@ -554,11 +554,11 @@
|
||||
|
||||
|
||||
|
||||
例如,LinkedIn的Espresso使用Helix 【31】進行叢集管理(依靠ZooKeeper),實現瞭如[圖6-8](../img/fig6-8.png)所示的路由層。 HBase,SolrCloud和Kafka也使用ZooKeeper來跟蹤分割槽分配。 MongoDB具有類似的體系結構,但它依賴於自己的**配置伺服器(config server)** 實現和mongos守護程序作為路由層。
|
||||
例如,LinkedIn的Espresso使用Helix 【31】進行叢集管理(依靠ZooKeeper),實現瞭如[圖6-8](img/fig6-8.png)所示的路由層。 HBase,SolrCloud和Kafka也使用ZooKeeper來跟蹤分割槽分配。 MongoDB具有類似的體系結構,但它依賴於自己的**配置伺服器(config server)** 實現和mongos守護程序作為路由層。
|
||||
|
||||
|
||||
|
||||
Cassandra和Riak採取不同的方法:他們在節點之間使用**流言協議(gossip protocol)** 來傳播群集狀態的變化。請求可以傳送到任意節點,該節點會轉發到包含所請求的分割槽的適當節點([圖6-7](../img/fig6-7.png)中的方法1)。這個模型在資料庫節點中增加了更多的複雜性,但是避免了對像ZooKeeper這樣的外部協調服務的依賴。
|
||||
Cassandra和Riak採取不同的方法:他們在節點之間使用**流言協議(gossip protocol)** 來傳播群集狀態的變化。請求可以傳送到任意節點,該節點會轉發到包含所請求的分割槽的適當節點([圖6-7](img/fig6-7.png)中的方法1)。這個模型在資料庫節點中增加了更多的複雜性,但是避免了對像ZooKeeper這樣的外部協調服務的依賴。
|
||||
|
||||
|
||||
|
||||
|
78
zh-tw/ch7.md
78
zh-tw/ch7.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch7.png)
|
||||
![](img/ch7.png)
|
||||
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ ACID一致性的概念是,**對資料的一組特定約束必須始終成立**
|
||||
|
||||
|
||||
|
||||
[圖7-1](../img/fig7-1.png)是這類問題的一個簡單例子。假設你有兩個客戶端同時在資料庫中增長一個計數器。(假設資料庫沒有內建的自增操作)每個客戶端需要讀取計數器的當前值,加 1 ,再回寫新值。[圖7-1](../img/fig7-1.png) 中,因為發生了兩次增長,計數器應該從42增至44;但由於競態條件,實際上只增至 43 。
|
||||
[圖7-1](img/fig7-1.png)是這類問題的一個簡單例子。假設你有兩個客戶端同時在資料庫中增長一個計數器。(假設資料庫沒有內建的自增操作)每個客戶端需要讀取計數器的當前值,加 1 ,再回寫新值。[圖7-1](img/fig7-1.png) 中,因為發生了兩次增長,計數器應該從42增至44;但由於競態條件,實際上只增至 43 。
|
||||
|
||||
|
||||
|
||||
@ -190,7 +190,7 @@ ACID意義上的隔離性意味著,**同時執行的事務是相互隔離的**
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-1.png)
|
||||
![](img/fig7-1.png)
|
||||
|
||||
|
||||
|
||||
@ -276,7 +276,7 @@ ACID意義上的隔離性意味著,**同時執行的事務是相互隔離的**
|
||||
|
||||
|
||||
|
||||
這些定義假設你想同時修改多個物件(行,文件,記錄)。通常需要**多物件事務(multi-object transaction)** 來保持多塊資料同步。[圖7-2](../img/fig7-2.png)展示了一個來自電郵應用的例子。執行以下查詢來顯示使用者未讀郵件數量:
|
||||
這些定義假設你想同時修改多個物件(行,文件,記錄)。通常需要**多物件事務(multi-object transaction)** 來保持多塊資料同步。[圖7-2](img/fig7-2.png)展示了一個來自電郵應用的例子。執行以下查詢來顯示使用者未讀郵件數量:
|
||||
|
||||
|
||||
|
||||
@ -292,7 +292,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
在[圖7-2](../img/fig7-2.png)中,使用者2 遇到異常情況:郵件列表裡顯示有未讀訊息,但計數器顯示為零未讀訊息,因為計數器增長還沒有發生[^ii]。隔離性可以避免這個問題:透過確保使用者2 要麼同時看到新郵件和增長後的計數器,要麼都看不到。反正不會看到執行到一半的中間結果。
|
||||
在[圖7-2](img/fig7-2.png)中,使用者2 遇到異常情況:郵件列表裡顯示有未讀訊息,但計數器顯示為零未讀訊息,因為計數器增長還沒有發生[^ii]。隔離性可以避免這個問題:透過確保使用者2 要麼同時看到新郵件和增長後的計數器,要麼都看不到。反正不會看到執行到一半的中間結果。
|
||||
|
||||
|
||||
|
||||
@ -300,7 +300,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-2.png)
|
||||
![](img/fig7-2.png)
|
||||
|
||||
|
||||
|
||||
@ -308,11 +308,11 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
[圖7-3](../img/fig7-3.png)說明了對原子性的需求:如果在事務過程中發生錯誤,郵箱和未讀計數器的內容可能會失去同步。在原子事務中,如果對計數器的更新失敗,事務將被中止,並且插入的電子郵件將被回滾。
|
||||
[圖7-3](img/fig7-3.png)說明了對原子性的需求:如果在事務過程中發生錯誤,郵箱和未讀計數器的內容可能會失去同步。在原子事務中,如果對計數器的更新失敗,事務將被中止,並且插入的電子郵件將被回滾。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-3.png)
|
||||
![](img/fig7-3.png)
|
||||
|
||||
|
||||
|
||||
@ -352,7 +352,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
一些資料庫也提供更復雜的原子操作[^iv],例如自增操作,這樣就不再需要像 [圖7-1](../img/fig7-1.png) 那樣的讀取-修改-寫入序列了。同樣流行的是 **[比較和設定(CAS, compare-and-set)](#比較並設定(CAS))** 操作,僅當值沒有被其他併發修改過時,才允許執行寫操作。
|
||||
一些資料庫也提供更復雜的原子操作[^iv],例如自增操作,這樣就不再需要像 [圖7-1](img/fig7-1.png) 那樣的讀取-修改-寫入序列了。同樣流行的是 **[比較和設定(CAS, compare-and-set)](#比較並設定(CAS))** 操作,僅當值沒有被其他併發修改過時,才允許執行寫操作。
|
||||
|
||||
|
||||
|
||||
@ -382,7 +382,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
* 在關係資料模型中,一個表中的行通常具有對另一個表中的行的外來鍵引用。(類似的是,在一個圖資料模型中,一個頂點有著到其他頂點的邊)。多物件事務使你確保這些引用始終有效:當插入幾個相互引用的記錄時,外來鍵必須是正確的和最新的,不然資料就沒有意義。
|
||||
|
||||
* 在文件資料模型中,需要一起更新的欄位通常在同一個文件中,這被視為單個物件——更新單個文件時不需要多物件事務。但是,缺乏連線功能的文件資料庫會鼓勵非規範化(參閱“[關係型資料庫與文件資料庫在今日的對比](ch2.md#關係型資料庫與文件資料庫在今日的對比)”)。當需要更新非規範化的資訊時,如 [圖7-2](../img/fig7-2.png) 所示,需要一次更新多個文件。事務在這種情況下非常有用,可以防止非規範化的資料不同步。
|
||||
* 在文件資料模型中,需要一起更新的欄位通常在同一個文件中,這被視為單個物件——更新單個文件時不需要多物件事務。但是,缺乏連線功能的文件資料庫會鼓勵非規範化(參閱“[關係型資料庫與文件資料庫在今日的對比](ch2.md#關係型資料庫與文件資料庫在今日的對比)”)。當需要更新非規範化的資訊時,如 [圖7-2](img/fig7-2.png) 所示,需要一次更新多個文件。事務在這種情況下非常有用,可以防止非規範化的資料不同步。
|
||||
|
||||
* 在具有二級索引的資料庫中(除了純粹的鍵值儲存以外幾乎都有),每次更改值時都需要更新索引。從事務角度來看,這些索引是不同的資料庫物件:例如,如果沒有事務隔離性,記錄可能出現在一個索引中,但沒有出現在另一個索引中,因為第二個索引的更新還沒有發生。
|
||||
|
||||
@ -490,11 +490,11 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
在**讀已提交**隔離級別執行的事務必須防止髒讀。這意味著事務的任何寫入操作只有在該事務提交時才能被其他人看到(然後所有的寫入操作都會立即變得可見)。如[圖7-4](../img/fig7-4.png)所示,使用者1 設定了`x = 3`,但使用者2 的 `get x `仍舊返回舊值2 (當用戶1 尚未提交時)。
|
||||
在**讀已提交**隔離級別執行的事務必須防止髒讀。這意味著事務的任何寫入操作只有在該事務提交時才能被其他人看到(然後所有的寫入操作都會立即變得可見)。如[圖7-4](img/fig7-4.png)所示,使用者1 設定了`x = 3`,但使用者2 的 `get x `仍舊返回舊值2 (當用戶1 尚未提交時)。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-4.png)
|
||||
![](img/fig7-4.png)
|
||||
|
||||
|
||||
|
||||
@ -506,9 +506,9 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
- 如果事務需要更新多個物件,髒讀取意味著另一個事務可能會只看到一部分更新。例如,在[圖7-2](../img/fig7-2.png)中,使用者看到新的未讀電子郵件,但看不到更新的計數器。這就是電子郵件的髒讀。看到處於部分更新狀態的資料庫會讓使用者感到困惑,並可能導致其他事務做出錯誤的決定。
|
||||
- 如果事務需要更新多個物件,髒讀取意味著另一個事務可能會只看到一部分更新。例如,在[圖7-2](img/fig7-2.png)中,使用者看到新的未讀電子郵件,但看不到更新的計數器。這就是電子郵件的髒讀。看到處於部分更新狀態的資料庫會讓使用者感到困惑,並可能導致其他事務做出錯誤的決定。
|
||||
|
||||
- 如果事務中止,則所有寫入操作都需要回滾(如[圖7-3](../img/fig7-3.png)所示)。如果資料庫允許髒讀,那就意味著一個事務可能會看到稍後需要回滾的資料,即從未實際提交給資料庫的資料。想想後果就讓人頭大。
|
||||
- 如果事務中止,則所有寫入操作都需要回滾(如[圖7-3](img/fig7-3.png)所示)。如果資料庫允許髒讀,那就意味著一個事務可能會看到稍後需要回滾的資料,即從未實際提交給資料庫的資料。想想後果就讓人頭大。
|
||||
|
||||
|
||||
|
||||
@ -528,13 +528,13 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
- 如果事務更新多個物件,髒寫會導致不好的結果。例如,考慮 [圖7-5](../img/fig7-5.png),以一個二手車銷售網站為例,Alice和Bob兩個人同時試圖購買同一輛車。購買汽車需要兩次資料庫寫入:網站上的商品列表需要更新,以反映買家的購買,銷售發票需要傳送給買家。在[圖7-5](../img/fig7-5.png)的情況下,銷售是屬於Bob的(因為他成功更新了商品列表),但發票卻寄送給了愛麗絲(因為她成功更新了發票表)。讀已提交會阻止這樣的事故。
|
||||
- 如果事務更新多個物件,髒寫會導致不好的結果。例如,考慮 [圖7-5](img/fig7-5.png),以一個二手車銷售網站為例,Alice和Bob兩個人同時試圖購買同一輛車。購買汽車需要兩次資料庫寫入:網站上的商品列表需要更新,以反映買家的購買,銷售發票需要傳送給買家。在[圖7-5](img/fig7-5.png)的情況下,銷售是屬於Bob的(因為他成功更新了商品列表),但發票卻寄送給了愛麗絲(因為她成功更新了發票表)。讀已提交會阻止這樣的事故。
|
||||
|
||||
- 但是,讀已提交併不能防止[圖7-1](../img/fig7-1.png)中兩個計數器增量之間的競爭狀態。在這種情況下,第二次寫入發生在第一個事務提交後,所以它不是一個髒寫。這仍然是不正確的,但是出於不同的原因,在“[防止更新丟失](#防止丟失更新)”中將討論如何使這種計數器增量安全。
|
||||
- 但是,讀已提交併不能防止[圖7-1](img/fig7-1.png)中兩個計數器增量之間的競爭狀態。在這種情況下,第二次寫入發生在第一個事務提交後,所以它不是一個髒寫。這仍然是不正確的,但是出於不同的原因,在“[防止更新丟失](#防止丟失更新)”中將討論如何使這種計數器增量安全。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-5.png)
|
||||
![](img/fig7-5.png)
|
||||
|
||||
|
||||
|
||||
@ -562,7 +562,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
出於這個原因,大多數資料庫[^vi]使用[圖7-4](../img/fig7-4.png)的方式防止髒讀:對於寫入的每個物件,資料庫都會記住舊的已提交值,和由當前持有寫入鎖的事務設定的新值。當事務正在進行時,任何其他讀取物件的事務都會拿到舊值。 只有當新值提交後,事務才會切換到讀取新值。
|
||||
出於這個原因,大多數資料庫[^vi]使用[圖7-4](img/fig7-4.png)的方式防止髒讀:對於寫入的每個物件,資料庫都會記住舊的已提交值,和由當前持有寫入鎖的事務設定的新值。當事務正在進行時,任何其他讀取物件的事務都會拿到舊值。 只有當新值提交後,事務才會切換到讀取新值。
|
||||
|
||||
|
||||
|
||||
@ -578,11 +578,11 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
但是在使用此隔離級別時,仍然有很多地方可能會產生併發錯誤。例如[圖7-6](../img/fig7-6.png)說明了讀已提交時可能發生的問題。
|
||||
但是在使用此隔離級別時,仍然有很多地方可能會產生併發錯誤。例如[圖7-6](img/fig7-6.png)說明了讀已提交時可能發生的問題。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-6.png)
|
||||
![](img/fig7-6.png)
|
||||
|
||||
|
||||
|
||||
@ -642,7 +642,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
為了實現快照隔離,資料庫使用了我們看到的用於防止[圖7-4](../img/fig7-4.png)中的髒讀的機制的一般化。資料庫必須可能保留一個物件的幾個不同的提交版本,因為各種正在進行的事務可能需要看到資料庫在不同的時間點的狀態。因為它同時維護著單個物件的多個版本,所以這種技術被稱為**多版本併發控制(MVCC, multi-version concurrency control)**。
|
||||
為了實現快照隔離,資料庫使用了我們看到的用於防止[圖7-4](img/fig7-4.png)中的髒讀的機制的一般化。資料庫必須可能保留一個物件的幾個不同的提交版本,因為各種正在進行的事務可能需要看到資料庫在不同的時間點的狀態。因為它同時維護著單個物件的多個版本,所以這種技術被稱為**多版本併發控制(MVCC, multi-version concurrency control)**。
|
||||
|
||||
|
||||
|
||||
@ -650,7 +650,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
[圖7-7](../img/fig7-7.png)說明了如何在PostgreSQL中實現基於MVCC的快照隔離【31】(其他實現類似)。當一個事務開始時,它被賦予一個唯一的,永遠增長[^vii]的事務ID(`txid`)。每當事務向資料庫寫入任何內容時,它所寫入的資料都會被標記上寫入者的事務ID。
|
||||
[圖7-7](img/fig7-7.png)說明了如何在PostgreSQL中實現基於MVCC的快照隔離【31】(其他實現類似)。當一個事務開始時,它被賦予一個唯一的,永遠增長[^vii]的事務ID(`txid`)。每當事務向資料庫寫入任何內容時,它所寫入的資料都會被標記上寫入者的事務ID。
|
||||
|
||||
|
||||
|
||||
@ -658,7 +658,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-7.png)
|
||||
![](img/fig7-7.png)
|
||||
|
||||
|
||||
|
||||
@ -674,7 +674,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
`UPDATE` 操作在內部翻譯為 `DELETE` 和 `INSERT` 。例如,在[圖7-7](../img/fig7-7.png)中,事務13 從賬戶2 中扣除100美元,將餘額從500美元改為400美元。實際上包含兩條賬戶2 的記錄:餘額為 \$500 的行被標記為**被事務13刪除**,餘額為 \$400 的行**由事務13建立**。
|
||||
`UPDATE` 操作在內部翻譯為 `DELETE` 和 `INSERT` 。例如,在[圖7-7](img/fig7-7.png)中,事務13 從賬戶2 中扣除100美元,將餘額從500美元改為400美元。實際上包含兩條賬戶2 的記錄:餘額為 \$500 的行被標記為**被事務13刪除**,餘額為 \$400 的行**由事務13建立**。
|
||||
|
||||
|
||||
|
||||
@ -696,7 +696,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
這些規則適用於建立和刪除物件。在[圖7-7](../img/fig7-7.png)中,當事務12 從賬戶2 讀取時,它會看到 \$500 的餘額,因為 \$500 餘額的刪除是由事務13 完成的(根據規則3,事務12 看不到事務13 執行的刪除),且400美元記錄的建立也是不可見的(按照相同的規則)。
|
||||
這些規則適用於建立和刪除物件。在[圖7-7](img/fig7-7.png)中,當事務12 從賬戶2 讀取時,它會看到 \$500 的餘額,因為 \$500 餘額的刪除是由事務13 完成的(根據規則3,事務12 看不到事務13 執行的刪除),且400美元記錄的建立也是不可見的(按照相同的規則)。
|
||||
|
||||
|
||||
|
||||
@ -762,7 +762,7 @@ SELECT COUNT(*)FROM emails WHERE recipient_id = 2 AND unread_flag = true
|
||||
|
||||
|
||||
|
||||
併發的寫入事務之間還有其他幾種有趣的衝突。其中最著名的是**丟失更新(lost update)** 問題,如[圖7-1](../img/fig7-1.png)所示,以兩個併發計數器增量為例。
|
||||
併發的寫入事務之間還有其他幾種有趣的衝突。其中最著名的是**丟失更新(lost update)** 問題,如[圖7-1](img/fig7-1.png)所示,以兩個併發計數器增量為例。
|
||||
|
||||
|
||||
|
||||
@ -928,7 +928,7 @@ UPDATE wiki_pages SET content = '新內容'
|
||||
|
||||
|
||||
|
||||
### 寫入偏差與幻讀
|
||||
### 寫入偏斜與幻讀
|
||||
|
||||
|
||||
|
||||
@ -944,11 +944,11 @@ UPDATE wiki_pages SET content = '新內容'
|
||||
|
||||
|
||||
|
||||
現在想象一下,Alice和Bob是兩位值班醫生。兩人都感到不適,所以他們都決定請假。不幸的是,他們恰好在同一時間點選按鈕下班。[圖7-8](../img/fig7-8.png)說明了接下來的事情。
|
||||
現在想象一下,Alice和Bob是兩位值班醫生。兩人都感到不適,所以他們都決定請假。不幸的是,他們恰好在同一時間點選按鈕下班。[圖7-8](img/fig7-8.png)說明了接下來的事情。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-8.png)
|
||||
![](img/fig7-8.png)
|
||||
|
||||
|
||||
|
||||
@ -1244,11 +1244,11 @@ COMMIT;
|
||||
|
||||
|
||||
|
||||
出於這個原因,具有單執行緒序列事務處理的系統不允許互動式的多語句事務。取而代之,應用程式必須提前將整個事務程式碼作為儲存過程提交給資料庫。這些方法之間的差異如[圖7-9](../img/fig7-9.png) 所示。如果事務所需的所有資料都在記憶體中,則儲存過程可以非常快地執行,而不用等待任何網路或磁碟I/O。
|
||||
出於這個原因,具有單執行緒序列事務處理的系統不允許互動式的多語句事務。取而代之,應用程式必須提前將整個事務程式碼作為儲存過程提交給資料庫。這些方法之間的差異如[圖7-9](img/fig7-9.png) 所示。如果事務所需的所有資料都在記憶體中,則儲存過程可以非常快地執行,而不用等待任何網路或磁碟I/O。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-9.png)
|
||||
![](img/fig7-9.png)
|
||||
|
||||
|
||||
|
||||
@ -1360,7 +1360,7 @@ VoltDB還使用儲存過程進行復制:但不是將事務的寫入結果從
|
||||
|
||||
- 如果事務A讀取了一個物件,並且事務B想要寫入該物件,那麼B必須等到A提交或中止才能繼續。 (這確保B不能在A底下意外地改變物件。)
|
||||
|
||||
- 如果事務A寫入了一個物件,並且事務B想要讀取該物件,則B必須等到A提交或中止才能繼續。 (像[圖7-1](../img/fig7-1.png)那樣讀取舊版本的物件在2PL下是不可接受的。)
|
||||
- 如果事務A寫入了一個物件,並且事務B想要讀取該物件,則B必須等到A提交或中止才能繼續。 (像[圖7-1](img/fig7-1.png)那樣讀取舊版本的物件在2PL下是不可接受的。)
|
||||
|
||||
|
||||
|
||||
@ -1546,7 +1546,7 @@ WHERE room_id = 123 AND
|
||||
|
||||
|
||||
|
||||
先前討論了快照隔離中的寫入偏差(參閱“[寫入偏差與幻讀](#寫入偏差與幻讀)”)時,我們觀察到一個迴圈模式:事務從資料庫讀取一些資料,檢查查詢的結果,並根據它看到的結果決定採取一些操作(寫入資料庫)。但是,在快照隔離的情況下,原始查詢的結果在事務提交時可能不再是最新的,因為資料可能在同一時間被修改。
|
||||
先前討論了快照隔離中的寫入偏差(參閱“[寫入偏斜與幻讀](#寫入偏斜與幻讀)”)時,我們觀察到一個迴圈模式:事務從資料庫讀取一些資料,檢查查詢的結果,並根據它看到的結果決定採取一些操作(寫入資料庫)。但是,在快照隔離的情況下,原始查詢的結果在事務提交時可能不再是最新的,因為資料可能在同一時間被修改。
|
||||
|
||||
|
||||
|
||||
@ -1572,11 +1572,11 @@ WHERE room_id = 123 AND
|
||||
|
||||
|
||||
|
||||
回想一下,快照隔離通常是透過多版本併發控制(MVCC;見[圖7-10](../img/fig7-10.png))來實現的。當一個事務從MVCC資料庫中的一致快照讀時,它將忽略取快照時尚未提交的任何其他事務所做的寫入。在[圖7-10](../img/fig7-10.png)中,事務43 認為Alice的 `on_call = true` ,因為事務42(修改Alice的待命狀態)未被提交。然而,在事務43想要提交時,事務42 已經提交。這意味著在讀一致性快照時被忽略的寫入已經生效,事務43 的前提不再為真。
|
||||
回想一下,快照隔離通常是透過多版本併發控制(MVCC;見[圖7-10](img/fig7-10.png))來實現的。當一個事務從MVCC資料庫中的一致快照讀時,它將忽略取快照時尚未提交的任何其他事務所做的寫入。在[圖7-10](img/fig7-10.png)中,事務43 認為Alice的 `on_call = true` ,因為事務42(修改Alice的待命狀態)未被提交。然而,在事務43想要提交時,事務42 已經提交。這意味著在讀一致性快照時被忽略的寫入已經生效,事務43 的前提不再為真。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-10.png)
|
||||
![](img/fig7-10.png)
|
||||
|
||||
|
||||
|
||||
@ -1596,11 +1596,11 @@ WHERE room_id = 123 AND
|
||||
|
||||
|
||||
|
||||
第二種情況要考慮的是另一個事務在讀取資料之後修改資料。這種情況如[圖7-11](../img/fig7-11.png)所示。
|
||||
第二種情況要考慮的是另一個事務在讀取資料之後修改資料。這種情況如[圖7-11](img/fig7-11.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/fig7-11.png)
|
||||
![](img/fig7-11.png)
|
||||
|
||||
|
||||
|
||||
@ -1612,7 +1612,7 @@ WHERE room_id = 123 AND
|
||||
|
||||
|
||||
|
||||
在[圖7-11]()中,事務42 和43 都在班次1234 查詢值班醫生。如果在`shift_id`上有索引,則資料庫可以使用索引項1234 來記錄事務42 和43 讀取這個資料的事實。 (如果沒有索引,這個資訊可以在表級別進行跟蹤)。這個資訊只需要保留一段時間:在一個事務完成(提交或中止),並且所有的併發事務完成之後,資料庫就可以忘記它讀取的資料了。
|
||||
在[圖7-11](img/fig7-11.png)中,事務42 和43 都在班次1234 查詢值班醫生。如果在`shift_id`上有索引,則資料庫可以使用索引項1234 來記錄事務42 和43 讀取這個資料的事實。 (如果沒有索引,這個資訊可以在表級別進行跟蹤)。這個資訊只需要保留一段時間:在一個事務完成(提交或中止),並且所有的併發事務完成之後,資料庫就可以忘記它讀取的資料了。
|
||||
|
||||
|
||||
|
||||
@ -1620,7 +1620,7 @@ WHERE room_id = 123 AND
|
||||
|
||||
|
||||
|
||||
在[圖7-11](../img/fig7-11.png)中,事務43 通知事務42 其先前讀已過時,反之亦然。事務42首先提交併成功,儘管事務43 的寫影響了42 ,但因為事務43 尚未提交,所以寫入尚未生效。然而當事務43 想要提交時,來自事務42 的衝突寫入已經被提交,所以事務43 必須中止。
|
||||
在[圖7-11](img/fig7-11.png)中,事務43 通知事務42 其先前讀已過時,反之亦然。事務42首先提交併成功,儘管事務43 的寫影響了42 ,但因為事務43 尚未提交,所以寫入尚未生效。然而當事務43 想要提交時,來自事務42 的衝突寫入已經被提交,所以事務43 必須中止。
|
||||
|
||||
|
||||
|
||||
|
46
zh-tw/ch8.md
46
zh-tw/ch8.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/ch8.png)
|
||||
![](img/ch8.png)
|
||||
|
||||
|
||||
|
||||
@ -194,7 +194,7 @@
|
||||
|
||||
|
||||
|
||||
網際網路和資料中心(通常是乙太網)中的大多數內部網路都是**非同步分組網路(asynchronous packet networks)**。在這種網路中,一個節點可以向另一個節點發送一個訊息(一個數據包),但是網路不能保證它什麼時候到達,或者是否到達。如果您傳送請求並期待響應,則很多事情可能會出錯(其中一些如[圖8-1](../img/fig8-1.png)所示):
|
||||
網際網路和資料中心(通常是乙太網)中的大多數內部網路都是**非同步分組網路(asynchronous packet networks)**。在這種網路中,一個節點可以向另一個節點發送一個訊息(一個數據包),但是網路不能保證它什麼時候到達,或者是否到達。如果您傳送請求並期待響應,則很多事情可能會出錯(其中一些如[圖8-1](img/fig8-1.png)所示):
|
||||
|
||||
|
||||
|
||||
@ -212,7 +212,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig8-1.png)
|
||||
![](img/fig8-1.png)
|
||||
|
||||
|
||||
|
||||
@ -336,7 +336,7 @@
|
||||
|
||||
|
||||
|
||||
* 如果多個不同的節點同時嘗試將資料包傳送到同一目的地,則網路交換機必須將它們排隊並將它們逐個送入目標網路鏈路(如[圖8-2](../img/fig8-2.png)所示)。在繁忙的網路鏈路上,資料包可能需要等待一段時間才能獲得一個插槽(這稱為網路擁塞)。如果傳入的資料太多,交換機佇列填滿,資料包將被丟棄,因此需要重新發送資料包 - 即使網路執行良好。
|
||||
* 如果多個不同的節點同時嘗試將資料包傳送到同一目的地,則網路交換機必須將它們排隊並將它們逐個送入目標網路鏈路(如[圖8-2](img/fig8-2.png)所示)。在繁忙的網路鏈路上,資料包可能需要等待一段時間才能獲得一個插槽(這稱為網路擁塞)。如果傳入的資料太多,交換機佇列填滿,資料包將被丟棄,因此需要重新發送資料包 - 即使網路執行良好。
|
||||
|
||||
* 當資料包到達目標機器時,如果所有CPU核心當前都處於繁忙狀態,則來自網路的傳入請求將被作業系統排隊,直到應用程式準備好處理它為止。根據機器上的負載,這可能需要一段任意的時間。
|
||||
|
||||
@ -346,7 +346,7 @@
|
||||
|
||||
|
||||
|
||||
![](../img/fig8-2.png)
|
||||
![](img/fig8-2.png)
|
||||
|
||||
|
||||
|
||||
@ -388,7 +388,7 @@
|
||||
|
||||
|
||||
|
||||
### 同步網路 vs 非同步網路
|
||||
### 同步網路與非同步網路
|
||||
|
||||
|
||||
|
||||
@ -630,11 +630,11 @@
|
||||
|
||||
|
||||
|
||||
[圖8-3](../img/fig8-3.png)顯示了在具有多領導者複製的資料庫中對時鐘的危險使用(該例子類似於[圖5-9](../img/fig5-9.png))。 客戶端A在節點1上寫入`x = 1`;寫入被複制到節點3;客戶端B在節點3上增加x(我們現在有`x = 2`);最後這兩個寫入都被複制到節點2。
|
||||
[圖8-3](img/fig8-3.png)顯示了在具有多領導者複製的資料庫中對時鐘的危險使用(該例子類似於[圖5-9](img/fig5-9.png))。 客戶端A在節點1上寫入`x = 1`;寫入被複制到節點3;客戶端B在節點3上增加x(我們現在有`x = 2`);最後這兩個寫入都被複制到節點2。
|
||||
|
||||
|
||||
|
||||
![](../img/fig8-3.png)
|
||||
![](img/fig8-3.png)
|
||||
|
||||
|
||||
|
||||
@ -642,11 +642,11 @@
|
||||
|
||||
|
||||
|
||||
在[圖8-3](../img/fig8-3.png)中,當一個寫入被複制到其他節點時,它會根據發生寫入的節點上的日曆時鐘標記一個時間戳。在這個例子中,時鐘同步是非常好的:節點1和節點3之間的偏差小於3ms,這可能比你在實踐中能預期的更好。
|
||||
在[圖8-3](img/fig8-3.png)中,當一個寫入被複制到其他節點時,它會根據發生寫入的節點上的日曆時鐘標記一個時間戳。在這個例子中,時鐘同步是非常好的:節點1和節點3之間的偏差小於3ms,這可能比你在實踐中能預期的更好。
|
||||
|
||||
|
||||
|
||||
儘管如此,[圖8-3](../img/fig8-3.png)中的時間戳卻無法正確排列事件:寫入`x = 1`的時間戳為42.004秒,但寫入`x = 2`的時間戳為42.003秒,即使`x = 2`在稍後出現。當節點2接收到這兩個事件時,會錯誤地推斷出`x = 1`是最近的值,而丟棄寫入`x = 2`。效果上表現為,客戶端B的增量操作會丟失。
|
||||
儘管如此,[圖8-3](img/fig8-3.png)中的時間戳卻無法正確排列事件:寫入`x = 1`的時間戳為42.004秒,但寫入`x = 2`的時間戳為42.003秒,即使`x = 2`在稍後出現。當節點2接收到這兩個事件時,會錯誤地推斷出`x = 1`是最近的值,而丟棄寫入`x = 2`。效果上表現為,客戶端B的增量操作會丟失。
|
||||
|
||||
|
||||
|
||||
@ -656,7 +656,7 @@
|
||||
|
||||
* 資料庫寫入可能會神祕地消失:具有滯後時鐘的節點無法覆蓋之前具有快速時鐘的節點寫入的值,直到節點之間的時鐘偏差消逝【54,55】。此方案可能導致一定數量的資料被悄悄丟棄,而未嚮應用報告任何錯誤。
|
||||
|
||||
* LWW無法區分**高頻順序寫入**(在[圖8-3](../img/fig8-3.png)中,客戶端B的增量操作**一定**發生在客戶端A的寫入之後)和**真正併發寫入**(寫入者意識不到其他寫入者)。需要額外的因果關係跟蹤機制(例如版本向量),以防止違背因果關係(請參閱“[檢測併發寫入](ch5.md#檢測併發寫入)”)。
|
||||
* LWW無法區分**高頻順序寫入**(在[圖8-3](img/fig8-3.png)中,客戶端B的增量操作**一定**發生在客戶端A的寫入之後)和**真正併發寫入**(寫入者意識不到其他寫入者)。需要額外的因果關係跟蹤機制(例如版本向量),以防止違背因果關係(請參閱“[檢測併發寫入](ch5.md#檢測併發寫入)”)。
|
||||
|
||||
* 兩個節點很可能獨立地生成具有相同時間戳的寫入,特別是在時鐘僅具有毫秒解析度的情況下。為了解決這樣的衝突,還需要一個額外的**決勝值(tiebreaker)**(可以簡單地是一個大隨機數),但這種方法也可能會導致違背因果關係【53】。
|
||||
|
||||
@ -962,11 +962,11 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
例如,[圖8-4](../img/fig8-4.png)顯示了由於不正確的鎖實現導致的資料損壞錯誤。 (這個錯誤不僅僅是理論上的:HBase曾經有這個問題【74,75】)假設你要確保一個儲存服務中的檔案一次只能被一個客戶訪問,因為如果多個客戶試圖對此寫入,該檔案將被損壞。您嘗試透過在訪問檔案之前要求客戶端從鎖定服務獲取租約來實現此目的。
|
||||
例如,[圖8-4](img/fig8-4.png)顯示了由於不正確的鎖實現導致的資料損壞錯誤。 (這個錯誤不僅僅是理論上的:HBase曾經有這個問題【74,75】)假設你要確保一個儲存服務中的檔案一次只能被一個客戶訪問,因為如果多個客戶試圖對此寫入,該檔案將被損壞。您嘗試透過在訪問檔案之前要求客戶端從鎖定服務獲取租約來實現此目的。
|
||||
|
||||
|
||||
|
||||
![](../img/fig8-4.png)
|
||||
![](img/fig8-4.png)
|
||||
|
||||
|
||||
|
||||
@ -982,11 +982,11 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
當使用鎖或租約來保護對某些資源(如[圖8-4](../img/fig8-4.png)中的檔案儲存)的訪問時,需要確保一個被誤認為自己是“天選者”的節點不能擾亂系統的其它部分。實現這一目標的一個相當簡單的技術就是**防護(fencing)**,如[圖8-5](../img/fig8-5.png)所示
|
||||
當使用鎖或租約來保護對某些資源(如[圖8-4](img/fig8-4.png)中的檔案儲存)的訪問時,需要確保一個被誤認為自己是“天選者”的節點不能擾亂系統的其它部分。實現這一目標的一個相當簡單的技術就是**防護(fencing)**,如[圖8-5](img/fig8-5.png)所示
|
||||
|
||||
|
||||
|
||||
![](../img/fig8-5.png)
|
||||
![](img/fig8-5.png)
|
||||
|
||||
|
||||
|
||||
@ -998,7 +998,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
在[圖8-5](../img/fig8-5.png)中,客戶端1以33的令牌獲得租約,但隨後進入一個長時間的停頓並且租約到期。客戶端2以34的令牌(該數字總是增加)獲取租約,然後將其寫入請求傳送到儲存服務,包括34的令牌。稍後,客戶端1恢復生機並將其寫入儲存服務,包括其令牌值33。但是,儲存伺服器會記住它已經處理了一個具有更高令牌編號(34)的寫入,因此它會拒絕帶有令牌33的請求。
|
||||
在[圖8-5](img/fig8-5.png)中,客戶端1以33的令牌獲得租約,但隨後進入一個長時間的停頓並且租約到期。客戶端2以34的令牌(該數字總是增加)獲取租約,然後將其寫入請求傳送到儲存服務,包括34的令牌。稍後,客戶端1恢復生機並將其寫入儲存服務,包括其令牌值33。但是,儲存伺服器會記住它已經處理了一個具有更高令牌編號(34)的寫入,因此它會拒絕帶有令牌33的請求。
|
||||
|
||||
|
||||
|
||||
@ -1174,7 +1174,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
***唯一性***
|
||||
***唯一性(uniqueness)***
|
||||
|
||||
|
||||
|
||||
@ -1182,7 +1182,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
***單調序列***
|
||||
***單調序列(monotonic sequence)***
|
||||
|
||||
|
||||
|
||||
@ -1190,7 +1190,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
***可用性***
|
||||
***可用性(availability)***
|
||||
|
||||
|
||||
|
||||
@ -1206,7 +1206,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
為了澄清這種情況,有必要區分兩種不同的性質:**安全性(safety)**和**活性(liveness)**。在剛剛給出的例子中,**唯一性(uniqueness)**和**單調序列(monotonic sequence)**是安全屬性,但**可用性**是**活性(liveness)**屬性。
|
||||
為了澄清這種情況,有必要區分兩種不同的屬性:**安全(safety)屬性**和**活性(liveness)屬性**。在剛剛給出的例子中,**唯一性**和**單調序列**是安全屬性,而**可用性**是活性屬性。
|
||||
|
||||
|
||||
|
||||
@ -1214,7 +1214,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
安全性通常被非正式地定義為,**沒有壞事發生**,而活性通常就類似:**最終好事發生**。但是,最好不要過多地閱讀那些非正式的定義,因為好與壞的含義是主觀的。安全性和活性的實際定義是精確的和數學的【90】:
|
||||
安全通常被非正式地定義為:**沒有壞事發生**,而活性通常就類似:**最終好事發生**。但是,最好不要過多地閱讀那些非正式的定義,因為好與壞的含義是主觀的。安全和活性的實際定義是精確的和數學的【90】:
|
||||
|
||||
|
||||
|
||||
@ -1224,7 +1224,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
區分安全性和活性屬性的一個優點是可以幫助我們處理困難的系統模型。對於分散式演算法,在系統模型的所有可能情況下,要求**始終**保持安全屬性是常見的【88】。也就是說,即使所有節點崩潰,或者整個網路出現故障,演算法仍然必須確保它不會返回錯誤的結果(即保證安全性得到滿足)。
|
||||
區分安全屬性和活性屬性的一個優點是可以幫助我們處理困難的系統模型。對於分散式演算法,在系統模型的所有可能情況下,要求**始終**保持安全屬性是常見的【88】。也就是說,即使所有節點崩潰,或者整個網路出現故障,演算法仍然必須確保它不會返回錯誤的結果(即保證安全屬性得到滿足)。
|
||||
|
||||
|
||||
|
||||
@ -1236,7 +1236,7 @@ while (true) {
|
||||
|
||||
|
||||
|
||||
安全性和活性屬性以及系統模型對於推理分散式演算法的正確性非常有用。然而,在實踐中實施演算法時,現實的混亂事實再一次地讓你咬牙切齒,很明顯系統模型是對現實的簡化抽象。
|
||||
安全屬性和活性屬性以及系統模型對於推理分散式演算法的正確性非常有用。然而,在實踐中實施演算法時,現實的混亂事實再一次地讓你咬牙切齒,很明顯系統模型是對現實的簡化抽象。
|
||||
|
||||
|
||||
|
||||
|
332
zh-tw/ch9.md
332
zh-tw/ch9.md
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
不等待某些事情完成(例如,將資料傳送到網路中的另一個節點),並且不會假設要花多長時間。請參閱第153頁上的“同步與非同步複製”,第284頁上的“同步與非同步網路”,以及第306頁上的“系統模型與現實”。
|
||||
不等待某些事情完成(例如,將資料傳送到網路中的另一個節點),並且不會假設要花多長時間。請參閱[同步複製與非同步複製](ch5.md#同步複製與非同步複製)”,“[同步網路與非同步網路](ch8.md#同步網路與非同步網路)”,以及“[系統模型與現實](ch8.md#系統模型與現實)”。
|
||||
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
|
||||
|
||||
|
||||
2.在事務的上下文中:將一些寫入操作分為一組,這組寫入要麼全部提交成功,要麼遇到錯誤時全部回滾。參見第223頁的“原子性”和第354頁的“原子提交和兩階段提交(2PC)”。
|
||||
2.在事務的上下文中:將一些寫入操作分為一組,這組寫入要麼全部提交成功,要麼遇到錯誤時全部回滾。參見“[原子性(Atomicity)](ch7.md#原子性(Atomicity))”和“[原子提交與二階段提交(2PC)](ch9.md#原子提交與二階段提交(2PC))”。
|
||||
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
|
||||
|
||||
接收方接收資料速度較慢時,強制降低傳送方的資料傳送速度。也稱為流量控制。請參閱第441頁上的“訊息系統”。
|
||||
接收方接收資料速度較慢時,強制降低傳送方的資料傳送速度。也稱為流量控制。請參閱“[訊息系統](ch11.md#訊息系統)”。
|
||||
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
|
||||
|
||||
|
||||
一種計算,它將一些固定的(通常是大的)資料集作為輸入,並將其他一些資料作為輸出,而不修改輸入。見第十章。
|
||||
一種計算,它將一些固定的(通常是大的)資料集作為輸入,並將其他一些資料作為輸出,而不修改輸入。見[第十章](ch10.md)。
|
||||
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@
|
||||
|
||||
|
||||
|
||||
有一些已知的上限或大小。例如,網路延遲情況(請參閱“超時和未定義的延遲”在本頁281)和資料集(請參閱第11章的介紹)。
|
||||
有一些已知的上限或大小。例如,網路延遲情況(請參閱“[超時與無窮的延遲](ch8.md#超時與無窮的延遲)”)和資料集(請參閱[第11章](ch11.md)的介紹)。
|
||||
|
||||
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
|
||||
|
||||
|
||||
表現異常的節點,這種異常可能以任意方式出現,例如向其他節點發送矛盾或惡意訊息。請參閱第304頁上的“拜占庭故障”。
|
||||
表現異常的節點,這種異常可能以任意方式出現,例如向其他節點發送矛盾或惡意訊息。請參閱“[拜占庭故障](ch8.md#拜占庭故障)”。
|
||||
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
|
||||
|
||||
|
||||
一個被廣泛誤解的理論結果,在實踐中是沒有用的。參見第336頁的“CAP定理”。
|
||||
一個被廣泛誤解的理論結果,在實踐中是沒有用的。參見“[CAP定理](ch9.md#CAP定理)”。
|
||||
|
||||
|
||||
|
||||
@ -122,7 +122,7 @@
|
||||
|
||||
|
||||
|
||||
事件之間的依賴關係,當一件事發生在另一件事情之前。例如,後面的事件是對早期事件的迴應,或者依賴於更早的事件,或者應該根據先前的事件來理解。請參閱第186頁上的“發生之前的關係和併發性”和第339頁上的“排序和因果關係”。
|
||||
事件之間的依賴關係,當一件事發生在另一件事情之前。例如,後面的事件是對早期事件的迴應,或者依賴於更早的事件,或者應該根據先前的事件來理解。請參閱“[“此前發生”的關係和併發](ch5.md#“此前發生”的關係和併發)”和“[順序與因果關係](ch5.md#順序與因果關係)”。
|
||||
|
||||
|
||||
|
||||
@ -134,7 +134,7 @@
|
||||
|
||||
|
||||
|
||||
分散式計算的一個基本問題,就是讓幾個節點同意某些事情(例如,哪個節點應該是資料庫叢集的領導者)。問題比乍看起來要困難得多。請參閱第364頁上的“容錯共識”。
|
||||
分散式計算的一個基本問題,就是讓幾個節點同意某些事情(例如,哪個節點應該是資料庫叢集的領導者)。問題比乍看起來要困難得多。請參閱“[容錯共識](ch9.md#容錯共識)”。
|
||||
|
||||
|
||||
|
||||
@ -146,7 +146,7 @@
|
||||
|
||||
|
||||
|
||||
一個數據庫,其中來自幾個不同的OLTP系統的資料已經被合併和準備用於分析目的。請參閱第91頁上的“資料倉庫”。
|
||||
一個數據庫,其中來自幾個不同的OLTP系統的資料已經被合併和準備用於分析目的。請參閱“[資料倉庫](ch3.md#資料倉庫)”。
|
||||
|
||||
|
||||
|
||||
@ -158,7 +158,7 @@
|
||||
|
||||
|
||||
|
||||
描述某些東西應有的屬性,但不知道如何實現它的確切步驟。在查詢的上下文中,查詢最佳化器採用宣告性查詢並決定如何最好地執行它。請參閱第42頁上的“資料的查詢語言”。
|
||||
描述某些東西應有的屬性,但不知道如何實現它的確切步驟。在查詢的上下文中,查詢最佳化器採用宣告性查詢並決定如何最好地執行它。請參閱“[資料查詢語言](ch2.md#資料查詢語言)”。
|
||||
|
||||
|
||||
|
||||
@ -170,7 +170,7 @@
|
||||
|
||||
|
||||
|
||||
為了加速讀取,在標準資料集中引入一些冗餘或重複資料,通常採用快取或索引的形式。非規範化的值是一種預先計算的查詢結果,像物化檢視。請參見“單物件和多物件操作”(第228頁)和“從同一事件日誌中派生多個檢視”(第461頁)。
|
||||
為了加速讀取,在標準資料集中引入一些冗餘或重複資料,通常採用快取或索引的形式。非規範化的值是一種預先計算的查詢結果,像物化檢視。請參見“[單物件和多物件操作](ch7.md#單物件和多物件操作)”和“[從同一事件日誌中派生多個檢視](ch11.md#從同一事件日誌中派生多個檢視)”。
|
||||
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@
|
||||
|
||||
|
||||
|
||||
一種資料集,根據其他資料透過可重複執行的流程建立。必要時,你可以執行該流程再次建立衍生資料。衍生資料通常用於提高特定資料的讀取速度。常見的衍生資料有索引、快取和物化檢視。參見第三部分的介紹。
|
||||
一種資料集,根據其他資料透過可重複執行的流程建立。必要時,你可以執行該流程再次建立衍生資料。衍生資料通常用於提高特定資料的讀取速度。常見的衍生資料有索引、快取和物化檢視。參見[第三部分](part-iii.md)的介紹。
|
||||
|
||||
|
||||
|
||||
@ -206,7 +206,7 @@
|
||||
|
||||
|
||||
|
||||
在由網路連線的多個節點上執行。對於部分節點故障,具有容錯性:系統的一部分發生故障時,其他部分仍可以正常工作,通常情況下,軟體無需瞭解故障相關的確切情況。請參閱第274頁上的“故障和部分故障”。
|
||||
在由網路連線的多個節點上執行。對於部分節點故障,具有容錯性:系統的一部分發生故障時,其他部分仍可以正常工作,通常情況下,軟體無需瞭解故障相關的確切情況。請參閱“[故障與部分失效](ch8.md#故障與部分失效)”。
|
||||
|
||||
|
||||
|
||||
@ -218,7 +218,7 @@
|
||||
|
||||
|
||||
|
||||
以某種方式儲存資料,即使發生各種故障,也不會丟失資料。請參閱第226頁上的“永續性”。
|
||||
以某種方式儲存資料,即使發生各種故障,也不會丟失資料。請參閱“[永續性(Durability)](ch7.md#永續性(Durability))”。
|
||||
|
||||
|
||||
|
||||
@ -230,7 +230,7 @@
|
||||
|
||||
|
||||
|
||||
提取-轉換-載入(Extract-Transform-Load)。從源資料庫中提取資料,將其轉換為更適合分析查詢的形式,並將其載入到資料倉庫或批處理系統中的過程。請參閱第91頁上的“資料倉庫”。
|
||||
提取-轉換-載入(Extract-Transform-Load)。從源資料庫中提取資料,將其轉換為更適合分析查詢的形式,並將其載入到資料倉庫或批處理系統中的過程。請參閱“[資料倉庫](ch3.md#資料倉庫)”。
|
||||
|
||||
|
||||
|
||||
@ -242,7 +242,7 @@
|
||||
|
||||
|
||||
|
||||
在具有單一領導者的系統中,故障切換是將領導角色從一個節點轉移到另一個節點的過程。請參閱第156頁的“處理節點故障”。
|
||||
在具有單一領導者的系統中,故障切換是將領導角色從一個節點轉移到另一個節點的過程。請參閱“[處理節點宕機](ch5.md#處理節點宕機)”。
|
||||
|
||||
|
||||
|
||||
@ -254,7 +254,7 @@
|
||||
|
||||
|
||||
|
||||
如果出現問題(例如,機器崩潰或網路連線失敗),可以自動恢復。請參閱第6頁上的“可靠性”。
|
||||
如果出現問題(例如,機器崩潰或網路連線失敗),可以自動恢復。請參閱“[可靠性](ch1.md#可靠性)”。
|
||||
|
||||
|
||||
|
||||
@ -278,7 +278,7 @@
|
||||
|
||||
|
||||
|
||||
一種資料副本,僅處理領導者發出的資料變更,不直接接受來自客戶端的任何寫入。也稱為輔助、僕從、只讀副本或熱備份。請參閱第152頁上的“領導和追隨者”。
|
||||
一種資料副本,僅處理領導者發出的資料變更,不直接接受來自客戶端的任何寫入。也稱為輔助、僕從、只讀副本或熱備份。請參閱“[領導者與追隨者](ch5.md#領導者與追隨者)”。
|
||||
|
||||
|
||||
|
||||
@ -290,7 +290,7 @@
|
||||
|
||||
|
||||
|
||||
透過任意關鍵字來搜尋文字,通常具有附加特徵,例如匹配類似的拼寫詞或同義詞。全文索引是一種支援這種查詢的次級索引。請參閱第88頁上的“全文搜尋和模糊索引”。
|
||||
透過任意關鍵字來搜尋文字,通常具有附加特徵,例如匹配類似的拼寫詞或同義詞。全文索引是一種支援這種查詢的次級索引。請參閱“[全文搜尋和模糊索引](ch3.md#全文搜尋和模糊索引)”。
|
||||
|
||||
|
||||
|
||||
@ -302,7 +302,7 @@
|
||||
|
||||
|
||||
|
||||
一種資料結構,由頂點(可以指向的東西,也稱為節點或實體)和邊(從一個頂點到另一個頂點的連線,也稱為關係或弧)組成。請參閱第49頁上的“和圖相似的資料模型”。
|
||||
一種資料結構,由頂點(可以指向的東西,也稱為節點或實體)和邊(從一個頂點到另一個頂點的連線,也稱為關係或弧)組成。請參閱“[圖資料模型](ch2.md#圖資料模型)”。
|
||||
|
||||
|
||||
|
||||
@ -314,7 +314,7 @@
|
||||
|
||||
|
||||
|
||||
將輸入轉換為看起來像隨機數值的函式。相同的輸入會轉換為相同的數值,不同的輸入一般會轉換為不同的數值,也可能轉換為相同數值(也被稱為衝突)。請參閱第203頁的“根據鍵的雜湊值分隔”。
|
||||
將輸入轉換為看起來像隨機數值的函式。相同的輸入會轉換為相同的數值,不同的輸入一般會轉換為不同的數值,也可能轉換為相同數值(也被稱為衝突)。請參閱“[根據鍵的雜湊分割槽](ch6.md#根據鍵的雜湊分割槽)”。
|
||||
|
||||
|
||||
|
||||
@ -326,7 +326,7 @@
|
||||
|
||||
|
||||
|
||||
用於描述一種操作可以安全地重試執行,即執行多次的效果和執行一次的效果相同。請參閱第478頁的“冪等”。
|
||||
用於描述一種操作可以安全地重試執行,即執行多次的效果和執行一次的效果相同。請參閱“[冪等性](ch11.md#冪等性)”。
|
||||
|
||||
|
||||
|
||||
@ -338,7 +338,7 @@
|
||||
|
||||
|
||||
|
||||
一種資料結構。透過索引,你可以根據特定欄位的值,在所有資料記錄中進行高效檢索。請參閱第70頁的“讓資料庫更強大的資料結構”。
|
||||
一種資料結構。透過索引,你可以根據特定欄位的值,在所有資料記錄中進行高效檢索。請參閱“[驅動資料庫的資料結構](ch3.md#驅動資料庫的資料結構)”。
|
||||
|
||||
|
||||
|
||||
@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
在事務上下文中,用於描述併發執行事務的互相干擾程度。序列執行具有最強的隔離性,不過其它程度的隔離也通常被使用。請參閱第225頁的“隔離”。
|
||||
在事務上下文中,用於描述併發執行事務的互相干擾程度。序列執行具有最強的隔離性,不過其它程度的隔離也通常被使用。請參閱“[隔離性(Isolation)](ch7.md#隔離性(Isolation))”。
|
||||
|
||||
|
||||
|
||||
@ -362,7 +362,7 @@
|
||||
|
||||
|
||||
|
||||
彙集有共同點的記錄。在一個記錄與另一個記錄有關(外來鍵,文件參考,圖中的邊)的情況下最常用,查詢需要獲取參考所指向的記錄。請參閱第33頁上的“多對一和多對多關係”和第393頁上的“減少端連線和分組”。
|
||||
彙集有共同點的記錄。在一個記錄與另一個記錄有關(外來鍵,文件參考,圖中的邊)的情況下最常用,查詢需要獲取參考所指向的記錄。請參閱“[多對一和多對多的關係](ch2.md#多對一和多對多的關係)”和“[Reduce側連線與分組](ch10.md#Reduce側連線與分組)”。
|
||||
|
||||
|
||||
|
||||
@ -374,7 +374,7 @@
|
||||
|
||||
|
||||
|
||||
當資料或服務被複制到多個節點時,由領導者分發已授權變更的資料副本。領導者可以透過某些協議選舉產生,也可以由管理者手動選擇。也被稱為主人。請參閱第152頁的“領導者和追隨者”。
|
||||
當資料或服務被複制到多個節點時,由領導者分發已授權變更的資料副本。領導者可以透過某些協議選舉產生,也可以由管理者手動選擇。也被稱為主人。請參閱“[領導者與追隨者](ch5.md#領導者與追隨者)”。
|
||||
|
||||
|
||||
|
||||
@ -386,7 +386,7 @@
|
||||
|
||||
|
||||
|
||||
表現為系統中只有一份透過原子操作更新的資料副本。請參閱第324頁的“線性化”。
|
||||
表現為系統中只有一份透過原子操作更新的資料副本。請參閱“[線性一致性](ch9.md#線性一致性)”。
|
||||
|
||||
|
||||
|
||||
@ -398,7 +398,7 @@
|
||||
|
||||
|
||||
|
||||
一種效能最佳化方式,如果經常在相同的時間請求一些離散資料,把這些資料放到一個位置。請參閱第41頁的“請求資料的區域性性”。
|
||||
一種效能最佳化方式,如果經常在相同的時間請求一些離散資料,把這些資料放到一個位置。請參閱“[查詢的資料區域性性](ch2.md#查詢的資料區域性性)”。
|
||||
|
||||
|
||||
|
||||
@ -410,7 +410,7 @@
|
||||
|
||||
|
||||
|
||||
一種保證只有一個執行緒、節點或事務可以訪問的機制,如果其它執行緒、節點或事務想訪問相同元素,則必須等待鎖被釋放。請參閱第257頁的“兩段鎖(2PL)”和301頁的“領導者和鎖”。
|
||||
一種保證只有一個執行緒、節點或事務可以訪問的機制,如果其它執行緒、節點或事務想訪問相同元素,則必須等待鎖被釋放。請參閱“[兩階段鎖定(2PL)](ch7.md#兩階段鎖定(2PL))”和“[領導者和鎖](ch8.md#領導者和鎖)”。
|
||||
|
||||
|
||||
|
||||
@ -422,7 +422,7 @@
|
||||
|
||||
|
||||
|
||||
日誌是一個只能以追加方式寫入的檔案,用於存放資料。預寫式日誌用於在儲存引擎崩潰時恢復資料(請參閱第82頁的“使二叉樹更穩定”);結構化日誌儲存引擎使用日誌作為它的主要儲存格式(請參閱第76頁的“有序字串表和日誌結構的合併樹”);複製型日誌用於把寫入從領導者複製到追隨者(請參閱第152頁的“領導者和追隨者”);事件性日誌可以表現為資料流(請參閱第446頁的“分段日誌”)。
|
||||
日誌是一個只能以追加方式寫入的檔案,用於存放資料。預寫式日誌用於在儲存引擎崩潰時恢復資料(請參閱“[讓B樹更可靠](ch3.md#讓B樹更可靠)”);結構化日誌儲存引擎使用日誌作為它的主要儲存格式(請參閱“[SSTables和LSM樹](ch3.md#SSTables和LSM樹)”);複製型日誌用於把寫入從領導者複製到追隨者(請參閱“[領導者與追隨者](ch5.md#領導者與追隨者)”);事件性日誌可以表現為資料流(請參閱“[分割槽日誌](ch11.md#分割槽日誌)”)。
|
||||
|
||||
|
||||
|
||||
@ -434,7 +434,7 @@
|
||||
|
||||
|
||||
|
||||
急切地計算並寫出結果,而不是在請求時計算。請參閱第101頁的“聚合:資料立方和物化檢視”和419頁的“中間狀態的物化”。
|
||||
急切地計算並寫出結果,而不是在請求時計算。請參閱“[聚合:資料立方體和物化檢視](ch3.md#聚合:資料立方體和物化檢視)”和“[物化中間狀態](ch10.md#物化中間狀態)”。
|
||||
|
||||
|
||||
|
||||
@ -458,7 +458,7 @@
|
||||
|
||||
|
||||
|
||||
以沒有冗餘或重複的方式進行結構化。 在規範化資料庫中,當某些資料發生變化時,您只需要在一個地方進行更改,而不是在許多不同的地方複製很多次。 請參閱第33頁上的“多對一和多對多關係”。
|
||||
以沒有冗餘或重複的方式進行結構化。 在規範化資料庫中,當某些資料發生變化時,您只需要在一個地方進行更改,而不是在許多不同的地方複製很多次。 請參閱“[多對一和多對多的關係](ch2.md#多對一和多對多的關係)”。
|
||||
|
||||
|
||||
|
||||
@ -470,7 +470,7 @@
|
||||
|
||||
|
||||
|
||||
線上分析處理。 透過對大量記錄進行聚合(例如,計數,總和,平均)來表徵的訪問模式。 請參閱第90頁上的“交易處理或分析?”。
|
||||
線上分析處理。 透過對大量記錄進行聚合(例如,計數,總和,平均)來表徵的訪問模式。 請參閱“[事務處理還是分析?](ch3.md#事務處理還是分析?)”。
|
||||
|
||||
|
||||
|
||||
@ -482,7 +482,7 @@
|
||||
|
||||
|
||||
|
||||
線上事務處理。 訪問模式的特點是快速查詢,讀取或寫入少量記錄,這些記錄通常透過鍵索引。 請參閱第90頁上的“交易處理或分析?”。
|
||||
線上事務處理。 訪問模式的特點是快速查詢,讀取或寫入少量記錄,這些記錄通常透過鍵索引。 請參閱“[事務處理還是分析?](ch3.md#事務處理還是分析?)”。
|
||||
|
||||
|
||||
|
||||
@ -494,7 +494,7 @@
|
||||
|
||||
|
||||
|
||||
將單機上的大型資料集或計算結果拆分為較小部分,並將其分佈到多臺機器上。 也稱為分片。 見第6章。
|
||||
將單機上的大型資料集或計算結果拆分為較小部分,並將其分佈到多臺機器上。 也稱為分片。見[第6章](ch6.md)。
|
||||
|
||||
|
||||
|
||||
@ -506,7 +506,7 @@
|
||||
|
||||
|
||||
|
||||
透過計算有多少值高於或低於某個閾值來衡量值分佈的方法。 例如,某個時間段的第95個百分位響應時間是時間t,則該時間段中,95%的請求完成時間小於t,5%的請求完成時間要比t長。 請參閱第13頁上的“描述效能”。
|
||||
透過計算有多少值高於或低於某個閾值來衡量值分佈的方法。 例如,某個時間段的第95個百分位響應時間是時間t,則該時間段中,95%的請求完成時間小於t,5%的請求完成時間要比t長。 請參閱“[描述效能](ch1.md#描述效能)”。
|
||||
|
||||
|
||||
|
||||
@ -530,7 +530,7 @@
|
||||
|
||||
|
||||
|
||||
在操作完成之前,需要對操作進行投票的最少節點數量。 請參閱第179頁上的“讀寫的法定人數”。
|
||||
在操作完成之前,需要對操作進行投票的最少節點數量。 請參閱“[讀寫的法定人數](ch5.md#讀寫的法定人數)”。
|
||||
|
||||
|
||||
|
||||
@ -542,7 +542,7 @@
|
||||
|
||||
|
||||
|
||||
將資料或服務從一個節點移動到另一個節點以實現負載均衡。 請參閱第209頁上的“再平衡分割槽”。
|
||||
將資料或服務從一個節點移動到另一個節點以實現負載均衡。 請參閱“[分割槽再平衡](ch6.md#分割槽再平衡)”。
|
||||
|
||||
|
||||
|
||||
@ -554,7 +554,7 @@
|
||||
|
||||
|
||||
|
||||
在幾個節點(副本)上保留相同資料的副本,以便在某些節點無法訪問時,資料仍可訪問。請參閱第5章。
|
||||
在幾個節點(副本)上保留相同資料的副本,以便在某些節點無法訪問時,資料仍可訪問。請參閱[第5章](ch5.md)。
|
||||
|
||||
|
||||
|
||||
@ -566,7 +566,7 @@
|
||||
|
||||
|
||||
|
||||
一些資料結構的描述,包括其欄位和資料型別。 可以在資料生命週期的不同點檢查某些資料是否符合模式(請參閱第39頁上的“文件模型中的模式靈活性”),模式可以隨時間變化(請參閱第4章)。
|
||||
一些資料結構的描述,包括其欄位和資料型別。 可以在資料生命週期的不同點檢查某些資料是否符合模式(請參閱“[文件模型中的模式靈活性](ch2.md#文件模型中的模式靈活性)”),模式可以隨時間變化(請參閱[第4章](ch4.md))。
|
||||
|
||||
|
||||
|
||||
@ -578,7 +578,7 @@
|
||||
|
||||
|
||||
|
||||
與主要資料儲存器一起維護的附加資料結構,使您可以高效地搜尋與某種條件相匹配的記錄。 請參閱第85頁上的“其他索引結構”和第206頁上的“分割槽和二級索引”。
|
||||
與主要資料儲存器一起維護的附加資料結構,使您可以高效地搜尋與某種條件相匹配的記錄。 請參閱“[其他索引結構](ch3.md#其他索引結構)”和“[分割槽與次級索引](ch6.md#分割槽與次級索引)”。
|
||||
|
||||
|
||||
|
||||
@ -602,7 +602,7 @@
|
||||
|
||||
|
||||
|
||||
與共享記憶體或共享磁碟架構相比,獨立節點(每個節點都有自己的CPU,記憶體和磁碟)透過傳統網路連線。 見第二部分的介紹。
|
||||
與共享記憶體或共享磁碟架構相比,獨立節點(每個節點都有自己的CPU,記憶體和磁碟)透過傳統網路連線。 見[第二部分](part-ii.md)的介紹。
|
||||
|
||||
|
||||
|
||||
@ -614,11 +614,11 @@
|
||||
|
||||
|
||||
|
||||
1.各分割槽負載不平衡,例如某些分割槽有大量請求或資料,而其他分割槽則少得多。也被稱為熱點。請參閱第205頁上的“工作負載偏斜和減輕熱點”和第407頁上的“處理偏斜”。
|
||||
1.各分割槽負載不平衡,例如某些分割槽有大量請求或資料,而其他分割槽則少得多。也被稱為熱點。請參閱“[負載偏斜和熱點消除](ch6.md#負載偏斜和熱點消除)”和“[處理偏斜](ch10.md#處理偏斜)”。
|
||||
|
||||
|
||||
|
||||
2.時間線異常導致事件以不期望的順序出現。 請參閱第237頁上的“快照隔離和可重複讀取”中的關於讀取偏斜的討論,第246頁上的“寫入偏斜和模糊”中的寫入偏斜以及第291頁上的“訂購事件的時間戳”中的時鐘偏斜。
|
||||
2.時間線異常導致事件以不期望的順序出現。 請參閱“[快照隔離和可重複讀](ch7.md#快照隔離和可重複讀)”中的關於讀取偏斜的討論,“[寫入偏斜與幻讀](ch7.md#寫入偏斜與幻讀)”中的寫入偏斜以及“[有序事件的時間戳](ch8.md#有序事件的時間戳)”中的時鐘偏斜。
|
||||
|
||||
|
||||
|
||||
@ -630,7 +630,7 @@
|
||||
|
||||
|
||||
|
||||
兩個節點同時認為自己是領導者的情況,這種情況可能違反系統擔保。 請參閱第156頁的“處理節點中斷”和第300頁的“真相由多數定義”。
|
||||
兩個節點同時認為自己是領導者的情況,這種情況可能違反系統擔保。 請參閱“[處理節點宕機](ch5.md#處理節點宕機)”和“[真相由多數所定義](ch8.md#真相由多數所定義)”。
|
||||
|
||||
|
||||
|
||||
@ -642,7 +642,7 @@
|
||||
|
||||
|
||||
|
||||
一種對事務邏輯進行編碼的方式,它可以完全在資料庫伺服器上執行,事務執行期間無需與客戶端通訊。 請參閱第252頁的“實際序列執行”。
|
||||
一種對事務邏輯進行編碼的方式,它可以完全在資料庫伺服器上執行,事務執行期間無需與客戶端通訊。 請參閱“[真的序列執行](ch7.md#真的序列執行)”。
|
||||
|
||||
|
||||
|
||||
@ -654,7 +654,7 @@
|
||||
|
||||
|
||||
|
||||
持續執行的計算。可以持續接收事件流作為輸入,並得出一些輸出。 見第11章。
|
||||
持續執行的計算。可以持續接收事件流作為輸入,並得出一些輸出。 見[第11章](ch11.md)。
|
||||
|
||||
|
||||
|
||||
@ -678,7 +678,7 @@
|
||||
|
||||
|
||||
|
||||
一個儲存主要權威版本資料的系統,也被稱為真相的來源。首先在這裡寫入資料變更,其他資料集可以從記錄系統衍生。 參見第三部分的介紹。
|
||||
一個儲存主要權威版本資料的系統,也被稱為真相的來源。首先在這裡寫入資料變更,其他資料集可以從記錄系統衍生。 參見[第三部分](part-iii.md)的介紹。
|
||||
|
||||
|
||||
|
||||
@ -690,7 +690,7 @@
|
||||
|
||||
|
||||
|
||||
檢測故障的最簡單方法之一,即在一段時間內觀察是否缺乏響應。 但是,不可能知道超時是由於遠端節點的問題還是網路中的問題造成的。 請參閱第281頁上的“超時和無限延遲”。
|
||||
檢測故障的最簡單方法之一,即在一段時間內觀察是否缺乏響應。 但是,不可能知道超時是由於遠端節點的問題還是網路中的問題造成的。 請參閱“[超時與無窮的延遲](ch8.md#超時與無窮的延遲)”。
|
||||
|
||||
|
||||
|
||||
@ -702,7 +702,7 @@
|
||||
|
||||
|
||||
|
||||
一種比較事物的方法(例如時間戳),可以讓您總是說出兩件事中哪一件更大,哪件更小。 總的來說,有些東西是無法比擬的(不能說哪個更大或更小)的順序稱為偏序。 請參見第341頁的“因果順序不是全序”。
|
||||
一種比較事物的方法(例如時間戳),可以讓您總是說出兩件事中哪一件更大,哪件更小。 總的來說,有些東西是無法比擬的(不能說哪個更大或更小)的順序稱為偏序。 請參見“[因果順序不是全序的](ch9.md#因果順序不是全序的)”。
|
||||
|
||||
|
||||
|
||||
@ -714,7 +714,7 @@
|
||||
|
||||
|
||||
|
||||
為了簡化錯誤處理和併發問題,將幾個讀寫操作分組到一個邏輯單元中。 見第7章。
|
||||
為了簡化錯誤處理和併發問題,將幾個讀寫操作分組到一個邏輯單元中。 見[第7章](ch7.md)。
|
||||
|
||||
|
||||
|
||||
@ -726,7 +726,7 @@
|
||||
|
||||
|
||||
|
||||
一種確保多個數據庫節點全部提交或全部中止事務的演算法。 請參閱第354頁上的“原子提交和兩階段提交(2PC)”。
|
||||
一種確保多個數據庫節點全部提交或全部中止事務的演算法。 請參閱[原子提交與二階段提交(2PC)](ch9.md#原子提交與二階段提交(2PC))”。
|
||||
|
||||
|
||||
|
||||
@ -738,7 +738,7 @@
|
||||
|
||||
|
||||
|
||||
一種用於實現可序列化隔離的演算法,該演算法透過事務獲取對其讀取或寫入的所有資料的鎖,直到事務結束。 請參閱第257頁上的“兩階段鎖定(2PL)”。
|
||||
一種用於實現可序列化隔離的演算法,該演算法透過事務獲取對其讀取或寫入的所有資料的鎖,直到事務結束。 請參閱“[兩階段鎖定(2PL)](ch7.md#兩階段鎖定(2PL))”。
|
||||
|
||||
|
||||
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
|
||||
|
||||
### 伸縮至更高的載荷
|
||||
## 伸縮至更高的載荷
|
||||
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
|
||||
|
||||
|
||||
#### 無共享架構
|
||||
### 無共享架構
|
||||
|
||||
|
||||
|
||||
@ -120,11 +120,11 @@
|
||||
|
||||
|
||||
|
||||
複製和分割槽是不同的機制,但它們經常同時使用。如[圖II-1](../img/figii-1.png)所示。
|
||||
複製和分割槽是不同的機制,但它們經常同時使用。如[圖II-1](img/figii-1.png)所示。
|
||||
|
||||
|
||||
|
||||
![](../img/figii-1.png)
|
||||
![](img/figii-1.png)
|
||||
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
|
||||
## 記錄和衍生資料系統
|
||||
## 記錄系統和衍生資料系統
|
||||
|
||||
|
||||
|
||||
@ -22,15 +22,15 @@
|
||||
|
||||
|
||||
|
||||
#### 記錄系統(System of record)
|
||||
***記錄系統(System of record)***
|
||||
|
||||
|
||||
|
||||
**記錄系統**,也被稱為**真相源(source of truth)**,持有資料的權威版本。當新的資料進入時(例如,使用者輸入)首先會記錄在這裡。每個事實正正好好表示一次(表示通常是**標準化的(normalized)**)。如果其他系統和**記錄系統**之間存在任何差異,那麼記錄系統中的值是正確的(根據定義)。
|
||||
**記錄系統**,也被稱為**真相源(source of truth)**,持有資料的權威版本。當新的資料進入時(例如,使用者輸入)首先會記錄在這裡。每個事實正正好好表示一次(表示通常是**正規化的(normalized)**)。如果其他系統和**記錄系統**之間存在任何差異,那麼記錄系統中的值是正確的(根據定義)。
|
||||
|
||||
|
||||
|
||||
#### 衍生資料系統(Derived data systems)
|
||||
***衍生資料系統(Derived data systems)***
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user