百萬節點數據庫擴展之道(1): 傳統關系型數據庫

本博客在http://doc001.com/同步更新。

本文主要內容翻譯自MySQL開發者Ulf Wendel在PHP Submmit 2013上所做的報告「Scaling database to million of nodes」。翻譯過程中沒有全盤照搬原PPT,按照自己的理解進行了部分改寫。水平有限,如有錯誤和疏漏,歡迎指正。

本文是系列的第一篇,本系列所有文章如下:

  • 百萬節點數據庫擴展之道(1): 傳統關系型數據庫

前言

今天的數據庫世界讓人倍感迷惑。回想十年前進行Web開發,可選擇的數據庫還極為有限。然而現在,除了傳統的數據庫外,有150+的NoSQL數據庫供君選擇。這期間究竟發生了什么翻天覆地的變化,使得單節點數據庫逐步演變為數百萬節點的全球數據庫?為了解答這個疑惑,本文將引導讀者回顧這些年數據庫的那些事兒。

在正文之前,讀者應當明白,與傳統開發相比,在海量數據庫系統上進行應用開發存在一定區別,而開發海量數據庫本身則有天壤之別。

數據庫的出現

1960年代——黑暗歲月

有誰曾經接觸過大型機的日常數據交換?2000年以后呢?

1960年代的數據存儲示意圖

這個時候的應用數據被存儲在磁帶上,數據庫還未被發明出來。公司在生產環境使用的每一個應用都有自己的數據存儲方式。開發者使用十六進制編譯器來解讀數據,制作報告時需要花費了很多額外的時間從多個應用抽取數據。從這個安全問題頻出的年代看,這是怎樣天真美好的歲月,應用安全到只有知悉所有實現細節才能保持運行的地步,就算你拿到數據也白搭。(Zz..囧)

1970年代——神跡初顯

這個時代的數據存儲的目標包括:

  • 數據的內在視圖(存儲細節)和外在視圖(外在表現)分離
  • 中心式存儲
  • 數據一致性,高效的數據訪問
  • 多用戶支持,訪問控制
1970年代數據庫示意圖

(關系型)數據庫終于被發明了出來,成為解決一個公司內部不同應用的數據共享問題的有效手段。

一個數據庫系統必須確保數據一致性,并提供諸如訪問控制之類的手段保證多應用、多用戶環境下的數據安全。當然,數據的存儲效率也極為關鍵。

使用數據庫帶來的一個好處就是,用戶看到的數據外在視圖和內在視圖是完全隔離的。用戶根本不需要關心數據的存儲細節。不管內部的存儲方式如何,數據庫使用一致的SQL語言提供數據。

基本數據概念

什么是數據?

我們知道數據一致性是一個數據庫系統的核心問題,然而,究竟什么是數據?
顯然,所有的數據都會有一個類型和一個值域。例如,一個字符串是一組字母的序列,一個數卻只包含數字,它們的類型就是不一樣的。

不同類型的數據示意圖

操作符(operator)與數據結構(data structure)

數據可以分為標量(scalar)數據類型和非標量(non-scalar)數據類型兩類。一個標量數據類型只保存唯一的數據項,字符串、整數就是典型的標量數據類型。與之相反,一個非標量數據類型是包含多個數據項的類型,類就是典型的非標量數據類型。

操作符用于操作數據類型,每一個數據類型都有一組適用的操作符集合。例如,類的構造器(constructor)是用于初始化類成員的操作符。

數據結構規定了數據的組織、存儲方式。一些數據庫系統允許自定義數據結構。一個對象(object)由一個數據結構和定義在其上的一組操作符組成,而POD(plain old data)數據結構就只包含數據結構。

操作符與數據結構示意圖

狀態(state)

在一個程序中,數據是動態的。數據的狀態隨時間發生變化,修改操作會導致數據狀態變化,通常這些狀態遵循一定的規則。

數據狀態示意圖

數據庫的數據模型

數據庫的一個數據模型定義了數據庫的數據結構、數據的存儲方式,和全局操作符。數據庫會長期運行,全局操作符實際上規定了可能發生的狀態變化。

數據庫內部使用數據庫模式(schema)描述一個特定數據模型。很少有數據庫以無模式的方式存儲數據,當然,很多的NoSQL系統的模式非常靈活。

常見數據模型分為四種:

  • 關系模型(Relational data model)
  • 文檔模型(Document model)
  • 鍵值模型(Key-Value model)
  • 寬列模型(Wide columnar model)

后三種都屬于NoSQL的范疇。接下來將簡要介紹這四種模型。

關系型數據庫

盡管關系模型的缺點被一些NoSQL所改進,但是除了這些缺點,我們不應該忘記關系模型的優點。至少到現在,關系模型仍然占有統治地位。

NoSQL的革新本質在于數據模型本身,而其它的改進多流于表面,完全可以被關系型數據庫借鑒。例如,關系型數據庫也可以實現HTTP接口;關系型數據庫也可以提供更低層次的訪問接口來繞開SQL;關系型數據庫也可以簡化管理體系;等等。

模式設計

模式設計是關系型數據庫應用開發的第一步,包含3個步驟:

  1. 提煉出需要存儲的信息。
  2. 創建實體-關系(E-R)模型:
  • 實體:主題、事情、物體
  • 屬性:對實體的描述、信息項、規則
  • 候選鍵:能夠唯一標識一個實體的屬性集合
  • 關系:實體間的關聯,1:1、1:n、n:m
  1. 將E-R模型轉化為物理數據模型:
  • 網絡模型、關系模型、分層模型
  • 關系模型:表、屬性、主鍵
  • 關系模型:應用數據庫規范化法則

數據庫規范化(database normalization)

數據庫規范化的目的是降低數據表的冗余和依賴程度。數據庫規范化有很多范式,其中第一范式(first normal form, 1NF)規定:

  • 一個關系的所有屬性都是不可再分的原子數據項
  • 每一個屬性只包含唯一的值
    該范式禁止創建嵌套的表結構,例如下圖中,在一張博客發表表內嵌套一個博客評論列表。嵌套的表結構在NoSQL中比較常見。
嵌套表示意圖

為了不破壞1NF,同時滿足一些必要的嵌套需求,SQL:99和SQL:2003引入了非原子數據結構。SQL:99增加了ROW和ARRAY,SQL:2003增加了MULTISET。遺憾的是,很多關系型數據庫都沒有實現這些數據類型。進一步說,如果這些數據類型得到了實現,關系型數據庫連接(join)操作將會變得非常高效,鍵值數據庫和文檔數據庫在這方面的優勢也就不那么明顯了。

查詢

關系型數據庫的查詢通過SQL語言進行,其理論基礎是關系代數。

ACID事務

關系型數據庫的事務滿足ACID:

  • 原子性(atomicity)
    • 一個事務的操作要么全做,要么全不做
    • 在各種失效情況下也予以保證:掉電、崩潰...
  • 一致性(consistency)
    • 事務只能導致數據庫從一個有效狀態轉變到另外一個有效狀態
    • 已定義的規則不會被違反:約束、觸發器...
  • 隔離性(isolation)
    • 并行執行的事務與串行執行的效果等效,不會互相干擾
  • 持久性(durability)
    • 一旦事務提交,就不可撤銷
    • 在各種失效情況下也予以保證:掉電、崩潰...

ACID反映了數據庫管理系統(database management system,DBMS)設計和開發的目標。DBMS不僅僅保證數據被正確組織(數據模型,模式),保證數據被輕松訪問(關系代數、SQL),也需要保證多用戶環境下的數據安全。

在RDMS事務中,用戶的工作要么全做,要么都不做,不存在中間狀態。事務不會破壞任何已定義的規則,在完成時保證數據庫仍然處于一個已定義的一致狀態。事務在被提交前,不會被其它并發的事務妨礙。事務一旦提交,其結果永遠不會丟失。

并發控制

假設兩個事務同時想修改同一個數據項,需要保證它們的修改不會相互沖突。這個工作由并發控制(concurrency control)算法來完成。

并發控制算法的分類如下圖所示:

并發控制算法分類

這張圖是從原PPT翻譯得到的,對該分類有疑問的請參考其它文獻。

并發控制算法可以分為悲觀算法和樂觀算法兩大類。悲觀算法在事務開始前就檢查沖突數據,提前鎖定,使事務訪問順序化。樂觀算法將沖突檢查推遲到最后進行,如果沖突,則回滾事務。

隔離級別

ANSI/ISO SQL定義了若干隔離級別,隔離級別會影響并發控制算法的效率:

  • 可序列化(serializable)
    • 最高級別的隔離,在事務期間對沖突數據的讀寫保持范圍鎖(range lock),即沖突事務順序進行
  • 可重復讀(repeatable read)
    • 沒有范圍鎖,可能存在「幻影讀(phantom read)」現象
  • 授權讀(read committed)
    • 可能發生「不可重復讀(non-repeatable read)」
  • 未授權讀(read uncommitted)
    • 允許「臟讀(dirty read)」

幻象讀:一個事務中,兩個完全相同的查詢語句執行得到不同的「結果集」。在下圖的例子中,事務1的第2次查詢語句讀到了事務2新提交的數據。

幻象讀

不可重復讀:在一次事務中,「一行數據」獲取兩遍得到不同的結果。在下圖的例子中,事務2提交成功,因此它對id為1的行的修改就對其它事務可見了,與事務1之前已經從這行讀到了另外一個「age」的值不同。

不可重復讀

臟讀:當一個事務允許讀取另外一個事務修改但未提交的數據時,就可能發生臟讀。在下圖的例子中,事務2修改了一行,但是沒有提交,事務1讀了這個沒有提交的數據。現在如果事務2回滾了剛才的修改或者做了另外的修改的話,事務1中查到的數據就是不正確的了

臟讀

物理層面

關系型數據庫將記錄存儲在「頁(page)」中,每一個頁是4~32KB大小的連續內存區域。一個頁可以包含一個或多個記錄。如果單個頁不能存儲下一個記錄的全部數據,那么將使用額外的溢出頁(overflow page)。為了優化訪問效率,關系型數據庫使用B-tree或其衍生數據結構將頁按序存儲在磁盤上。如果數據的實際存儲順序與一個索引的順序一致,那么這個索引是一個聚集索引(clustered index)。例如,InnoDB就使用聚集索引按照主鍵來組織數據表。聚集索引有助于獲得更高的順序搜索性能。

連接(join)

考慮一個數據表連接操作r?s(r、s分別是兩個數據表),常見的執行策略包括:

  • 嵌套循環(nested loop)
    • 通過兩層嵌套循環完成,首先掃描表r,每讀到一條記錄,就去掃描表s以查找符合要求的記錄
    • 算法復雜度O(nr*ns),其中nr和ns分別是表r和表s中的記錄數量
    • 對索引和連接條件無任何要求
  • 塊嵌套循環(block nested loop)
    • 嵌套循環的一個變種,以塊為單位而不是以記錄為單位處理關系
    • 相比嵌套循環,能夠減少從硬盤傳輸數據的次數,因此,效率有所改進
  • 索引嵌套循環(indexed nested loop)
    • 如果內層嵌套的被連接表的連接屬性上有索引,則可以利用索引來優化符合要求記錄的查找
  • 歸并連接(merge join,又稱排序-歸并-連接,sort-merge join)
    • 對連接的兩個表按公共屬性排序,利用歸并排序算法尋找公共屬性相同的符合條件的記錄
    • 可用于計算自然連接和等值連接
  • 散列連接(hash join)
    • 將連接的兩個表按照公共屬性使用相同的hash函數將記錄映射到同一空間,再對相同hash值的記錄做匹配
    • 實際的實現只對一個較小的表建hash查找表,另外一個表直接匹配記錄
    • 可用于計算自然連接和等值連接

原PPT只提及了nested loop和hash join,這里根據其它材料進行了補充。

關系型數據庫架構擴展

盡可能地緩存

緩存是提高數據庫性能的一個有效手段。

數據庫保存的數據有狀態的(stateful),且為硬狀態(hard-state),保證數據一致性(consistent);緩存保存的數據無狀態(stateless),且為軟狀態(soft-state),不保證數據一致性(inconsisteng)。

一個典型的緩存系統如下圖所示:

緩存示意圖

軟狀態維持一段有限的時間,在過期前需要重新刷新,否則自動失效。軟狀態可能比數據庫中的狀態滯后。相反,硬狀態一直存在,且一定正確。

緩存的無狀態特征允許緩存系統簡單地通過增加/減少資源來調整規模。

接下來的描述都是以MySQL為例展開的。

MySQL內置緩存

除了外部的緩存系統,MySQL本身也進行了兩項重要的改進:

  1. 內置了查詢緩存,該緩存的數據狀態與數據庫是一致的。
  2. 通過InnoDB提供了Memcache協議的底層記錄訪問接口,訪問速度比外部的Memcache更快,簡化了架構。
MySQL改進示意圖

MySQL主從

MySQL對可擴展性的答案是主從復制模式。在該模式中,所有的客戶端寫請求由唯一的master進行處理。master將操作記錄到二進制日志中,該日志被異步發送給slave們,slave們重放操作,完成數據的更新。slave可以處理客戶端的讀操作,前提是,應用可以容忍極短時間的數據滯后。該模式應該和緩存結合使用。

MySQL主從示意圖

在一個讀操作為主的環境(如Web應用)中,該模式具備極佳的橫向擴展能力,增加新slave的代價可以忽略不計。

在一個寫操作為主的環境中,該模式很難橫向擴展:

  • 只有一個master處理所有的寫操作,很容易單節點故障
  • 很難讀寫分離,即使使用PECL/mysqlnd_ms效果也不是很好
  • MySQL要努力啊...

更多MySQL演示資料參見slideshare

架構擴展的目標

每一個MySQL工程師都應該知道架構擴展的目標有這些:

  • 可用性(availability)
    • 節點失效不會對集群造成影響
  • 可伸縮性
    • 地理分布
    • 能夠根據用戶和數據的規模伸縮
    • 均衡讀/寫負載
  • 分區透明
    • 一切內部細節對客戶端都是透明的

MySQL架構擴展方案分類

根據欲達到的目標,可對現有的MySQL解決方案進行歸類。在這里,按照「事務執行的位置」和「節點同步發生的時間」將解決方案分為四類:

MySQL架構擴展方案

未完待續...

下一部分將介紹NoSQL理論和Amazon Dynamo,參見:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容