資料庫系統設計概述

碼哥位元組發表於2020-08-02

世界上只有兩種開發人員,一種使用資料庫系統的,一種開發資料庫系統的。

資料是系統最重要的資訊。大部分系統都是對資料的管理。應用系統通過資料模型來構建現實世界,通過演算法操作物件或資料結構,來改變資料模型的狀態。資料被組織在作業系統檔案中,我們通過資料系統來組織,查詢,搜尋,處理資料。

本文將從資料庫的發展、資料庫的分類、常見資料庫架構,資料庫常見概念和技術等方面探討這個我們接觸最多的底層系統,並通過穿插不同資料庫的實現原理,來了解資料庫的具體實現。

本文分為五個大章節。探古溯源,從資料庫的誕生,發展,現狀和展望來了解資料庫存在的意義,以及資料庫設計的歷史與現實原因。百家爭鳴,本節從不同分類方式,講解一些不同的資料庫系統實現,有助於擴充我們的視野,在技術選型時可以作為參考(底層資料庫系統的選型對整個系統的架構實在太重要了)。承上啟下,本節是整篇文章的中間章節,前兩章以興趣點,純理論展開,在本節中將對前兩章做一個總結,有了前兩章知識,我們已經可以去選擇適合專案需求的資料庫系統,對那些想更深入瞭解底層儲存的同學也可以選擇自己感興趣的資料庫型別和方案找到相應的實現,從而進入下一步學習。下面兩章將講解更多具體的技術點。知行合一,這一章節將講解資料庫的實現,分析一些資料庫架構,分散式問題和解決方案,透析具體的資料庫常見的技術點。

針對不同興趣,大家可以按需取之,跳過不感興趣的,看想關注的點。

一、探古溯源

疑今者察之古,不知來者視之往。——《管子》

資料庫管理系統允許人員組織,儲存和從計算機檢索資料。在計算機的早期,使用“打孔卡”用於輸入,輸出和資料儲存。打孔卡提供了一種快速的資料輸入和檢索方法。資料庫在計算機的最新發展中起了非常重要的作用。第一批計算機程式是在 1950 年代初期開發的,幾乎完全專注於編碼語言和演算法。當時,計算機基本上是大型計算器,資料(名稱,電話號碼)被認為是處理資訊的殘餘物。當計算機開始商業化後,資料的重要性開始越來越被人重視。

timeline of database

題外話:穿越時間——筆者去了解一個東西,總喜歡追根溯源,從時間的起點,或從邏輯的深處開始探索。一個東西的邏輯原點往往是純粹的簡單的,之後隨時間發展和廣延的展開會逐漸複雜起來。所以從頭開始瞭解一個東西,往往更容易理解。比如我們看一個系統的原始碼,可以從該系統的 1.0.0 版本開始,可以從這個系統最初想要解決的問題開始。

計算機資料庫始於 1960 年代。此十年中,有兩種流行的資料模型:稱為 CODASYL 的網路模型和稱為 IMS 的層次模型。SABER 系統被證明是商業上成功的一種資料庫系統,該系統被 IBM 用來幫助美國航空管理其預訂資料。

1970 年,大神 EF Codd 發表了一篇重要的論文:《大型共享資料庫的資料關係模型》,提出了使用關聯式資料庫模型的建議,他的想法改變了人們對資料庫的看法。在他的模型中,資料庫的架構或邏輯組織與物理資訊儲存斷開連線,這成為資料庫系統的標準原理。之後 UBC 開發了 Ingres 和在 IBM 開發了 SystemR。Ingres 使用一種稱為 QUEL 的查詢語言,引導而誕生了 Ingres CorpMS SQL ServerSybasePACEBritton-Lee 之類的系統。另一方面,System R 使用 SEQUEL 查詢語言,它有助於 SQL / DSDB2AllbaseOracleNon-Stop SQL 的開發。關聯式資料庫管理系統(RDBMS)已經成為公認的術語。

1976 年 P. Chen 提出了一個新的資料庫模型,稱為 Entity-Relationship,即 ER。該模型使設計人員可以專注於資料應用程式,而不是邏輯表結構。1980 年結構化查詢語言或 SQL 成為標準查詢語言。

RDBM系統是儲存和處理結構化資料的有效方法。然而,隨著網際網路的快速發展,“非結構化”資料(視訊,照片,音樂等)變得更加普遍。非結構化資料既是非關係資料,又是無模式資料,而關聯式資料庫管理系統根本就沒有設計用於處理此類資料。21 世紀後,NoSql模型進入人們的視野,NoSql 的出現是對網際網路以及對更快的速度和對非結構化資料的處理需求的一種回應。一般而言,由於 NoSQL 資料庫的速度和靈活性,它們在某些用例中比關聯式資料庫更可取的。NoSQL模型是非關係型的,並且採用“分散式”資料庫系統。這個非關係系統速度很快,使用臨時組織資料的方法,並且處理大量不同型別的資料。一般而言,NoSQL 相對於 RDBMS 系統有如下優勢:

  • 更高的可擴充套件性
  • 分散式計算系統
  • 低成本
  • 靈活的架構
  • 可以處理非結構化和半結構化資料
  • 沒有複雜的關係

在資料庫的發展歷程中,雖然只經歷了短短半個世紀,卻誕生了一批優秀的資料庫系統,SystemRPostgresqlMysqlDB2OracleMongoDBHBaseNeo4jElasticsearch 等等,都在軟體的發展中發揮了重要的。

hitory of database

二、百家爭鳴

現在春天來了嘛,一百種花都讓它開放,不要只讓幾種花開放,還有幾種花不讓它開放,這就叫百花齊放。—— 毛潤之

迄今為止,業界誕生的資料系統數不勝數。如果你開啟DB-Engines 網站,可以看到幾百個功能定位不同的資料庫系統。檢視DB-Engines的分類排名,可以看出DB-Engines將如此眾多的系統大致分為以下幾類(網址):

db engines

Willian Blair 在《Database Software Market:The Long-Awaited Shake-up》一文中以以下維度為資料庫系統做了一個細緻的分類:關係型/非關係型、操作型/分析型

databases

上圖中的縱軸分類為 Relational Database(關係型資料庫,RDBMS)和 Nonrelational Database (非關係型資料庫,NoSQL),橫軸的分類為 Operational(操作型,即 OLTP)和 Analytical(分析型,即 OLAP)。

非關係型的分類是一個比較籠統的劃分,主要是針對傳統關係型來區分的,與傳統關係型系統模型不一致的都劃分到了非關係型中。

非關係型(NoSQL)可以再進一步劃分:Key-Value 型、列儲存型、文件型、圖資料庫等。

  • 文件儲存:MongoDB、Elasticsearch、Amazon DocumentDB、Azure Cosmos DB 等。
  • Key-Value 儲存:Redis Labs、Oracle Berkeley DB、Amazon DynamoDB、Aerospike、LevelDB 等。
  • 圖資料庫:Neo4j 等。
  • 時序資料庫:InfluxDB、Timescale 等。
  • WideCloumn:DataStax、Cassandra、Apache HBase 和 Bigtable 等。

database type

關係模型

關係型模型是大多數開發人員接觸最早,接觸最多的資料庫模型。它基於集合理論,是最經典的資料庫模式。關係型資料庫採用行和列的二維表來建模資料。它適合於提前知道資料模型,並且資料模型比較固定,發生變化比較小,對查詢比較靈活的場景,你只需要將資料以行和列的方式儲存,在查詢的時候以不同的需要組合資料。關係型不適合資料層次較多,記錄與記錄之間關聯較多的場景,這種場景往往造成查詢複雜度上升,查詢效能下降。

關係型資料庫主要用於大多數商業資料處理,其大多數是事務處理(如 ERP 系統、銀行交易、航空公司訂票、銷售系統、金融財務管理系統等)和批處理場景(如客戶發票、工資單、報告等)。

20 世紀 70 年代至今,關係型資料庫經久不衰,其簡潔的資料模型和經典的 SQL 查詢語句支撐了當前大部分網際網路系統,線上論壇、社交網路、電子商務等等,各式各樣的系統背後,都隱藏著一個強大的關聯式資料庫。

關係型資料庫用的比較多的除了 OracleSql Server 等商業資料庫外,就是 Mysql 了,另外本人比較喜歡和推崇是 Postgresql,被稱為世界上功能最強大的開源資料庫。

分析的世界

聯機分析處理(Online analytical processing),簡稱OLAP,OLAP 是相對與傳統的OLTP(聯機事務處理,Online Transaction Processing)系統而言的,OLTP 是傳統的關係型資料庫的主要應用,側重於基本的、日常的互動式的事務處理,例如銀行交易。OLAP 是資料倉儲系統的主要應用,支援複雜的分析操作,側重分析決策支援,並且提供直觀易懂的查詢結果。OLAP 工具讓使用者能夠從多個角度互動地分析多維資料。OLAP 由三個基本的分析操作組成:上卷(roll-up)、鑽取(drill-down)、切片(slicing)和切塊(dicing)。上卷涉及可以在一個或多個維度中累積和計算的資料的聚合。

OLAP 利於大資料量,資料更新少,經常使用大量資料做聚合統計的場景。OLTP 適合資料量小,頻繁操作更新資料的場景。

OLAP 主要應用於商業智慧、風控分析、智慧報表等業務場景。

分析事務是兩個世界。在分析需求不大的時候,很多團隊直接使用業務事務資料庫做分析使用,這隻能支援小資料量、分析需求變化不大,弱分析的場景。真正的資料分析場景,往往使用單獨的資料倉儲。在不影響業務庫的情況下,實時或週期批量地從中提取資料,轉換成對分析友好的資料模式,執行必要的清理和轉換,然後載入到資料倉儲中。將資料匯入倉庫的過程稱為提取-轉換-載入(Extract-Transform-Load, ETL)。

ETL

OLTPOLAP沒有明確的邊界,它們的一些典型特性如下所示:

OLTP OLAP
使用者 操作人員,底層管理人員 決策人員,高階管理人員
功能 日常操作處理 分析決策
DB 設計 面向應用 面向主題
資料 當前的,新的,細節的,二維的,分立的 歷史的,聚集的,多維整合的,統一的
存取 讀寫數十上百條資料 讀百萬級資料
讀特徵 基於鍵,返回少量資料 基於大量資料彙總
寫特徵 隨機訪問,低延遲 批量或資料流
DB 大小 100MB~~GB 100GB~~TB
時間要求 實時性 對時間的要求不嚴格
主要應用 資料庫 資料倉儲

業界有許多優秀的開源的 OLAP 系統,比如:

  • Druid:Metamarkets 公司開發的一個用於大資料實時處理的開源分散式系統。目前已經成為 Apache 的開源專案。官網 瞭解
  • Kylin:Apache Kylin™ 是一個開源的、分散式的分析型資料倉儲,提供 Hadoop/Spark 之上的 SQL 查詢介面及多維分析(OLAP)能力以支援超大規模資料,最初由 eBay 開發並貢獻至開源社群。它能在亞秒內查詢巨大的表。官網
  • Presto:Presto 是一個對 PB 級資料執行互動式分析的開源分散式 SQL 查詢引擎。官網
  • ClickHouse:ClickHouse 是由號稱“俄羅斯 Google”的 Yandex 開發的一個列儲存的 OLAP 系統。官網

列式儲存

傳統 OLTP 資料庫通常採用行式儲存。以下圖為例,所有的列依次排列構成一行,以行為單位儲存,再配合以 B+ 樹或 SS-Table 作為索引,就能快速通過主鍵找到相應的行資料。

row-format

行儲存適用於 OLTP 場景,OLTP 的大多數操作都是以實體(Entity)為單位,即對每條記錄的增刪改查,因此將一行資料在物理上放在相鄰的位置更利於操作,也更利於特定的優化。

在 OLAP 場景中,極少單獨操作單條記錄的情況。OLAP 分析往往針對大量的資料集,在大量的資料集的基礎上對特定的列做分組、過濾、聚合操作。因此在物理上將每列資料放在相鄰的位置。

column-format

這樣如果針對某一列做分析聚合,只需要找到相應列的檔案,或資料塊的位置,比如,要計算上圖資料的平均 Age,只需要獲取 Age 列的資料集即可。但是,面向行的儲存引擎仍然需要將所有行從磁碟載入到記憶體中、解析它們,並過濾出不符合所需條件的行。這可能需要很長的時間。

基於列模式的儲存,天然就會具備以下幾個優點:

  • 自動索引

    因為基於列儲存,所以每一列本身就相當於索引。所以在做一些需要索引的操作時,就不需要額外的資料結構來為此列建立合適的索引。

  • 利於資料壓縮

    利於壓縮有兩個原因。一來你會發現大部分列資料基數其實是重複的,拿上面的資料來說,因為同一個 author 會發表多篇部落格,所以 author 列出現的所有值的基數肯定是小於部落格數量的,因此在 author 列的儲存上其實是不需要儲存部落格數量這麼大的資料量的;二來相同的列資料型別一致,這樣利於資料結構填充的優化和壓縮,而且對於數字列這種資料型別可以採取更多有利的演算法去壓縮儲存。

列式儲存的概念其實很早就有,只是應時代所需,列式儲存在近幾年才火熱起來,一時湧現了很多優秀的列式儲存資料庫,甚至很多之前的行儲存系統,也有了列式儲存的能力。

  • Hbase:一個分散式的、面向列的開源資料庫,該技術來源於 Fay Chang 所撰寫的 Google 論文《Bigtable:一個結構化資料的[分散式儲存系統]》。HBase 不同於一般的關聯式資料庫,它是一個適合於非結構化資料儲存的資料庫。另一個不同的是 HBase 基於列的而不是基於行的模式。
  • Cassandra:它最初由 Facebook 開發,用於改善電子郵件系統的搜尋效能的簡單格式資料,集 Google BigTable 的資料模型與 Amazon Dynamo 的完全分散式架構於一身。Facebook 於 2008 將 Cassandra 開源,此後,由於 Cassandra 良好的可擴充套件性其被許多知名網站所採用,成為了一種流行的分散式結構化資料儲存方案。
  • 其中上一章節提到的很多 OLAP 資料庫大多數是面向列式儲存的。如 DruidClickHouse 等。

檢索不再高深

曾幾何時,全文檢索是一個多麼高深的技術,雖然如 Google 這樣的全網搜尋引擎背後的搜尋演算法和技術依然不是輕易就可以實現的。但現在大大小小的各種 App,網站的搜尋功能的背後技術基本被一個強大的開源系統輕鬆就可以實現了。這個系統就是 Elasticsearch,一個基於 Lucence 的分散式實時全文檢索資料庫。

倫敦的公寓內,Shay Banon 正在忙著尋找工作,而他的妻子正在藍帶 (Le Cordon Bleu) 烹飪學校學習廚藝。在空閒時間,他開始編寫搜尋引擎來幫助妻子管理越來越豐富的菜譜。

他的首個迭代版本叫做 Compass。第二個迭代版本就是 Elasticsearch(基於 Apache Lucene 開發)。他將 Elasticsearch 作為開源產品釋出給公眾,並建立了 #elasticsearch IRC 通道,剩下來就是靜待使用者出現了。

公眾反響十分強烈。使用者自然而然地就喜歡上了這一軟體。由於使用量急速攀升,此軟體開始有了自己的社群,並引起了人們的高度關注,尤其引發了 Steven Schuurman、Uri Boness 和 Simon Willnauer 的濃厚興趣。他們四人最終共同組建了一家搜尋公司。

一個程式設計師為幫助妻子管理菜譜開發的搜尋工具最終稱為一個強大的全文檢索資料庫。看來,物件導向依然是程式設計師創作的強大靈感源泉之一。

revert-index

將非結構化資料中的一部分資訊提取出來,重新組織,使其變得有一定結構,然後對此有一定結構的資料進行搜尋,從而達到搜尋相對較快的目的。這部分從非結構化資料中提取出的然後重新組織的資訊,稱之索引。將這些索引與文件建立對映關聯,通過索引檢索出對應的文件資料,這種詞彙到文件的對映被稱之為倒排索引。先建立索引,再對索引進行搜尋的過程就叫全文檢索

提到全文檢索,不得不提到的一個技術就是 Lucene,Lucene 是 apache 下的一個開放原始碼的全文檢索引擎工具包。提供了完整的查詢引擎和索引引擎,部分文字分析引擎。

Elastisearch 就是基於 Lucene 的一個分散式開源全文檢索資料庫。它提供了一個分散式多使用者能力的全文搜尋引擎,基於 RESTful web 介面。Elasticsearch 是用 Java 開發的,並作為 Apache 許可條款下的開放原始碼釋出,是當前流行的企業級搜尋引擎。設計用於雲端計算中,能夠達到實時搜尋,穩定,可靠,快速,安裝使用方便。許多系統的搜尋功能背後,其實就是一個強大的 Elastisearch 服務,Elasticsearch 也常由於日誌檢索,資料分析場景。

K-V 快取霸主

在整個計算機系統中磁碟和網路是最慢的部分,一個系統中最重要的東西就是資料,而目前系統中的資料最終都儲存在磁碟上。因此當前磁碟緩慢的讀寫速度和人民對系統響應資料和系統高併發之間的矛盾,就是目前系統需要解決的主要矛盾。將透徹了,所有的系統優化都是在緩解這個矛盾。

為提供系統響應資料和併發能力,一個最常見的手段就是快取。在計算機系統中,CPU,記憶體,磁碟,網路的訪問效率差著不同的數量級,為緩解這種數量級帶來的訪問效率問題,最常見的手段就是快取。CPU 和記憶體之間有快取,稱之為 CPU 高效緩衝;記憶體和磁碟之間也自帶快取。

cache

在分散式系統中,資料庫訪問的壓力,我們常常使用分散式快取系統來解決。

Redis 是一個高效能的 key-value 資料庫。它支援儲存的 value 型別相對更多,包括 string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和 hash(雜湊型別)。Redis 支援快取過期時間,原子操作,資料持久化,支援叢集模式。

  • K-V 快取:將資料 K-V 化並快取在 Redis 中,從而提高資料的訪問效率,減小資料庫的訪問壓力,這種常見的系統優化策略。
  • 分散式鎖:分散式鎖,就是一個全域性的臨界資源,通過對這個臨界資源的獨佔達到一種全域性鎖的功能,任何全域性共享資源都可以實現分散式鎖的功能,甚至 MySql,分散式檔案系統。基於 Redis 的分散式鎖,是常見的一種實現。
  • Pub\Sub:釋出訂閱的管道功能本不應該是一個分散式快取系統的功能,但 Redis 實現了這一部分功能,在一些簡單的釋出訂閱場景下也可以很好的工作。
  • 布隆過濾器:通過一個 bit 的 0 或 1 來表示 key 是否存在,通過 bit 集合來表示一組資料,這就是簡單的布隆過濾器的實現。相對與用類似 Hash 的方式來儲存 key 對映 boolean 值的方式,布隆過濾器可以節省大量的空間。Redis 就有布隆過濾器的實現。布隆過濾器常用來對大量資料做 True Or Flase 的判斷,比如快取是否存在,比如大量使用者是否有許可權。
  • HyperLogLog:HyperLogLog 是用來快速計算基數的。基數,即不重複元素的個數(類似 SQL 的 count distinct)。
  • 工具:介紹一些好用的 Java 技術棧的相關工具。Jetcache,阿里開源的一個基於註解的快取框架。Redisson,一個強大的 Redis Java 客戶端工具。

小而精

通常我們使用的資料庫系統大多是 Client-Server 模式的,即資料庫服務作為一個常駐程式執行在 Server 端,應用程式通過 TCP/IP 協議訪問資料庫系統。還有一種嵌入式的資料庫,可以執行在本機中,這種資料庫嵌入到應用程式中,隨應用程式啟動,資料儲存在本地磁碟中。這種資料庫是輕量的,一般佔用記憶體少,程式碼精簡。

  • SQLite:遵守 ACID,實現了大多數 SQL 標準,支援 SQL 語法。支援 JDBC。
  • H2:一個 Java 編寫的關係型資料庫,它可以被嵌入 Java 應用程式中使用,或者作為一個單獨的資料庫伺服器執行。Spring Boot 內建的資料庫。
  • Berkeley DB:一個高效的嵌入式資料庫和鍵-值資料庫程式設計庫。
  • LevelDB:是 Google 開源的持久化 KV 單機資料庫,具有很高的隨機寫,順序讀/寫效能,LevelDB 應用了 LSM(Log Structured Merge) 策略。另一個 Facebook 基於 levelDB 開發的 RocksDB,也是一個高效能的 key-value 型內嵌式儲存引擎。LevelDB 或 RocksDB 常常被當作儲存引擎使用。比如強大的時間序列資料庫 Influxdb 早期底層儲存引擎就是用於的 LevelDB;RocksDB 是流式計算框架 Flink 的 Checkpoint 的底層儲存引擎;著名的分散式 Actor 框架 Akka 也使用 RocksDB 作為預設的 checkpint 儲存。由於其強大的順序讀寫能力,也常常用來做 WAL(write-ahead-log)日誌儲存引擎。

這些小而精的嵌入式資料庫,除了用在一些小型裝置上,如手機客戶端等。也常常被用於很多自研資料庫系統的儲存引擎。這些自研的資料庫系統,以上面那些嵌入式資料庫作為儲存引擎,在上面實現自己特有功能,從而實現一個特殊的資料庫系統,比如擴充套件分散式功能,基於其現實一個分散式儲存系統;比如基於 LevelDB 等實現磁碟佇列,和分散式佇列;比如基於其儲存特殊的模型的資料,如時間序列資料庫;比如基於其實現本地操作日誌記錄和重試提交,實現最終一致性的分散式事務解決方案。

三、承上啟下

前幾章我們已經瞭解了資料庫系統的發展,也從不同角度瞭解了資料庫系統的不同分類,並且瞭解到了許多不同功能場景的資料庫系統。為我們如何選擇資料庫系統已經增添了一份基礎知識。我們應該如何選擇一個適合的儲存方案呢?

原則

  1. 選擇是基於需求確定的。所以必須明確需求場景,然後按需求場景選擇適合的儲存方案。
  2. 沒有調查就沒有發言權。方案調研就是一個調查過程,需要先了解不同資料庫的基本特性,才能選擇合適的儲存方案。

基本場景

和前章資料庫系統的分類很相似。其實上面資料庫系統的分類一方面就是基於不同的使用場景才設計的,從而有不同實現的資料庫系統,從而有針對不同場景的特殊優化,從而逐漸形成了不同場景的特殊模型。

事務性,如 Mysql 這些是最常見的事務性系統使用的儲存方案,滿足 ACID,使用簡單。支援千萬級別資料級別的讀寫。分析性,適合 BI,資料包表、資料監控等資料服務系統。文件型,適合高度可變的資料模型,當你事先不知道你的資料看起來究竟像什麼樣子,文件型是一個不錯的選擇,文件型也適合點查詢多餘集合查詢的場景。圖資料庫,圖資料庫是一種很特殊的,新興的資料庫型別,它側重於分析解釋資料之間的相互關係而不是資料值本身,它適合推薦引擎、許可權訪問控制和地理資料等場景。時序性,時序性資料庫在資料分析,時序資料展示,監控領域使用比較多,它適合對大量時間型資料查詢、過濾、組合、聚合分析等。K-V 型,快取和固定 View 模式的資料展示,K-V 型的需要按查詢組合好儲存起來,這樣查詢時按 key 獲取即可。

讀寫

  • 是否需要寫事務
  • 順序讀寫還是隨機讀寫
  • 偏點查詢還是大量資料集分析查詢
  • 資料結構變化大,還是查詢結構變化大

資料量

資料量,需要考慮資料的數量,也需要考慮資料數量的增長速度,這樣就需要考慮資料庫的量級承載能力以及水平擴充套件能力。

資料用途

對臨時資料和重要的業務資料的儲存可以採用相對側重點不一致的方案。對資料的一致性要求的強弱也會影響資料儲存系統的選型。對資料事務的要求,對資料儲存時間的選擇也會不一樣。

可靠性

資料的可靠性即保證資料的可用的能力,可靠性與成本一般是權衡的一體兩面,需要對資料可用性的要求選用不同的儲存架構。

可擴充套件性

可擴充套件性表現在資料使用的可擴充套件和系統本身的可擴充套件上。

可維護性

  • 可運維性:方便運營團隊來保持系統平穩執行。
  • 簡單性:簡化系統複雜性,使新工程師能夠輕鬆理解系統。
  • 可演化性:後續工程師能夠輕鬆地對系統進行改進,並根據需求變化將其適配到非典型場景,也稱為可延伸性、易於修改性或可塑性。

學習和了解資料底層儲存,除了可以搭建良好的儲存架構是提供思路上的幫助,也可以讓我們學習到很多平時純業務開發接觸不多的底層技術實現。對底層技術的瞭解和掌握,又可以反過來讓我們更加了解我們的整個業務系統,對系統的合理性優化做出重要的選擇。也可以幫助我們實現自己的系統。

開源資料庫系統的良好的分散式架構,優秀的網路通訊,效能強勁的記憶體和磁碟訪問優化以及更多經典的資料介面和演算法都是值得我們學習和借鑑的。

四、知行合一

知是行的主意,行是知的工夫;知是行之始,行是知之成。—— 王陽明

這一章節將簡單講解一些資料庫系統的常見技術點。

系統架構

Master-Slave

Master-slave 架構可以說是最常用的資料儲存架構,關係型資料庫如:mysql,postgreSql,oracle,Nosql 諸如:MongoDb,訊息佇列如:Kafka,RabbitMQ 等都使用了這種架構。

master_slave

在整個系統中,Master 承擔寫任務,Slave 通過複製 Master 的資料保證與 Master 資料的一致性。Master 和 Slave 都可以承擔讀任務。Master 架構解決了資料的高可用問題(Slave 儲存了資料副本),也擴充套件了資料讀併發能力(多 Slave 同時通過讀請求)。

在 Master-Slave 架構中,單 Master 如果出現故障,就會導致這個資料庫系統不可用,這時就可以採用 Master-Master 架構,系統中同時存在多個 Master 節點,但是,多個 Mater 節點並不同時提供寫服務,同時只會存在一個可寫的 Master,另一個 Master 作為備機存在,只有當其他 Master 不可用時才會被選舉稱為 Master 節點提供寫服務,作為備機的 Master 是可以提供讀服務的。這種架構的只解決了單 Master 節點的高可用問題,並沒有解決單 Master 負載過大的問題,這裡之所以只有一個 Master 提供寫服務,是為了保證寫資料的一致性問題。

資料一致性

我們將同一份資料在不同資料節點上的儲存稱之為副本。只要系統中資料存在多個副本,就會有資料一致性問題。如何保證資料多副本的一致性,一直以來都是分散式系統的最大挑戰。多節點資料同步,一般採用複製方式,從節點複製主節點的資料,多節點之間相互複製等等,但無論採用哪種方式,都無法避免不一致的情況。

資料一致性可以分為最終一致性強一致性。強一致性模型能夠允許你的單服務程式移植到分散式節點叢集上並且不會發生任何錯誤。強一致性往往通過犧牲系統可用性來達到,在寫入資料時,如無法保證多副本一致,則失敗。最終一致性模型中,當停止改變數值的一段不確定的時間後,所有的複製集將會最終保持一致。這表明,在這段時間之前,資料副本在某種情形下是不一致的,但資料最終會達到一致,最終一致性意味著“收斂”,即預期所有的副本最終會收斂到相同的值。

在資料收斂過程中,為保證最終資料的一致性性,還有許多問題需要解決。如系統間的時序問題,原子提交問題,共識問題。

CAP 理論

定理:一個分散式系統不可能同時滿足 consistency、availability、partition tolerance 這三個基本需求,最多同時滿足兩個。

  • consistency 一致性:所有節點同一時刻看到相同資料
  • availability 可用性:節點失敗不阻止影響正在執行的節點的工作
  • partition tolerance 分割槽容錯:即使出現資訊丟失或網路、節點失敗,系統也能繼續執行(通過複製)

cap

這三種性質進行倆倆組合,可以得到下面三種情況:

  • CA:完全嚴格的仲裁協議,例如 2PC(兩階段提交協議,第一階段投票,第二階段事物提交)
  • CP:不完全(多數)仲裁協議,例如 Paxos、Raft
  • AP:使用衝突解決的協議,例如 Dynamo、Gossip

CA 和 CP 系統設計遵循的都是強一致性理論。不同的是 CA 系統不能容忍節點發生故障。CP 系統能夠容忍 2f+1 個節點中有 f 個節點發生失敗。

分割槽

p_r_mini

上面說副本只能保證資料的可用性。為提高大量資料集的讀寫能力,我們可以將資料拆分成不同的分割槽分開處理,這種技術稱之為分片

分片,即將資料集分割成相互獨立的小資料集,減少因資料集增長而帶來對單個節點的壓力。資料分片有以下好處:

  • 提高效能:限制分割槽中資料量的大小,降低資料壓力
  • 提高可用性:資料之間相互獨立,不同分割槽之間失敗互不影響,允許失敗節點的存在

分割槽自然也會帶來一些問題,首先需要考慮的就是如何分割槽的問題

  • 基於關鍵字區間:將資料按關鍵字劃分為不同區間,將相同區間的資料寫入同一個節點。比如使用者資料 id 分佈在[1-1000000]之間,需將資料分佈到 10 個節點上,可以將資料劃分成十個區間:
  • 關鍵字雜湊分割槽: 通過 Hash 演算法計算分割槽號,將資料寫入相應分割槽號的分割槽中。

資料分割槽帶來的負載傾斜熱點問題:由於資料的不確定性,資料關鍵字計算出來的分割槽儲存可能集中在某幾個區間內,這樣就可能導致某些節點資料明顯多餘其他節點,這種資料集中於某個節點的情況就是資料熱點。由於資料熱點的出現,整個系統的負載將傾斜到這些節點上,造成分割槽間的負載不均衡,這就是負載傾斜問題。

去中心化:Dynamo

Dynamo 是 Amazon 的一個分散式儲存。Amazon 發表了一篇論文 Dynamo: Amazon’s Highly Available Key-value Store 講解 Dynamo 架構,使得 Dynamo 稱為許多資料儲存系統借鑑的架構。

Dynamo 基於一些眾所周知的技術實現了可擴充套件性高可用性

  • 資料通過一致性雜湊演算法進行分割槽和複製(partitioned and replicated)
  • 通過物件版本化(object versioning)實現一致性
  • 副本之間的一致性由一種仲裁的技術(quorum-like technique)和一個去中心化的副本同步協議(replica synchroni protocol)來保證
  • 基於 gossip 協議進行分散式故障檢測和成員檢測(membership)協議管理節點

Dynamo 是一個完全去中心化的系統。

no_master

向 Dynamo 新增或移除儲存節點不需要人工 partition(調整雜湊節點)或 redistribution(在節點之間重新平衡資料分佈)

Dynamo 採用最終一致性方案。

生產級別的儲存系統的架構是很複雜的。除了最終儲存資料的元件之外,系統還要針對以下方面制定可擴充套件和健壯的解決方案:負載均衡、成員管理(membership)、故障檢測、故障恢復、副本同步、過載處理(overload handling)、狀態轉移、併發和任務排程、請求 marshalling、請求路由(routing)、系統監控和告警,以及配置管理。

下表總結了 Dynamo 使用的這些技術及每項技術的好處。

table-1

Partition
  • 技術:一致性雜湊
  • 好處:增量可擴充套件性
寫高可用
  • 技術:讀時協調(解決衝突)的向量時鐘(vector clocks with reconciliation during reads)
  • 好處:version size 和更新頻率(update rates)解耦
短時故障處理
  • 技術:寬鬆的選舉和 hinted handoff(移交給其他節點處理,附帶提示資訊)
  • 好處:部分副本不可用時,仍然可以提供高可用性和永續性
持久(permanent)故障恢復
  • 技術:基於 Merkle tree 的逆熵(anti-entropy)
  • 好處:後臺同步版本不一致的副本
成員管理和故障檢測
  • 技術:基於 Gossip 的成員管理協議和故障檢測
  • 好處:保持了架構的對稱性,無需一箇中心元件(centralized registry)來儲存成員和節點狀態等資訊

分散式資料庫 Cassandra 就是 Dynamo 的典型實現。

有主架構:Bigtable

Bigtable 是 google 開源的資料庫系統。Bigtable 是典型的有主架構

Bigtable 主要由三個元件構成:

  1. 一個客戶端庫,會連結到每個客戶端
  2. 一個 master server
  3. 多個 tablet server

master 負責:

  1. 將 tablet 分配給 tablet server
  2. 檢測 tablet server 的過期(expiration)及新加(addition)事件
  3. 平衡 tablet server 負載
  4. 垃圾回收(GC)
  5. 處理 schema 變動,例如 table 和 column family 的建立

BigTable 的 Master 只負責後設資料的管理,Table Server 負載自身管理的 Table 的讀寫功能,客戶端只想 Master 同步後設資料,資料不經過 Master 節點,直接和 Table Server 通訊。因此,BigTable 中 Master 節點的負載很低。

有主架構中,Master 承擔的能力也會不一致,比如在下圖架構中,Master 只承擔 Coordinate 功能,管理後設資料和 Node 節點,Client 獲取 Mata Data,直接和相應的資料節點通訊。

master_worker1

在下面架構中,Client 不直接和 Data Node 節點通訊,而是和 Master 通訊,Master 更加相關後設資料將請求轉發給對應的 Data Node:

master_work2

Coordinate-Worker 架構是很多分散式資料庫採用的架構,有興趣的同學可以看看筆者之前講解的 《Druid 的架構設計》

索引

資料庫系統的索引,就是用來提高資料檢索效率的。資料庫系統的資料記錄儲存在磁碟中,如果沒有索引,要從磁碟中檢索相應的記錄,就需要掃描所有的資料段,這種 O(N) 的訪問效率和全磁碟的掃描自然不可能在真正的資料庫系統中使用。為提高資料檢索能力,資料庫系統引入索引技術,為磁碟上的資料記錄做一個索引結構,這些索引放在記憶體中,或按塊儲存在磁碟上(但只需要少數幾次磁碟讀取就可以讀入記憶體中),這樣檢索一個資料先從記憶體索引中查詢到對應的 Key 或磁碟位置,然後從磁碟中讀取記錄。

這裡索引做了兩個事情:

  • 將大量磁碟檢索變成記憶體檢索
  • 通過特定的資料結構可以提高記憶體檢索的效率,改變 O(N) 這種低效率的檢索

HASH 索引

hash_index

HASH 即雜湊表,類似 Java 的 HashMap 資料結構,Key-Value 格式。假設我們在記憶體內維護一個 HashMap Index,key 為資料的鍵,Value 是資料在磁碟的儲存偏移量。

  • 獲取資料時,先從記憶體 Map 獲取對應資料的磁碟 offset,然後讀取磁碟的資料。
  • 寫資料時,先將資料追加寫入磁碟,然後更新記憶體 HashMap Index。

Hash 索引聽起來過於簡單,但確實是一種可行的索引方法。Hash 索引簡單高效,查詢效能 O(1),更新也高效,當時也有明顯的缺點,比如:

  • 需要將整個雜湊表放入記憶體,這對於大資料量來說記憶體耗費將不可承受的。
  • 只能進行精確查詢。
  • 不能實現範圍查詢。

B-Tree 索引

B-trees 索引始見於 1970 年,經受了長久的時間考驗,時至今日,它仍然時幾乎所有關聯式資料庫中的標準索引實現,許多非關係型資料庫也經常使用。

瞭解B-trees索引先從二叉搜尋樹開始。二叉搜尋樹是一種特殊的二叉樹,它滿足以下條件:

  • 左子樹小於父節點
  • 右子樹大於父節點

BST

上圖是一個搜尋二叉樹,如果我要查詢 208 這個 key:

  • 先從根節點開始,即 136。比較 208 > 136,下一步應該從根節點的右子樹查詢
  • 398 > 208,繼續搜尋 398 的左子樹
  • 250 > 208,繼續搜尋 250 的左子樹
  • 200 < 208,繼續搜尋 200 的右子樹。
  • 200 的右子樹並不存在,因此資料中沒有 208,查詢結束

讓我們再查詢 40:

  • 從根節點 136 開始,136 > 40,繼續搜尋左子樹
  • 80 > 40,繼續搜尋左子樹
  • 40 = 40,節點存在,從節點中獲取資料 id,然後可以更加資料 id 查詢對應的資料

在索引結構中,樹的每個Node包含一個 key 值,一個資料指標(或資料 id、磁碟 offset 等)

二叉搜尋樹的時間複雜度是 log(N),這是一個不錯的結果。

二叉搜尋樹依舊只能獲取特定的值,如果我需要進行範圍查詢,即查詢兩個數之間的所有資料,就需要去遍歷樹中的每一個節點,去判斷節點是否在此範圍內,這種情況下,時間複雜度又下降到了 O(N)。因此我們需要改進上面的資料結構,現代大多數資料庫都才有一種改進的二叉搜尋樹—— B+Tree。

B+tree

B+Tree 在二叉搜尋樹的基礎上新增如下特徵:

  • 僅僅在葉子節點儲存索引資訊(關聯表資料的資訊)
  • 其餘節點僅僅用於查詢到最終的葉子節點(葉子節點包含了所有的 key)

在 B+Tree 中,每個 Key 會存在兩個 Node,所有中間節點只用於輔助檢索最終正確的葉子節點(葉子節點才包含關聯資料的資訊)。

讓我們嘗試從上面的 B+Tree 中搜尋出[40, 100]之間的節點:

  • 採用和二叉搜尋樹一樣的方式,我們只需要搜尋 40 這個節點(或搜尋出最接近 40 的節點,當 40 的節點不存在時)
  • 然後在葉子節點連結串列中往下追溯,知道超過 100

假設樹中共有 N 個節點,追溯了 M 個葉子節點,那麼可以得出,此次搜尋的時間複雜度是:log(N) + M。相對於之前的 O(N) 的二叉搜尋樹有以下好處:

  • 不需要讀取整棵樹,這樣可以減少讀取磁碟的次數(索引資料一般按頁儲存在磁碟上)
  • 大多數情況下 M (約等於檢索範圍)會遠小於整個資料量 N,因此這裡的 O(M) 時間複雜度大多數情況下遠小於 O(N)

任何事情都是雙面的。B+Tree 索引帶來的檢索優勢,必然會有其他方面的損失。這主要體現在刪除資料時。因為葉子節點類似於連結串列結構,刪除一個節點需要從 header 開始遍歷,時間複雜度是 O(N)。

B+Tree 索引具有比較好的檢索效能,為了減少磁碟訪問次數,大多數索引系統的 B+tree 都只有 3- 4 層,因此 B+Tree 索引能夠承載的節點數是有限的。B+Tree 在更新節點是需要自排序自平衡,這需要額外的效能消耗,B+Tree 的插入和刪除時間複雜度是 O(log(N))。這就是為什麼在使用資料庫時不建議索引欄位都新增索引,而是充分考慮具體情況,在需要的欄位上新增索引,否則索引太多會影響表的insert\update\delete操作效能。

LSM

B+Tree 是基於頁的索引引擎,B+Tree 的資料儲存本身是無序的,其建立索引的思想是在記憶體中維護一個 key 與資料磁碟位置的對應關係,並保證這個記憶體資料結構有序。有一種基於檔案的儲存引擎,它將資料劃分成檔案段,並保證資料在磁碟檔案段中有序,因此,這種儲存引擎並不需要在記憶體中維護所有資料的順序表,只需要在記憶體中維護一個稀疏的索引結構,每次從記憶體索引中搜尋到的資料並不是具體到每條資料,而是一個檔案段(或檔案塊),然後將這些有序的資料讀入記憶體,再按序獲取具體的資料。(如何保證寫入資料有序?)

LSM(Log-Structured Merge-Tree),就是這樣一種索引結構。LSM 的架構如所示:

lsm

SSTable: LSM 的磁碟檔案,稱作SSTable(Sorted String Table)。望文得意,LSM 儲存在磁碟中的檔案,資料也是按 Key 排序儲存的,這樣就可以解決上面講到的資料量大了之後無法將資料全部索引到記憶體中的問題。如果磁碟檔案也是有序的,那麼記憶體索引可以採取”稀疏索引“(Sparse Index),可以每一段記錄一個索引,將資料邏輯上分成多個block,稀疏索引只需要記錄每個block的偏移量,每條資料通過遍歷block實現。這樣索引量將大大減小。

Memtable: LSM 的記憶體結構叫做MemtableMemtable是一個有序結構,同樣可以採用樹結構,可以用跳錶。LSM 寫資料時,只需要寫入記憶體中的Memtable,當Memtable到達一定量之後,會非同步刷入磁碟,就是上面的SSTable

Immutable Memtable: 在資料從記憶體Memtable刷入SSTable時,為避免讀寫鎖導致的效能問題,LSM 會在記憶體中 copy 一份immutable Memtable表,顧名思義,這個資料結構不可改變,新寫入的資料只會寫入新的Memtableimmutable Memtable供刷盤執行緒讀取,查詢資料的請求也可以訪問這個資料結構,這樣如果資料在記憶體中,就不需要訪問磁碟,可以提供資料查詢的效率。

WAL: write ahead log,預寫日誌,關於 WAL,可以參考我之前的文章《你常聽說的 WAL 到底是什麼》。在 LSM 中,在資料刷入磁碟前,為防止異常導致資料丟失,LSM 會先將資料寫入 WAL,然後寫入 SSTable,系統重啟時,LSM 會從 WAL 中回溯 SSTable,當寫完一個 SSTable 時,LSM 會清理掉過期的 WAL 日誌,防止 WAL 過量。

LSM 如何寫入資料:

  1. 寫入 WAL
  2. 寫入 Memtable
  3. Memtable 達到閾值時,複製 Imutable Memtable
  4. 非同步刷入磁碟

LSM 如何刪除資料: 為保證順序寫磁碟,LSM 不會去直接刪除資料,而是通過寫一條 delete 標識來表示資料被刪除,資料只有在被 Compact 時才會被真正刪除。

LSM 如何讀取資料: LSM 讀取資料將從memtableimutablesstable依次讀取,直到讀取到資料或讀完所有層次的資料結構返回無資料。所以當資料不存在時,需要依次讀取各層檔案。LSM 可以通過引入布隆過濾器來先判斷一個資料是否存在,避免無效的掃檔案。

密集索引(dense index) 和 稀疏索引(spare index):密集索引為每條資料對應一個索引記錄,稀疏索引一般只索引資料塊或檔案,是跳躍式的。因此稀疏索引比密集索引更節省空間。

壓縮

資料壓縮對資料庫系統中 I/O 效能的影響相當明顯,它可以減少磁碟空間使用降低頻寬使用提高吞吐量。資料庫系統中的資料儲存索引儲存資料轉換資料備份網路通訊都會用到相應的壓縮技術。當將資料庫壓縮引入實時資料庫時。壓縮演算法必須提供高壓縮比才能實現高資料儲存,壓縮演算法必須足夠快,才能在實時資料庫中實現實時記錄和查詢功能。

壓縮過程一般由兩個獨立的部分組成,建模編碼。建模定義輸入流中不同符號的特徵。模型儲存有關符號在資料中出現的頻率的資訊,即符號概率。編碼是壓縮過程的第二部分,它根據模型提供的概率為不同符號建立一組程式碼,從而產生資料的壓縮版本。將更頻繁地出現的符號與較短的程式碼詞和較長的稀有符號互換。資料的均勻性會影響大多數壓縮演算法的壓縮比,但對壓縮速度沒有任何影響。因此,為了達到更好的壓縮效能,壓縮演算法是專門為資料的每個部分設計的,因此不同的壓縮演算法對不同型別的,不同量級和不同組合的資料的壓縮效果是不一致的。也因此大多數支援資料壓縮的資料庫系統都會提供多種不同的壓縮演算法讓使用者根據自身資料情況自由選擇。

壓縮演算法可以分為以下兩大類:

  • 有失真壓縮:有失真壓縮會重構原始資料。所以讀取的壓縮後的資料是不完整的。這種壓縮方式通常用在音訊、視訊等流檔案的壓縮中。
  • 無失真壓縮:無失真壓縮不影響壓縮資料的原始值。通常使用在文字,數字等資料的壓縮中。

壓縮應該考慮什麼問題:

  • 大小:壓縮後的檔案大小,即壓縮比。當使用壓縮時,本就是為了降低資料大小,所以壓縮演算法的壓縮比是首要考慮的問題。
  • 速度:壓縮速度會影響資料的讀寫效率,這對實時系統來說尤為重要,速度和大小是 trade-off 的兩面,必須充分考慮具體的使用場景。
  • 資源: 壓縮節省了磁碟和寬頻,但是會增加 CPU 和壓縮時的記憶體使用。所以壓縮時的資源耗損情況也需要考慮。

下面列舉一些常見的壓縮演算法或方法(Gzip、Bzip2、LZMA、XZ、LZ4、LZO),並做相應的對比:

測試條件:

  • Intel Core i5 CPU 750 at 2.67GHz
  • 8GB of DDR3 memory
  • tmpfs as ram disk
  • Linux kernel 3.3.2, gentoo amd64
  • CFLAGS: -pipe -O2 -g -floop-block -floop-interchange -fgraphite
  • bzip2-1.0.6-r3, xz-utils-5.0.3, gzip-1.4

檔案壓縮對比結果(原資料: 445M):

c-c

壓縮比對比:

c-r

壓縮耗時對比:c-t

各大資料庫系統多多少少都會用到壓縮技術來降低資料儲存空間,來提高系統效能,以下列舉一些資料庫系統使用到的壓縮技術:

  • Google 在 BigTable 和 MapReduce 中使用 Snappy 壓縮資料和網路傳輸。
  • SQL Server 使用 XPRESS 演算法壓縮備份資料。
  • Oracle 使用自實現的 Oracle Advanced Compression 演算法壓縮資料。
  • MySQL 使用 LZ77 演算法壓縮 InnoDB 的表。
  • Kafka 同時支援 gzip 和 snappy 和 lz4 演算法,並對預設的 lz4 做了特定的優化。
  • Druid 使用 lz4 壓縮資料。

數值壓縮:delta-of-delta

數值壓縮經常用於壓縮列式儲存的數字列。前面我們講到過,列式儲存將每列的資料儲存在相鄰的位置。這樣的儲存結構利於壓縮資料,下面我們講一下在許多列式儲存中使用的 Delta 數值壓縮技術。

delta of delta

如圖所示,假設有 6 個原始數值(73、300、302、332、343、372)。在未壓縮之前,每個數值佔用 4 個位元組,6 * 4 = 24 共佔用 24 個位元組。Delta 壓縮演算法不儲存原始的數值,而是先確定一個數字(一般取第一個數值),後面的數值是相對於第一個數值的差值,如圖第二行所示得到的資料集為(73、227、3、30、11、29)。因為最大的差值是 227,因此只需要一個 byte 就可以表示,因此之前需要使用 4 個位元組儲存的每個數值,現在只需要使用 1 個位元組。為了儲存對應的差值相關元描述資訊,需要額外的 1 位元組儲存這些資訊,上圖還將資料分塊儲存,因此最終需要的位元組數是 7 個。這樣相對於原始的 24 位元組節約了將近 3 倍的空間。

其實上圖就是 Elasticsearch 底層使用 Lucence 的原理。

delta-of-delta 適用於數值型別資料的壓縮,且對資料量大並且資料集中的資料壓縮才有效果。如果資料集比較小,且比較稀疏,資料的最大差值已經和資料值可以表示的最大值相差不大,那麼壓縮的意思便存在。

讀寫

資料儲存系統就是一個與磁碟和網路打交道的系統,所以資料儲存系統在這方面的優化可謂精益求精,比如非同步IO緩衝批量讀寫append寫資料按磁碟頁讀寫資料預讀資料磁碟記憶體對映技術等等。

非同步

與非同步 IO 對應的是同步 IO,即每進行一次 IO 操作,需要等待此次操作結束才能繼續接下來的操作,這樣在大量併發請求情況下,IO 的效率將會大大降低,磁碟 IO 和網路 IO 在併發量大的情況下采用非同步 IO 可以明顯提供效率。

Mysql 的 InnoDB 也採用 AIO 提高效率,InnoDB1.1.x 之前,AIO 的實現通過 InnoDB 儲存引擎中的程式碼來模擬實現,從 InnoDB1.1.x 開始,提供了核心級別的 AIO 支援,稱為 Native AIO。在 InnoDB 儲存引擎中,read ahead 方式的讀取都是通過 AIO 完成,髒頁的重新整理,即磁碟的寫入操作則全部由 AIO 完成。

在 Kafka 中,Broker 的資料磁碟落地,都是採用的 Java NIO 的方式處理的,這是 Java 的非同步 IO 的實現,Java NIO 可以提供資料寫入的併發效能。

緩衝

緩衝技術是為了協調吞吐速度相差很大的裝置之間資料傳送而採用的技術。

buffer

在資料到達與離去速度不匹配的地方,就應該使用緩衝技術。緩衝技術好比是一個水庫,如果上游來的水太多,下游來不及排走,水庫就起到“緩衝”作用,先讓水在水庫中停一些時候,等下游能繼續排水,再把水送往下游。

將緩衝和批量傳送結合,可以提高資料在在網路和磁碟中的寫入速率。在資料寫入網路或磁碟時,先設定一個緩衝池,當資料達到一定的數量或緩衝時間超時時,在將資料批量傳送出去,可以減少請求併發,也可以減少請求額外資料帶來的頻寬和磁碟消耗。

在 Mysql 中,Innodb 在多個地方使用緩衝來提升寫入效能。比如插入緩衝,將多個插入請求合併到一個操作中,這樣可以將之前的一些非順序寫入變成相對的順序寫入,以提高寫入效率。另一方面也可以按磁碟物理頁寫入資料,這樣充分利用了磁碟的寫入特性。

在 Elastisearch 和 Kafka 的客戶端中,都採用了緩衝批量寫入的功能來減少寫入併發情況。

磁碟

在磁碟的讀寫優化上,經常可以看到以下技術:

  • 按磁碟頁讀寫資料:磁碟讀寫的單位是。為了減少讀寫資料時磁碟訪問頻率,資料庫系統通常都按頁讀寫資料。

  • 預讀資料:一些資料庫系統認為使用者訪問了一部分資料,那麼在它相鄰的放的資料下次被訪問的可能性也很大,所以會預先讀取多個頁的資料。

  • 磁碟記憶體對映(MMP):即盤扇區對映到程式的虛擬記憶體空間的過程。MMP 讀寫資料時跨過了頁快取,減少了資料的拷貝次數;實現了使用者空間和核心空間的高效互動方式;可以緩解系統記憶體不足的壓力。

本文對各種技術淺嘗輒止,其實每一個技術點都可以深入講解,感興趣的同學請持續關注我們後期的文章。

參考:

《Designing Data-Intensive Applications》
《Database Software Market:The Long-Awaited Shake-up》
《Distributed systems for fun and profit》
《How does a relational database work》
《七週七資料庫》
《Mysql 技術內幕——InnoDB 儲存引擎》
《資料庫系統概念》
《Dynamo: Amazon's Highly Available Key-value Store》
http://arthurchiao.art/blog/amazon-dynamo-zh/
https://zhuanlan.zhihu.com/p/35622907
https://baike.baidu.com/
https://hbase.apache.org/
https://zh.wikipedia.org/
https://clickhouse.tech/
https://druid.apache.org/
https://www.elastic.co/

希望讀者 「點贊」「分享」「在看」三連就是最大的鼓勵。

後臺回覆 “加群” 進入專屬技術群一起成長

碼哥位元組

相關文章