前言
漫長的內功修煉過程,閱讀過程中產生的讀書筆記,可以看成是目錄的升級版或者正文的簡略版(筆記中詳略均依據本人主觀意見)
每章的小結值得反覆閱讀
一、可靠、可擴展、可維護的應用系統
- 構成數據密集型系統的基本模塊 P11
- 硬盤的平均無故障時間(MTTF)約爲10~50年 P15
- 滾動升級 P16
- 假定“人”是不可靠的,那麼該如何保證系統的可靠性呢? P17
- 即使一個系統現在工作可靠,並不意味着他將來一定能夠可靠運轉。
- 發生退化的一個常見的原因是負載增加
- 如果系統以某種方式增長,我們應對增長的的措施有哪些。
- 我們該如何添加計算資源來處理額外的負載
- 描述負載(Twitter的負載) P18
- 描述性能 P20
- 響應時間異常的可能原因 P21
- 如果想知道典型的響應時間,平均值並不是合適的指標,因爲他掩蓋了一些信息,無法告訴有多少用戶實際經歷了多少延遲
- 因此最好使用百分位數 P21
- 中位數稱爲50百分位數(縮寫p50,另外還有p95,p99,p999)
- 針對負載增加的情況,可採用自動彈性系統和手動 P24
- 可維護性 P25
- 一個優秀的運營團隊至少負責以下內容 P25
- 簡單性是我們構建系統的關鍵目標之一
- 簡化系統並不意味着減少系統功能,而主要意味着消除意外方面的複雜性 P27
- 消除意外複雜性的最好手段之一是抽象,一個好的設計抽象可以贏隱藏大量的實現細節
- 可演化性:易於改變
- 小結 P28
二、數據模型與查詢語言
- 主要有關係模型,文檔模型和一些基於圖的模型
- 採用noSQL的幾個驅動因素 P35
- 比爾蓋茨的簡歷 P37
- 如果在關係模式中讀取一份簡歷,那麼需要執行多路查詢,要麼需要執行混亂的多路鏈接
- 而對於JSON的表示方法,所有相關信息都在一個地方,一次查詢就夠了
- 用戶簡歷到用戶的職位,教育歷史和聯繫信息的一對多關係,意味着數據存在樹狀結構,JSON表示將數結構顯式化
- 當使用ID時,對人類有意義的信息都存儲在一個地方,引用它的所有內容都使用ID(ID只在數據庫中有意義);當直接存儲文本時,則使用它的每條記錄中都保存了一份這樣的可讀信息。 P38
- 如果複製了多份重複的數據,那麼該模式通常就違背了規範化 P39
- 文檔數據庫的比較 P43
- 當集合中的數據不具有相同的結構時,(關係)模式帶來的困擾大於他所能提供的幫助,無模式文檔可能是更自然的數據模型。但是,當所有記錄都有相同的結構時,模式則是記錄和確保這種結構的有效機制。 P45
- 存儲局部性的性能優勢 P45
- 局部性優勢僅適用需要同時訪問文檔大部分內容的場景
- 融合關係模型與文檔模型是未來數據庫發展的一條很好的途徑 P46
- 聲明式語言通常合適於並行執行,而命令式代碼由於制定了特定的執行順序,很難在多核和多臺機器上並行化 P47
- MapReduce查詢 P50
- MapReduce是一種編程模型,用於在許多機器上批量處理海量數據,興起於Google
- MongoDB支持有限的MapReduce方式在大量的文檔上執行只讀查詢
- 圖狀數據模型 P52
- 對於聲明式查詢語言,通常在編寫查詢語句時,不需要指定執行細節:查詢優化器會自動選擇效率最高的執行策略,因此開發者可以專注於應用的其他部分。
- Datalog 基礎 P62
- 小結 P65
- 基因組數據庫軟件 P66
- 全文搜索 P66
三、數據存儲與檢索
-
主要討論日誌結構的存儲引擎和麪向頁的存儲引擎 P71
-
一個世界上最簡單的數據庫 P72
-
許多數據庫內部使用日誌(log),日誌是一個僅支持追加式更新的數據文件 P72
-
索引是基於原始數據派生而來的數據結構。很多數據庫允許單獨添加和刪除索引,而不影響數據庫的內容,他只會影響查詢性能
- 維護額外的結構勢必會引入開銷,特別是在新數據寫入時 P73
-
哈希索引 P73
-
哈希表索引的侷限性 P76
-
SSTable(排序字符串列表):key-value對的順序按鍵排序,要求每個鍵在每個合併的段文件中只能出現一次
-
Lucene是Elasticsearch和solr等全文搜索系統所使用的索引引擎,它採用了類似SSTable的方法來保存其字典。
- 全文索引比key-value複雜得多,但它基於類似的想法:給定搜索查詢中的某個單詞,找到提及該單詞的所有文檔(網頁,產品描述等)。
- 它主要採用key-value結構實現,其中鍵是單詞(詞條),值是包含該單詞的文檔ID的列表(倒排表)。在Lucene中,從詞條到posting list的映射關係保存在類SSTable的排序文件中,這些文件可以根據需要在後臺合併。
-
SSTable相關的性能優化 P80
-
最廣泛使用的一種數據結構:B-tree P80
- 像SSTable一樣,B-tree保留按鍵排序的key-value對,這樣可以實現高效的key-value查詢和區間查詢。但相似僅此而已:B-tree本質上具有非常不同的設計理念 P81
-
日誌結構索引將數據分解爲可變大小的段,B-tree將數據分解成固定大小的頁(一般爲大小4kb左右)
- 頁是內部讀/寫的最小單元
- 這種設計更接近底層硬件,因爲磁盤也是以固定大小的塊排列
-
具有n個鍵的B-tree總是具有O(log n)的深度。 P82
- 大多數數據庫可以適合3~4層的B-tree,因此不需要遍歷非常深的頁面層次即可找到所需的頁(分支因子爲500的4KB頁的四級樹可以存儲高達256TB)
-
預寫日誌(write-ahead log WAL),也稱爲重做日誌(僅支持追加修改),用於在數據庫崩潰時,將B-tree恢復到最近一致的狀態
-
B-tree的優化 P83
-
B-tree和LSM-tree P84
-
其他索引結構 P86
-
聚集索引(在索引中直接保存行數據)
-
非聚集索引(僅儲存引用中的數據的引用)
-
覆蓋索引(折中方案)
-
多列索引 P86
- 級聯索引
- 多維索引
- 空間索引
-
全文搜索和模糊索引 P88
-
在內存中保存所有內容 P88
-
與直覺相反,內存數據庫的性能優勢並不是因爲他們不需要從磁盤讀取。如果有足夠的內存,即使是基於磁盤的存儲引擎,也可能永遠不需要從磁盤讀取,因爲操作系統將最近使用的磁盤塊緩存在內存當中。
- 相反,內存數據庫可以更快,是因爲他們避免了使用寫磁盤的格式對內存數據接口編碼的開銷
-
反緩存方法(內存數據架構擴展到遠大於可用內存的數據集,方法類似於操作系統對虛擬內存和交換文件的操作,但能更加有效的管理內存) P89
-
事務,主要指組成一個邏輯單元的一組讀寫操作 P89
-
對比事務處理(OLTP)與分析系統(OLAP)的主要特性 P90
-
數據倉庫 P91
- 是單獨的數據庫,分析人員可以在不影響OLTP操作的情況下盡情的使用
- 數據倉庫包含公司所有各種OLTP系統的只讀副本
-
將數據導入倉庫的過程稱爲提取-轉換-加載(Extract-Transform-Load, ETL)P91
-
使用單獨的數據倉庫而不是直接查詢OLTP系統進行分析,很大的優勢在於數據倉庫可以針對分析訪問模式進行優化,事實證明,之前討論的索引算法適合OLTP,但不擅長應對分析查詢。
-
數倉模型:星型模式和雪花模型
-
在典型的數據倉庫中,表通常非常寬:事實表通常超過100列,有時候有幾百列,緯度表也可能非常寬,可能包括與分析相關的所有元數據。P94
-
根據列中的具體模式,可以採用不同的壓縮技術。在數據倉庫中特別有效的一種技術是位圖編碼。 P96
-
面向列存儲(列壓縮) P99
-
物化視圖:一個類似表的對象,其內容是一些查詢的結果,常用於OLAP當中…
-
小結 P101
四、數據編碼與演化
- 滾動升級(分階段發佈),每次將新版本部署到少數幾個節點,檢查新版本是否正常運行,然後逐步在所有節點上升級新代碼,這樣新版本部署無需服務暫停,從而支持更頻繁的版本發佈和更好的演化。 P109
- 向前兼容,向後兼容 P110
- 程序通常使用(至少)兩種不同的數據表示形式 P110
- 在內存中,數據保存在對象,結構體,列表,數組,哈希表和樹等結構中。這些數據結構針對CPU的高效訪問和操作進行了優化(通常使用指針)
- 將數據寫入文件或通過網絡發送時,必須將其編碼爲某種字包含的字節序列(例如Json文檔)。由於指針對其他進程沒有意義,所以這個字節序列表示看起來與內存中使用的數據結構大不一樣。
- 從內存中的表示到字節序列的轉化稱爲編碼(或序列化),相反的過程稱爲解碼(或解析,反序列化)
- 不同語言編碼庫帶來的一些深層次問題 P111
- 不同的標準化編碼(Json,XML,CSV) P111
- 對於僅在組織內部使用的數據,可以選擇更緊湊或更快的解析格式。對於一個小數據集來說,收益可以忽略不計,但一旦達到TB級別,數據格式的選擇就會產生很大的影響。 P113
- AVRO的讀模式與寫模式 P118
- Protocol Buffers、Thrift和Avro都使用了模式來描述二進制編碼格式。它們的模式語言比XML模式或JSON模式簡單的多 P123
- Json、XML、CSV與基於模式的二進制編碼的對比(二進制的一些不錯的屬性) P124
- 它們可以比各種“二進制Json”變體更緊湊,可以省略編碼數據中的字段名稱。
- 模式是一種有價值的文檔形式,因爲模式時解碼所必須的,所以可以確定它是最新的(而手動維護的文檔可能很容易偏離現實)
- 模式數據庫允許在部署任何內容之前檢查模式更改的向前和向後兼容性
- 對於靜態類型編程的用戶來說,從模式生成代碼的能力是有用的,它能夠在編譯時進行類型檢查
- 數據流模式,最常見的進程間數據流動的方式: P124
- 通過數據庫
- 通過服務調用
- 通過異步消息傳遞
- 數據比代碼更加長久 P126
- 微服務 P127
- 服務本身可以是另一項服務的客戶端(例如,典型的web應用服務器作爲數據庫的客戶端)。
- 這種方法通常用於將大型應用程序按照功能區分分解爲較小的服務,這樣當一個服務需要另一個服務的某些功能或數據時,就會向另一個服務發出請求。這種構建應用程序的方式傳統上被稱爲面向服務的體系結構(SOA),最近則更名爲微服務體系結構
- 兩種流行的web服務方法:REST和SOAP P128
- swagger P129
- 遠程過程調用(RPC)的一些缺陷 P129
- 本地函數調用時可預測的…
- 網絡請求有另一種結果:超時
- 重試失敗請求,可能會發生請求實際上已經完成,只是響應丟失的情況。
- 調用本地函數,通常需要大致相同的時間來執行,而網絡請求則無法預測。
- 調用本地函數時,可以高效的將引用(指針),傳遞給本地內存對象中。進行網絡請求時,參數都需要進行編碼。對於較大的對象來說,很容易出現問題。
- 客戶端和服務可以用不同的編程語言來實現,所以RPC框架必須將數據類型從一種語言轉換成另一種語言。
- 嘗試使用遠程服務看起來像編程語言中的本地對象一樣毫無意義 P130
- RESTful Api的一些顯著優點: P131
- 它有利於實驗和調試
- 支持所有的主流編程語言和平臺
- 並且有一個龐大的工具生態系統
- 異步消息隊列
- 異步消息隊列的優點: P132
- 如果接收方不可用或過載,他可以充當緩衝區,從而提高系統的可靠性
- 他可以自動將消息重新發送到崩潰的進程,從而防止消息丟失
- 它避免了發送方需要知道接收方的IP地址和端口號(這在虛擬機經常容易起起停停的雲部署中特別有用)
- 它支持將一條消息發送給多個接收方
- 它在邏輯上將發送方與接收方分離(發送方只是發佈消息,並不關心誰使用它們)
- 然而消息傳遞通信通常是單向的
- 但是我們可以在實現一個回覆隊列,該隊列由原始消息發送者來消費(即可以實現類似RPC的請求/響應數據流)
- 異步消息隊列的優點: P132
- 消息代理
- RabbitMQ
- Apache Kafka
- 三種流行的分佈式Actor框架處理消息編碼的方式如下 P133
- 小結 P134
五、分佈式數據系統
- 在多臺機器上分佈數據的理由
- 擴展性
- 容錯與高可用性
- 延遲考慮
- 系統擴展的幾種架構 P140
- 共享內存架構(硬件成本高)
- 共享磁盤架構(頻繁的資源競爭)
- 無共享架構(節點獨立)✔
- 將數據分佈在多節點時有兩種常見的方式 P141
- 複製
- 在多個節點上保存相同數據的副本
- 數據複製方案的難點在於處理那些需要持續更改的數據 P145
- 三種流行的複製數據變化的方法
- 主從複製
- 多主節點複製
- 無主節點複製
- 分區
- 將一個大塊頭的數據拆分成多個較小的子集即分區,不同的分區分配給不同的節點(也稱爲分片)
- 上述兩者經常結合使用
- 複製
- 每個保存數據庫完整數據集的節點稱之爲副本 P146
- 主從複製的基本原理 P146
- 同步複製與異步複製 P147
- 把所有節點都配置爲同步複製有些不切實際。 P148
- 在實踐中,如果數據庫啓用了同步複製,通常意味着其中某一個從節點是同步的,而其他節點是異步模式。 P148
- 萬一同步的從節點變得不可用或者性能下降,則將另一個異步的從節點提升爲同步模式。
- 這樣可以保證至少有兩個節點擁有最新的數據副本。
- 這種配置稱爲半同步
- 主從複製還經常會被配置爲全異步模式,不管從節點上數據多麼滯後,主節點總是可以繼續相應寫請求,系統的吞吐性能更好。
- 如何配置新的從節點 P149
- 處理節點的失效 P149
- 從節點失效:追趕式恢復
- 主節點失效:節點切換
- 切換節點的風險 P151
- 上述問題,包括節點失效、網絡不可靠、副本一致性、持久性、可用性和延遲之間的各種細微權衡,實際上正是分佈式系統核心的基本問題。 P151
- 基於預寫日誌(WAL)傳輸 P152
- 基於行的邏輯日誌複製 P153
- 基於觸發器的複製 P154
- 藉助許多關係數據庫都支持的功能:觸發器和存儲過程
- 最終一致性 P155
- 複製滯後問題
- 讀自己的寫
- 讀寫一致性 P156
- 可行方案
- 單調讀 P157
- 單調讀保證,如果某個用戶一次進行多次讀取,則他絕對不會看到回滾現象,即在讀取較新值之後又發生讀舊值的情況 P157
- 前綴一致讀(分區數據庫中出現的一種特殊問題)
- 該保證是說,對於一系列按照某個順序發生的寫請求,那麼讀取這些內容時也會按照當時寫入的順序。
- 讀寫一致性 P156
- 讀自己的寫
- 複製滯後的解決方案 P159
- 多主節點複製
- 適用場景 P162
- 多數據中心
- 離線客戶端操作
- 協作編輯
- 適用場景 P162
- 需要解決衝突問題
- 處理衝突最理想的策略是避免衝突 P164
- 系統必須收斂於一致狀態
- 自定義衝突解決邏輯
- 解決衝突最合適的方式可能還是依靠應用層,所以大多數節點複製模型都有工具來讓用戶編寫應用代碼來解決衝突 P165
- 在寫入時執行
- 在讀取時執行
- 自動衝突解決 P166
- 操作轉換,在線協作編輯應用背後的衝突解決算法 P166
- 多主節點複製
- 複製的拓補接口 P166
- 環形拓補
- 星型拓補
- 全部-至-全部拓補
- quorum讀寫 P170
- 無主節點複製由於旨在更好的容忍寫入衝突,網絡中斷和延遲尖峯等,因此也可適用於多數據中心操作
- 處理寫衝突
- 最終寫入者獲勝 P176
- 併發性,時間和相對性 P178
- 如果兩個操作並不需要意識到對方,我們即可以聲稱他們是併發操作
- 確定前後關係,一種確定併發操作性的算法 P178
- 合併同時寫入的值 P180
- 版本矢量
- 小結 P181
參考文檔
《數據密集型應用系統設計》