詳解高性能數據庫:讀寫分離

640

雖然近十年來各種存儲技術飛速發展,但關係數據庫由於其 ACID 的特性和功能強大的 SQL 查詢,目前還是各種業務系統中關鍵和核心的存儲系統,很多場景下高性能的設計最核心的部分就是關係數據庫的設計。

不管是爲了滿足業務發展的需要,還是爲了提升自己的競爭力,關係數據庫廠商(Oracle、DB2、MySQL 等)在優化和提升單個數據庫服務器的性能方面也做了非常多的技術優化和改進。但業務發展速度和數據增長速度,遠遠超出數據庫廠商的優化速度,尤其是互聯網業務興起之後,海量用戶加上海量數據的特點,單個數據庫服務器已經難以滿足業務需要,必須考慮數據庫集羣的方式來提升性能。

從今天開始,我會分幾期來介紹高性能數據庫集羣。高性能數據庫集羣的第一種方式是“讀寫分離”,其本質是將訪問壓力分散到集羣中的多個節點,但是沒有分散存儲壓力;第二種方式是“分庫分表”,既可以分散訪問壓力,又可以分散存儲壓力。先來看看“讀寫分離”,下一期我再介紹“分庫分表”。

讀寫分離原理

讀寫分離的基本原理是將數據庫讀寫操作分散到不同的節點上,下面是其基本架構圖。

640

讀寫分離的基本實現是:

  • 數據庫服務器搭建主從集羣,一主一從、一主多從都可以。

  • 數據庫主機負責讀寫操作,從機只負責讀操作。

  • 數據庫主機通過複製將數據同步到從機,每臺數據庫服務器都存儲了所有的業務數據。

  • 業務服務器將寫操作發給數據庫主機,將讀操作發給數據庫從機。

需要注意的是,這裏用的是“主從集羣”,而不是“主備集羣”。“從機”的“從”可以理解爲“僕從”,僕從是要幫主人幹活的,“從機”是需要提供讀數據的功能的;而“備機”一般被認爲僅僅提供備份功能,不提供訪問功能。所以使用“主從”還是“主備”,是要看場景的,這兩個詞並不是完全等同的。

讀寫分離的實現邏輯並不複雜,但有兩個細節點將引入設計複雜度:主從複製延遲分配機制

複製延遲

以 MySQL 爲例,主從複製延遲可能達到 1 秒,如果有大量數據同步,延遲 1 分鐘也是有可能的。主從複製延遲會帶來一個問題:如果業務服務器將數據寫入到數據庫主服務器後立刻(1 秒內)進行讀取,此時讀操作訪問的是從機,主機還沒有將數據複製過來,到從機讀取數據是讀不到最新數據的,業務上就可能出現問題。例如,用戶剛註冊完後立刻登錄,業務服務器會提示他“你還沒有註冊”,而用戶明明剛纔已經註冊成功了。

解決主從複製延遲有幾種常見的方法:

1. 寫操作後的讀操作指定發給數據庫主服務器

例如,註冊賬號完成後,登錄時讀取賬號的讀操作也發給數據庫主服務器。這種方式和業務強綁定,對業務的侵入和影響較大,如果哪個新來的程序員不知道這樣寫代碼,就會導致一個 bug。

2. 讀從機失敗後再讀一次主機

這就是通常所說的“二次讀取”,二次讀取和業務無綁定,只需要對底層數據庫訪問的 API 進行封裝即可,實現代價較小,不足之處在於如果有很多二次讀取,將大大增加主機的讀操作壓力。例如,黑客暴力破解賬號,會導致大量的二次讀取操作,主機可能頂不住讀操作的壓力從而崩潰。

3. 關鍵業務讀寫操作全部指向主機,非關鍵業務採用讀寫分離

例如,對於一個用戶管理系統來說,註冊 + 登錄的業務讀寫操作全部訪問主機,用戶的介紹、愛好、等級等業務,可以採用讀寫分離,因爲即使用戶改了自己的自我介紹,在查詢時卻看到了自我介紹還是舊的,業務影響與不能登錄相比就小很多,還可以忍受。

分配機制

將讀寫操作區分開來,然後訪問不同的數據庫服務器,一般有兩種方式:程序代碼封裝中間件封裝

1. 程序代碼封裝

程序代碼封裝指在代碼中抽象一個數據訪問層(所以有的文章也稱這種方式爲“中間層封裝”),實現讀寫操作分離和數據庫服務器連接的管理。例如,基於 Hibernate 進行簡單封裝,就可以實現讀寫分離,基本架構是:

640

程序代碼封裝的方式具備幾個特點:

  • 實現簡單,而且可以根據業務做較多定製化的功能。

  • 每個編程語言都需要自己實現一次,無法通用,如果一個業務包含多個編程語言寫的多個子系統,則重複開發的工作量比較大。

  • 故障情況下,如果主從發生切換,則可能需要所有系統都修改配置並重啓。

目前開源的實現方案中,淘寶的 TDDL(Taobao Distributed Data Layer,外號: 頭都大了)是比較有名的。它是一個通用數據訪問層,所有功能封裝在 jar 包中提供給業務代碼調用。其基本原理是一個基於集中式配置的 jdbc datasource 實現,具有主備、讀寫分離、動態數據庫配置等功能,基本架構是:

640

(http://1.im.guokr.com/0Y5YjfjQ8eGOzeskpen2mlNIYA_b7DBLbGT0YHyUiLFZAgAAgwEAAFBO.png)

2. 中間件封裝

中間件封裝指的是獨立一套系統出來,實現讀寫操作分離和數據庫服務器連接的管理。中間件對業務服務器提供 SQL 兼容的協議,業務服務器無須自己進行讀寫分離。對於業務服務器來說,訪問中間件和訪問數據庫沒有區別,事實上在業務服務器看來,中間件就是一個數據庫服務器。其基本架構是:

640

數據庫中間件的方式具備的特點是:

  • 能夠支持多種編程語言,因爲數據庫中間件對業務服務器提供的是標準 SQL 接口。

  • 數據庫中間件要支持完整的 SQL 語法和數據庫服務器的協議(例如,MySQL 客戶端和服務器的連接協議),實現比較複雜,細節特別多,很容易出現 bug,需要較長的時間才能穩定。

  • 數據庫中間件自己不執行真正的讀寫操作,但所有的數據庫操作請求都要經過中間件,中間件的性能要求也很高。

  • 數據庫主從切換對業務服務器無感知,數據庫中間件可以探測數據庫服務器的主從狀態。例如,向某個測試表寫入一條數據,成功的就是主機,失敗的就是從機。

由於數據庫中間件的複雜度要比程序代碼封裝高出一個數量級,一般情況下建議採用程序語言封裝的方式,或者使用成熟的開源數據庫中間件。如果是大公司,可以投入人力去實現數據庫中間件,因爲這個系統一旦做好,接入的業務系統越多,節省的程序開發投入就越多,價值也越大。

目前的開源數據庫中間件方案中,MySQL 官方先是提供了 MySQL Proxy,但 MySQL Proxy 一直沒有正式 GA,現在 MySQL 官方推薦 MySQL Router。MySQL Router 的主要功能有讀寫分離、故障自動切換、負載均衡、連接池等,其基本架構如下:

640

(https://dev.mysql.com/doc/mysql-router/2.1/en/images/mysql-router-positioning.png)

奇虎 360 公司也開源了自己的數據庫中間件 Atlas,Atlas 是基於 MySQL Proxy 實現的,基本架構如下:

640

(https://camo.githubusercontent.com/42c01a1245183948ba8c61e5572d3aa9c3e8a08e/687474703a2f2f7777332e73696e61696d672e636e2f6c617267652f36653537303561356a7731656271353169336668716a32306a69306a6a7767392e6a7067)

以下是官方介紹,更多內容你可以參考這裏。

Atlas 是一個位於應用程序與 MySQL 之間中間件。在後端 DB 看來,Atlas 相當於連接它的客戶端,在前端應用看來,Atlas 相當於一個 DB。Atlas 作爲服務端與應用程序通信,它實現了 MySQL 的客戶端和服務端協議,同時作爲客戶端與 MySQL 通信。它對應用程序屏蔽了 DB 的細節,同時爲了降低 MySQL 負擔,它還維護了連接池。

小結

今天我爲你講了讀寫分離方式的原理,以及兩個設計複雜度:複製延遲和分配機制,希望對你有所幫助。

這就是今天的全部內容,留一道思考題給你吧,數據庫讀寫分離一般應用於什麼場景?能支撐多大的業務規模?

乾貨多多,歡迎支持作者訂閱極客時間專欄,新手由30元優惠券,掃碼下面二維碼可以得到0元返現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章