“從 0 開始學架構(gòu)”專欄已經(jīng)更新了 13 期唉铜,從各個方面闡述了架構(gòu)設(shè)計相關(guān)的理論和流程,包括架構(gòu)設(shè)計起源菠红、架構(gòu)設(shè)計的目的第岖、常見架構(gòu)復雜度分析、架構(gòu)設(shè)計原則试溯、架構(gòu)設(shè)計流程等蔑滓,掌握這些知識是做好架構(gòu)設(shè)計的基礎(chǔ)。
在具體的實踐過程中遇绞,為了更快键袱、更好地設(shè)計出優(yōu)秀的架構(gòu),除了掌握這些基礎(chǔ)知識外摹闽,還需要掌握業(yè)界已經(jīng)成熟的各種架構(gòu)模式蹄咖。大部分情況下,我們做架構(gòu)設(shè)計主要都是基于已有的成熟模式付鹿,結(jié)合業(yè)務(wù)和團隊的具體情況澜汤,進行一定的優(yōu)化或者調(diào)整;即使少部分情況我們需要進行較大的創(chuàng)新舵匾,前提也是需要對已有的各種架構(gòu)模式和技術(shù)非常熟悉俊抵。
接下來,我將逐一介紹最常見的“高性能架構(gòu)模式”“高可用架構(gòu)模式”“可擴展架構(gòu)模式”坐梯,這些模式可能你之前大概了解過徽诲,但其實每個方案里面都有很多細節(jié),只有深入的理解這些細節(jié)才能理解常見的架構(gòu)模式吵血,進而設(shè)計出優(yōu)秀的架構(gòu)谎替。
雖然近十年來各種存儲技術(shù)飛速發(fā)展,但關(guān)系數(shù)據(jù)庫由于其 ACID 的特性和功能強大的 SQL 查詢蹋辅,目前還是各種業(yè)務(wù)系統(tǒng)中關(guān)鍵和核心的存儲系統(tǒng)院喜,很多場景下高性能的設(shè)計最核心的部分就是關(guān)系數(shù)據(jù)庫的設(shè)計。
不管是為了滿足業(yè)務(wù)發(fā)展的需要晕翠,還是為了提升自己的競爭力喷舀,關(guān)系數(shù)據(jù)庫廠商(Oracle、DB2淋肾、MySQL 等)在優(yōu)化和提升單個數(shù)據(jù)庫服務(wù)器的性能方面也做了非常多的技術(shù)優(yōu)化和改進硫麻。但業(yè)務(wù)發(fā)展速度和數(shù)據(jù)增長速度,遠遠超出數(shù)據(jù)庫廠商的優(yōu)化速度樊卓,尤其是互聯(lián)網(wǎng)業(yè)務(wù)興起之后拿愧,海量用戶加上海量數(shù)據(jù)的特點,單個數(shù)據(jù)庫服務(wù)器已經(jīng)難以滿足業(yè)務(wù)需要碌尔,必須考慮數(shù)據(jù)庫集群的方式來提升性能浇辜。
從今天開始券敌,我會分幾期來介紹高性能數(shù)據(jù)庫集群。高性能數(shù)據(jù)庫集群的第一種方式是“讀寫分離”柳洋,其本質(zhì)是將訪問壓力分散到集群中的多個節(jié)點待诅,但是沒有分散存儲壓力;第二種方式是“分庫分表”熊镣,既可以分散訪問壓力卑雁,又可以分散存儲壓力。先來看看“讀寫分離”绪囱,下一期我再介紹“分庫分表”测蹲。
讀寫分離原理
讀寫分離的基本原理是將數(shù)據(jù)庫讀寫操作分散到不同的節(jié)點上,下面是其基本架構(gòu)圖鬼吵。
讀寫分離的基本實現(xiàn)是:
數(shù)據(jù)庫服務(wù)器搭建主從集群扣甲,一主一從、一主多從都可以齿椅。
數(shù)據(jù)庫主機負責讀寫操作琉挖,從機只負責讀操作。
數(shù)據(jù)庫主機通過復制將數(shù)據(jù)同步到從機媒咳,每臺數(shù)據(jù)庫服務(wù)器都存儲了所有的業(yè)務(wù)數(shù)據(jù)粹排。
業(yè)務(wù)服務(wù)器將寫操作發(fā)給數(shù)據(jù)庫主機种远,將讀操作發(fā)給數(shù)據(jù)庫從機涩澡。
需要注意的是,這里用的是“主從集群”坠敷,而不是“主備集群”妙同。“從機”的“從”可以理解為“仆從”膝迎,仆從是要幫主人干活的粥帚,“從機”是需要提供讀數(shù)據(jù)的功能的;而“備機”一般被認為僅僅提供備份功能限次,不提供訪問功能芒涡。所以使用“主從”還是“主備”,是要看場景的卖漫,這兩個詞并不是完全等同的费尽。
讀寫分離的實現(xiàn)邏輯并不復雜,但有兩個細節(jié)點將引入設(shè)計復雜度:主從復制延遲和分配機制羊始。
復制延遲
以 MySQL 為例旱幼,主從復制延遲可能達到 1 秒,如果有大量數(shù)據(jù)同步突委,延遲 1 分鐘也是有可能的柏卤。主從復制延遲會帶來一個問題:如果業(yè)務(wù)服務(wù)器將數(shù)據(jù)寫入到數(shù)據(jù)庫主服務(wù)器后立刻(1 秒內(nèi))進行讀取冬三,此時讀操作訪問的是從機,主機還沒有將數(shù)據(jù)復制過來缘缚,到從機讀取數(shù)據(jù)是讀不到最新數(shù)據(jù)的勾笆,業(yè)務(wù)上就可能出現(xiàn)問題。例如忙灼,用戶剛注冊完后立刻登錄匠襟,業(yè)務(wù)服務(wù)器會提示他“你還沒有注冊”,而用戶明明剛才已經(jīng)注冊成功了该园。
解決主從復制延遲有幾種常見的方法:
1. 寫操作后的讀操作指定發(fā)給數(shù)據(jù)庫主服務(wù)器
例如酸舍,注冊賬號完成后,登錄時讀取賬號的讀操作也發(fā)給數(shù)據(jù)庫主服務(wù)器里初。這種方式和業(yè)務(wù)強綁定远剩,對業(yè)務(wù)的侵入和影響較大,如果哪個新來的程序員不知道這樣寫代碼虾标,就會導致一個 bug蔓腐。
2. 讀從機失敗后再讀一次主機
這就是通常所說的“二次讀取”,二次讀取和業(yè)務(wù)無綁定刁品,只需要對底層數(shù)據(jù)庫訪問的 API 進行封裝即可泣特,實現(xiàn)代價較小,不足之處在于如果有很多二次讀取挑随,將大大增加主機的讀操作壓力状您。例如,黑客暴力破解賬號兜挨,會導致大量的二次讀取操作膏孟,主機可能頂不住讀操作的壓力從而崩潰。
3. 關(guān)鍵業(yè)務(wù)讀寫操作全部指向主機拌汇,非關(guān)鍵業(yè)務(wù)采用讀寫分離
例如柒桑,對于一個用戶管理系統(tǒng)來說,注冊 + 登錄的業(yè)務(wù)讀寫操作全部訪問主機噪舀,用戶的介紹魁淳、愛好、等級等業(yè)務(wù)与倡,可以采用讀寫分離界逛,因為即使用戶改了自己的自我介紹,在查詢時卻看到了自我介紹還是舊的蒸走,業(yè)務(wù)影響與不能登錄相比就小很多仇奶,還可以忍受。
分配機制
將讀寫操作區(qū)分開來,然后訪問不同的數(shù)據(jù)庫服務(wù)器该溯,一般有兩種方式:程序代碼封裝和中間件封裝岛抄。
1. 程序代碼封裝
程序代碼封裝指在代碼中抽象一個數(shù)據(jù)訪問層(所以有的文章也稱這種方式為“中間層封裝”),實現(xiàn)讀寫操作分離和數(shù)據(jù)庫服務(wù)器連接的管理狈茉。例如夫椭,基于 Hibernate 進行簡單封裝,就可以實現(xiàn)讀寫分離氯庆,基本架構(gòu)是:
程序代碼封裝的方式具備幾個特點:
實現(xiàn)簡單蹭秋,而且可以根據(jù)業(yè)務(wù)做較多定制化的功能。
每個編程語言都需要自己實現(xiàn)一次堤撵,無法通用仁讨,如果一個業(yè)務(wù)包含多個編程語言寫的多個子系統(tǒng),則重復開發(fā)的工作量比較大实昨。
故障情況下洞豁,如果主從發(fā)生切換,則可能需要所有系統(tǒng)都修改配置并重啟荒给。
目前開源的實現(xiàn)方案中丈挟,淘寶的 TDDL(Taobao Distributed Data Layer,外號: 頭都大了)是比較有名的志电。它是一個通用數(shù)據(jù)訪問層曙咽,所有功能封裝在 jar 包中提供給業(yè)務(wù)代碼調(diào)用。其基本原理是一個基于集中式配置的 jdbc datasource 實現(xiàn)挑辆,具有主備例朱、讀寫分離、動態(tài)數(shù)據(jù)庫配置等功能之拨,基本架構(gòu)是:
(http://1.im.guokr.com/0Y5YjfjQ8eGOzeskpen2mlNIYA_b7DBLbGT0YHyUiLFZAgAAgwEAAFBO.png)
2. 中間件封裝
中間件封裝指的是獨立一套系統(tǒng)出來茉继,實現(xiàn)讀寫操作分離和數(shù)據(jù)庫服務(wù)器連接的管理咧叭。中間件對業(yè)務(wù)服務(wù)器提供 SQL 兼容的協(xié)議蚀乔,業(yè)務(wù)服務(wù)器無須自己進行讀寫分離。對于業(yè)務(wù)服務(wù)器來說菲茬,訪問中間件和訪問數(shù)據(jù)庫沒有區(qū)別吉挣,事實上在業(yè)務(wù)服務(wù)器看來,中間件就是一個數(shù)據(jù)庫服務(wù)器婉弹。其基本架構(gòu)是:
數(shù)據(jù)庫中間件的方式具備的特點是:
能夠支持多種編程語言睬魂,因為數(shù)據(jù)庫中間件對業(yè)務(wù)服務(wù)器提供的是標準 SQL 接口。
數(shù)據(jù)庫中間件要支持完整的 SQL 語法和數(shù)據(jù)庫服務(wù)器的協(xié)議(例如镀赌,MySQL 客戶端和服務(wù)器的連接協(xié)議)氯哮,實現(xiàn)比較復雜,細節(jié)特別多商佛,很容易出現(xiàn) bug喉钢,需要較長的時間才能穩(wěn)定姆打。
數(shù)據(jù)庫中間件自己不執(zhí)行真正的讀寫操作,但所有的數(shù)據(jù)庫操作請求都要經(jīng)過中間件肠虽,中間件的性能要求也很高幔戏。
數(shù)據(jù)庫主從切換對業(yè)務(wù)服務(wù)器無感知,數(shù)據(jù)庫中間件可以探測數(shù)據(jù)庫服務(wù)器的主從狀態(tài)税课。例如闲延,向某個測試表寫入一條數(shù)據(jù),成功的就是主機韩玩,失敗的就是從機垒玲。
由于數(shù)據(jù)庫中間件的復雜度要比程序代碼封裝高出一個數(shù)量級,一般情況下建議采用程序語言封裝的方式找颓,或者使用成熟的開源數(shù)據(jù)庫中間件侍匙。如果是大公司,可以投入人力去實現(xiàn)數(shù)據(jù)庫中間件叮雳,因為這個系統(tǒng)一旦做好想暗,接入的業(yè)務(wù)系統(tǒng)越多,節(jié)省的程序開發(fā)投入就越多帘不,價值也越大说莫。
目前的開源數(shù)據(jù)庫中間件方案中,MySQL 官方先是提供了 MySQL Proxy寞焙,但 MySQL Proxy 一直沒有正式 GA储狭,現(xiàn)在 MySQL 官方推薦 MySQL Router。MySQL Router 的主要功能有讀寫分離捣郊、故障自動切換辽狈、負載均衡、連接池等呛牲,其基本架構(gòu)如下:
(https://dev.mysql.com/doc/mysql-router/2.1/en/images/mysql-router-positioning.png)
奇虎 360 公司也開源了自己的數(shù)據(jù)庫中間件 Atlas刮萌,Atlas 是基于 MySQL Proxy 實現(xiàn)的,基本架構(gòu)如下:
以下是官方介紹娘扩,更多內(nèi)容你可以參考這里着茸。
Atlas 是一個位于應用程序與 MySQL 之間中間件。在后端 DB 看來琐旁,Atlas 相當于連接它的客戶端涮阔,在前端應用看來,Atlas 相當于一個 DB灰殴。Atlas 作為服務(wù)端與應用程序通信敬特,它實現(xiàn)了 MySQL 的客戶端和服務(wù)端協(xié)議,同時作為客戶端與 MySQL 通信。它對應用程序屏蔽了 DB 的細節(jié)伟阔,同時為了降低 MySQL 負擔尸变,它還維護了連接池。
小結(jié)
今天我為你講了讀寫分離方式的原理减俏,以及兩個設(shè)計復雜度:復制延遲和分配機制召烂,希望對你有所幫助。
這就是今天的全部內(nèi)容娃承,留一道思考題給你吧奏夫,數(shù)據(jù)庫讀寫分離一般應用于什么場景?能支撐多大的業(yè)務(wù)規(guī)模历筝?
歡迎你把答案寫到留言區(qū)酗昼,和我一起討論。相信經(jīng)過深度思考的回答梳猪,也會讓你對知識的理解更加深刻麻削。(編輯亂入:精彩的留言有機會獲得豐厚福利哦!)