標籤:

《Designing Data-Intensive Application》讀書筆記 Part1

The Internet was done so well that most people think of it as a natural resource like the Pacific Ocean, rather than something that was man-made. When was the last time a tech‐ nology with a scale like that was so error-free? —Alan Kay, in interview with Dr Dobb』s Journal (2012)

1. Reliable, Scalable, and Maintainable Application

大部分 web 系統我們關心三個重要概念:

Reliability(可用性):系統需要持續正確工作(甚至是在遇到硬體故障、軟體錯誤、人為錯誤等)

  • 硬體故障:磁碟 crash、內存壞掉、電源被拔掉。通常通過硬體冗餘(備份)來解決
  • 軟體錯誤:比如軟體 bug、資源用盡、無響應、級聯錯誤。沒有特快快速解決軟體系統性錯誤的方法,有些方式還是有幫助的:比如做好系統之間交互的假定、全面的測試、進程隔離、崩潰自動重啟、生產環境監控和分析等。
  • 人為錯誤:人類是不可信的,經常出現操作、配置等出錯誤。
    • 以降低出錯可能的方式設計系統。比如良好的抽象、api、admin 介面能讓減少出錯的可能
    • 在人們最容易出錯的地方解耦。比如創建沙盒環境
    • 在所有層面測試,從單測到集成測試。自動化測試
    • 允許快速從人為失誤中恢復。比如快速回滾代碼和配置
    • 簡歷監控系統。比如性能和錯誤指標
    • 管理和培訓

Scalability(可擴展性):能應付系統增長(數據集、負載、複雜度的增長等)

  • 負載: 可以用一些負載參數衡量,比如 qps、緩存命中率、同時支撐用戶在線數等。書中用 tweet 舉例,感興趣可以看下,feed 劉系統常見的推拉模型。
  • 性能:1.負載參數增加的時候,保持系統資源(cpu、內存、帶寬)不變,性能如何變化?2.負載參數增加的時候,為了保持同樣性能需要增加多少資源?

在批處理系統例如 hadoop 中通常用吞吐量(throughput)衡量,即每秒處理的記錄個數。在線系統中通常用響應時間來衡量。 通常使用 percentiles 而不是算術平均值(average)來衡量響應時間,percentiles 指的是把響應時間從快到慢排列後取中位數作為分界點。比如如果 p95 響應時間是 1.5秒,意味著 100 個請求有 95 個少於 1.5秒,5個多於 1.5秒

Maintainability(可維護性):能夠有效率地擴充功能

眾所周知,軟體開發的主要成本不在起初的開發階段,而在持續維護:修復 bug、保持系統運轉、調查失敗、適應新平台、修改使其適用於新用例、修復技術債、添加新特性。 軟體系統的三個設計原則:

  • 可用性(Operability):保持操作簡單
    • 監控系統健康,快速恢復
    • 追查問題原因,比如系統故障或者性能惡化
    • 保持系統和平台更新
    • 預防不同系統之間相互影響
    • 為將來出現的問題做準備(容量升級)
    • 建立開發、配置管理的良好實踐
    • 預演複雜的維護任務,比如遷移應用到其他平台
    • 在系統配置修改的時候保持安全性
    • 保持生產環境平穩
    • 保留系統的知識財富
  • 簡約性(Simplicity):對新人友好,盡量減少複雜度

複雜癥狀:explosion of the state space,模塊之間緊耦合、糾纏不清的依賴、不一致的術語和命名、為了解決性能問題各種 hack、到處都是特殊用例。 避免意外複雜度(accidental complexity)的最好工具就是抽象,良好的抽象可以隱藏細節(高級語言程序設計就是例子)。

  • 可進化性(Evolvability):容易修改適應新需求

需求等總是在變,敏捷工作流提供了一個適應變化的框架(測試驅動開發TDD、重構)

2. Data Models and Query Languages

複雜應用通常有很多層,一個 api 構建在其他 api 上,但是基本思想很簡單:每一層通過乾淨的數據模型來隱藏其下層的複雜性

Relational Model Versus Document Model

資料庫多年來一直被傳統的關係型資料庫佔領,直到最近隨著互聯網各種業務的爆發,有時候傳統的關係型資料庫滿足不了需求,Nosql(Not Only Sql)出現。 在數據交互中,JSON 格式慢慢佔據主流,由於 某些格式可以用 JSON 自包含,我們可以用 MongoDB、RethinkDB、CouchDB、Espresso 等支持文檔型數據的資料庫來存儲。 在某些數據查詢中,使用 JSON 格式表示數據可以讓我們避免關係型資料庫中多次 join,更具效率

文檔模型的優點在於存儲模式靈活(schema-on-read)、性能更好,對於某些應用來說更符合應用使用的數據結構(比如自包含結構,一對多樹形結構)。

關係型資料庫則更好地支持 joins,多對一和多對多關係。

Query Languages for Data

講了聲明式查詢 (Declarative query lagunage) 和 命令式查詢(Imperative query)

Mapreduce query: Mongodb 支持 mapreduce 查詢,但是限制 map 和 reduce 是純函數 (pure function: 無副作用且無法執行額外資料庫查詢)

Graph-Like Data Models

圖由交點(節點)和邊(關係)組成。很多關係可用圖來建模:比如社交網路、web 網頁、路線圖。 很多知名演算法用來操作圖關係,比如最短路徑演算法、page rank

Property graph model: Neo4j(Use Cypher Query Language), Titan, InfiniteGraph.

3. Storage and Retrieval

Data Structures That Power Your Database

  • Hash Indexex: 類似於大多數編程語言中實現的dict,採用 hash map 實現,k,v 結構
  • SSTables(Sorted String Table) and LSM-Trees(Log-Structured Merge-Tree): basic idea of LSM-trees: Keeping a cascade of SSTables that merged in the background.
  • Btrees:幾乎是所有關係型資料庫實現索引的方式。

Transaction Processing or Analytics?

  • OLTP (online transcation processing): 互動式應用中訪問數據的模式,要求低延遲,通常只訪問小部分數據
  • OLAP (online analytics processing): 用來數據分析,通常需要掃描很大的數據集,每個記錄通常只訪問很少的列,通常用來聚集分析而不是直接返回給用戶,結果用來作為商業分析和決策。

Data Warehousing(數據倉庫)

ETL (Extract-Transform-Load): 數據倉庫是一個獨立的用來分析的資料庫,防止影響 OLTP 操作。大部分是從 OLTP 資料庫同步過來的只讀數據,轉換成比較容易分析的格式,然後導入數據倉庫。 常用的開源的有 Apaceh Hive, Spark SQL, Cloudera Impala Facebook Presto, Apache Tajo, Apache Drill.

Stars and Snowflakes: Schemas for Analytics

star scehma(dimensional modeling): the fact table in the middle, surrounded by its dimension snowflake schema: dimensions are further brokern down into subdimensions.

Column-Oriented Storage

OLAP 應用中大部分時間我們只需要一行中的某幾列。傳統的行式關係型資料庫會把每行所有數據載入,效率比較低(即使過濾了行也是先載入所有數據之後過濾)。 於是,列式存儲出現了。列式存儲的思想很簡單:不要把所有值存儲在一行,而是把所有值存儲在每列。這樣存儲就變成了從

date_keyproduct_sk140102691401026914010370

到:

date_key file contents: 140102, 140102, 140103 product_sk file contens: 69, 69, 70

列壓縮:一列中有很多元素是重複的,我們可以把一列分成不同的 n 個值,然後轉化成 n 個不同的 bitmap,每個 bitmap 代表一個值,每行用一個 bit 表示。bit 如果是 1 代表改行有相應值,0 代表沒有。

4. Encoding and Evolution

需求總是在變,這時候需要對原有的資料庫模式和代碼做出變更,同時保證兼容:

  • Backward compatibility: 新代碼可以讀取舊代碼寫入的數據
  • Forwared compatibility: 舊代碼可以讀取新代碼寫入的數據

Formats for Encoding Data

程序???常操作兩種類型的數據表現方式:

  • 內存中:數據被保存成對象、結構、列表、數組、哈希表、樹等。這些數據結構可以被高效率讀取和被 cpu 操作
  • 需要寫入文件或者網路的自包含的 bytes 序列:比如 JSON

把內存里的數據 編碼成比特序列的過程叫做 Encoding(serialization or marshalling),反過來叫做 Decoding(parsing, deserialization, unmarshalling)

JSON, XML and Binary Variants

JSON,XML,csv 經常用作數據交換,它們都是基於 文本的,可讀的。但是有以下問題:

  • 在數字編碼上有歧義。 XML 和 csv 無法區分包含數字的字元串和數字。 JSON 雖然區分但是無法精確處理大數字(js)
  • JSON 和 XML 不支持二進位 string(可以 base64編碼,但是會增加體積
  • There is optional schema support for both XML and JSON
  • csv 不支持任何模式

Binary encoding

內部使用的一些交換格式可以使用一些更高效率的編解碼方式。比如 二進位 encoding for JSON(MessagePack, BSON, BJSON, UBJSON, BISON, and Simle)。這些方式各有使用場景,但是依然沒有 json 和 xml 廣泛。而且,它們沒有預定義一個 schema,所以需要把所有對象的欄位名稱包含到 encoded 的數據里。

Thrift and Protocol Buffers

Apache Thrift and Protocol Buffers(protobuf) 都是二進位編碼庫。Protocol Buffers 最開始 google 開發,Thrift facebook 開發。

Thrift interface definition Language(IDL):

struct Person {n 1: required string username,n 2: optional ii64 favoriteNumber,n 3: optional list<string> interestsn}n

在 Protocol Buffers 等價定義:

message Person {n required string user_name = 1;n optional int64 favorite_number = 2;n repeated string interests = 3;n}n

兩者都是實現了代碼生成工具來生成對應語言的代碼。

Thrift 有兩種不同的二進位編碼格式:BinaryProtocol and CompactProtocol

Field tags and schema evolution

schema 變了以後 Thrift and Protocol Buffers 是如何處理 schema 變化的來保持前向和後向兼容的? 你可以改變欄位名,但是不能修改它的 tag 名稱。可以通過增加 tag 數字型大小來增加新的欄位,client 端可以簡單忽略新增的欄位。 由於每個 欄位都被一個 tag 數字標識,所以新代碼可以直接讀舊代碼的數據,因為每個 tag 代表的欄位含義不變。唯一的要求就是如果想添加一個新欄位,不能是 required,因為老代碼無法寫入新增的這個欄位。所以新加的欄位必須是 optional 或者有一個默認值。

Datatypes and schema evolution

如果改變一個欄位的類型呢,可以但是會有損失精度或者被截斷的風險。

Avro

Apache Avro 是另一種二進位編碼格式,Hadoop 子項目。包含兩種格式定義語言(Avro IDL),用於人類編輯,還有一種機器易讀的基於 JSON 的定義。

Modes of Dataflow

How data flows between processes:

  • via database

數據可能會在新、老代碼交換數據之間丟失。

  • via service call
  • via asynchronous message passing

Dataflow Through Services: Rest and RPC

不同進程之間通過網路交換數據,通常採用 client-server 模型。 SOA(service oriented architecture): 最近慢慢演變成 microservices architecture。既能自己提供服務,也能調用其他服務獲取數據。 這種架構使得修改和維護服務更容易。每個服務都可以由一個單獨的團隊來維護。

web service

  • REST: rest 不是協議,更像是一種基於 http 原則的設計哲學,經常用在微服務架構。遵守REST原則設計的 api 成為restful
  • SOAP: xml-based protocol, 大多構建在 http 上,但是目標更傾向於獨立於 http 並且避免使用 大部分 http 特性。jo

The problems with remote procedure calls(RPCs)

RPC:遠程過程調用,目標是使得遠程服務調用就像是在本地進程里。這個目標有些瑕疵,網路調用和本地函數調用是有很大區別的:

  • 本地調用是可預測的,成功或者失敗,取決於傳參控制。但是遠程網路調用是不可預測的,比如網路環境、遠程服務不可用
  • 本地調用返回結果、拋出異常或者因為程序死循環等用不返回,遠程調用可能會因為超時返回一個沒值的結果
  • 如果你重試一個失敗的網路請求,可能會發生請求發出去了只是沒有返回結果的情況,這個時候重試可能導致多次請求, 除非建立一個機制防止重複。
  • 本地調用基本都是執行一樣的時間,網路調用延遲可能會因為負載高而延遲
  • 本地調用可以傳遞指針,遠程調用必須都傳遞過去
  • rpc 跨語言,可能在轉換的時候出問題,比如某些語言不支持大數

Asynchronous Message-Passing Dataflow

和 rpc 類似的是通常一個客戶端請求(usually called a message) 被發送到另一個進程,消息不是直接通過網路請求發送,而是通過一個叫做 message broker(also called a message queue or message-oriented middleware),用來臨時存儲消息。 相比 rpc,有幾個好處:

  • 如果接受者負載高,可以作為一個緩存 buffer,提高系統的可用性
  • 可以自動重發消息如果進程 crash,保護消息避免丟失
  • 發送方不必知道接收方的 ip、埠號(使用雲部署平台的時候比較有用)
  • 允許一個消息發送個多個接受者
  • 邏輯上解耦了發送者和接收者

發送發一般不用關心消息的返回結果,從而實現非同步

Message brokers

歷史上消息 broker 曾被商業公司壟斷,最近開源的 RabbitMQ, ActiveMQ, HornetQ, NATS, Apache Kafka 慢慢流行。 message broker 通常如下使用:一個進程發送消息到命名隊列或者topic,broker保證消息被分發到一個或者多個消費者(訂閱了queue或者 topic的) 同一個topic上可以有多個生產者和消費者

Distributed actor frameworks

actor 模型是一種在單個進程上實現並發的編程模型。與使用線程實現不同,邏輯被封裝成 actors。每個 actor 代表一個實體或者 client(可以有自己的狀態),通過非同步消息和其他 actor 通信,但是消息分發不能被保證。一個actor 每次只處理一個消息。不用擔心i安城,每個 actor 可以被框架來調度。在分散式 actor 框架中,一個應用被擴展在多個節點,消息傳遞是透明的。 幾個流行的 分散式 actor 框架:

  • Akka
  • Orleans
  • Erlang OTP

推薦閱讀:

演講實錄|馬曉宇:When TiDB Meets Spark
[OSDI2012] Spanner: Google』s Globally-Distributed Database, Part 1
測試分散式系統的線性一致性
如果一個對象有1000+以上的屬性,那麼應該如何安排數據表規畫?
為什麼資料庫有那麼多數據類型?

TAG:数据库设计 |