之前提到了在高并發(fā)下數據庫的一種優(yōu)化方案:讀寫分離肛循,它就是依靠主從復制的技術使得數據庫實現了數據復制為多份,增強了抵抗大量并發(fā)讀請求的能力银择,提升了數據庫的查詢性能的同事多糠,也提升了數據的安全性。當某一個數據庫節(jié)點(無論主從)發(fā)生故障時浩考,我們還有其他的節(jié)點中存儲著全量的數據夹孔,保證數據不會丟失,此時系統(tǒng)的架構圖變成了下面這樣析孽。
這時搭伤,公司傳來一個好消息,運營推廣持續(xù)帶來了流量袜瞬,訂單量突破了5000w怜俐,訂單數據都是單表存儲的,壓力倍增邓尤,因為無論是數據庫的查詢還是寫入性能都在下降佑菩,數據庫的磁盤空間也在報警。所以要尋求高效的解決方式裁赠,以便系統(tǒng)能正常運轉下去殿漠。需要考慮以下幾點:
1.訂單越來越多,單表數據量超過了千萬甚至億幾倍佩捞。這時即使是用了索引绞幌,索引占用的空間,也隨著數據量的增長而增大一忱,數據庫就無法緩存全量的索引信息莲蜘,那么就需要從磁盤上讀取索引數據,就會影響到查詢的性能了帘营。這時要如何提升查詢性能呢票渠?
2.數據量的增加也占據了磁盤的空間,數據庫在備份和恢復的時間變長芬迄,如何讓數據庫系統(tǒng)支持如此大的數據量呢问顷?
3.不同模塊的數據,比如用戶數據和用戶關系數據禀梳,杜窄,全都存儲在一個主庫中,一旦主庫發(fā)生故障算途,所有的模塊都會受到影響塞耕,那么如何做到不同模塊的故障隔離呢?
4.在4核8G的云服務器上對MySQL5.7做性能測試嘴瓤,大概可以支撐500TPS和1000QPS扫外,可以看到數據庫對于寫入性能要弱于數據查詢的能力莉钙,那么隨著系統(tǒng)寫入請求量的增加,數據庫系統(tǒng)如何來處理更高的并發(fā)寫入請求呢筛谚?
這些問題可以歸納成胆胰,數據庫的寫入請求量大造成的性能和可用性方面的問題,要解決這些問你刻获,所采取的措施就是對數據進行分片。這樣就可以很好的分攤數據庫讀寫壓力瞎嬉,也可以突破單機的存儲平靜蝎毡,而常見的一種方式就是對數據庫做“分庫分表”。
分庫分表是一個常見的技術技術方案氧枣,不少人會在“分庫分表”這里踩坑沐兵,主要體現在:
1.對如何使用正確的分庫分表方式一知半解,沒有明白使用場景和方法便监。比如一些人會在查詢時不使用分區(qū)鍵扎谎;
2.分庫分表引入了一些問題后,沒有找到合適的解決方案烧董,比如在查詢時使用大量連表查詢等毁靶。
如何對數據庫做垂直拆分
分庫分表是一種常見的將數據分片的方式,它的基本思想是依照某一種策略將數據盡量平均的分配到多個數據庫節(jié)點或者多個表中逊移。
不同于主從復制時數據是全部的被復制到多個節(jié)點预吆,分庫分表吼,每個節(jié)點只保存部分的數據胳泉,這樣就可以有效的減少單個數據庫節(jié)點和單個數據表中存儲的數據量拐叉,在解決了數據存儲瓶頸的同時也能有效的提升數據查詢的性能。同時扇商,因為數據被分配到多個數據庫節(jié)點上凤瘦,那么數據的寫入請求也從請求單一主庫變成了請求多個數據分片節(jié)點,在一定程度上也會提升并發(fā)寫入的性能案铺。
數據庫分庫分表的方式有兩種:垂直拆分和水平拆分蔬芥。掌握拆分方式是關鍵,理解拆分原理是內核控汉。學習時最好可以結合自身業(yè)務來思考坝茎。
垂直拆分,就是對數據庫豎著拆分暇番,也就是將數據庫的表拆分到多個不同的數據庫中嗤放。
垂直拆分的原則一般是按照業(yè)務類型來拆分,核心思想是專庫專用壁酬,將業(yè)務耦合度比較高的表拆分到單獨的庫中次酌。舉個例子恨课,就是在整理衣服的時候,將羽絨股岳服、毛衣剂公、T恤分別放在不同的格子里,這樣就可以解決在開篇提到的第三個問題:把不同的業(yè)務的數據拆分到不同的數據庫節(jié)點上吊宋,這樣一旦數據庫發(fā)生故障只會影響到某一個模塊的功能纲辽,不會影響到整體功能,從而實現了數據層面的故障隔離璃搜。
以微博系統(tǒng)為例來說明:
在微博系統(tǒng)中有和用戶相關的表拖吼,有和內容有關的表,有和關系相關的表这吻,這些表的存儲在主庫中吊档。在拆分后,我們期望用戶相關的表拆分到用戶庫中唾糯,內容相關的表拆分到內容庫中怠硼,關系相關的表拆分到關系庫中。
對數據庫進行垂直拆分是一種偏常規(guī)的方式移怯,這種方式其實很會比較常用香璃,不過拆分之后,雖然可以暫時緩解存儲容量的瓶頸舟误,但并不是萬事大吉增显,因為數據庫垂直拆分后依然不能解決某一個業(yè)務模塊的數據大量膨脹的問題,一旦系統(tǒng)遭遇某一個業(yè)務庫的數據量暴增脐帝,在這情況下同云,還需要繼續(xù)尋找可以彌補的方式。
比如微博關系量早已過了千億堵腹,單一的數據庫或者數據表已經遠遠不能滿足存儲和查詢的需求了炸站,這個時候,需要將數據拆分到多個數據庫和數據表中疚顷,也就是對數據庫和數據表做水平拆分旱易。
如何對數據庫做水平拆分
和垂直拆分的關注點不同,垂直拆分的關注點在于業(yè)務的相關性腿堤,而水平拆分指的是將單一數據表按照某一種規(guī)則拆分到多個數據庫和多個數據表中阀坏,關注點在數據的特點。
拆分的規(guī)則有下面兩種
1.按照某一個字段的哈希值做拆分笆檀,這種拆分規(guī)則比較適用于實體表忌堂,比如說用戶表,內容表酗洒,我們一般按照這些實體表的ID字段來拆分士修。比如說我們想把用戶表拆分成16個庫枷遂,每個庫是64張表,那么可以先對用戶ID做哈希棋嘲,哈希的目的是將ID盡量打散酒唉,然后再對16取余,這樣就得到了分庫后的索引值沸移;對64取余痪伦,就得到了分表后的索引值。
2.另一種比較常用的是按照某一個字段的區(qū)間來拆分雹锣,比較常用的是時間字段网沾,比如說可以把一個月的數據放入一張表中,這樣在查詢時就可以根據創(chuàng)建時間先定位數據存儲在哪個表里面笆制,再按照查詢條件來查詢。
一般來說涣达,列表數據可以使用這種拆分方式在辆,比如一個人一段時間的訂單,發(fā)布的內容度苔。但是這種方式可能會存在明顯的缺點匆篓,你當然會更關注最近我買了什么,發(fā)了什么寇窑,所以查詢QPS也會更多一些鸦概,對性能有一定的影響。另外甩骏,使用這種拆分規(guī)則后窗市,數據表要提前建立好,否則如果時間到了2020年元旦饮笛,DBA卻忘記了建表咨察,那么2020年的數據就沒有褲邊可寫,就會發(fā)生故障福青。
數據庫在分庫分表之后摄狱,數據的訪問方式也有了極大的改變,原先只需要根據查詢條件從庫中查詢數據即可无午,現在則需要先確認數據在哪一個庫表中媒役,再到那個庫表中查詢數據。這種復雜度也可以通過數據庫中間件來解決宪迟,在08中講過酣衷。需要強調的是,你需要對所使用的數據庫中間件的原理有足夠的了解次泽,和足夠的運維上的把控能力鸥诽。
分庫分表雖然能夠解決數據庫擴展性的問題商玫,但是它也給我們的使用帶來了一些問題。
解決分庫分表引入的問題
分庫分表引入的一個最大的問題就是引入了分庫分表鍵牡借,也叫做分區(qū)鍵拳昌。也就是我們對數據庫做分庫分表所依據的字段。
從分庫分表規(guī)則中可以看出钠龙,無論是哈希拆分還是區(qū)間段的拆分炬藤,我們首先都需要選取一個數據庫字段,這帶來的一個問題是:我們之后所有的查詢都需要帶上這個字段碴里,才能找到數據所在的庫表沈矿,否則就只能向所有的數據庫和數據表發(fā)送查詢命令。如果像上面說的要拆分成16個庫和64張表咬腋,那么一次數據的查詢會變成16*64 = 1024次查詢羹膳,查詢的性能極差。
針對這個問題根竿,也有相應的解決思路陵像,比如在用戶庫中我們使用ID作為分區(qū)鍵,這時如果需要按照昵稱來查詢用戶時寇壳,你可以按照昵稱作為分區(qū)鍵再做一次拆分醒颖,但是這樣會極大的增加存儲成本,如果我們以后還需要按照注冊時間來哈訊要怎么辦壳炎?再做一次拆分么泞歉?
所以最合適的思路是要建立一個昵稱和ID的映射表,在查詢的時候要先通過昵稱查詢到ID匿辩,再通過ID查詢完整的數據腰耙,這個表也可以是分庫分表的,也需要占用一定的存儲空間铲球,但是因為表中只有兩個字段沟优,所以相比重新做一次拆分還是會節(jié)省不少空間。
分庫分表引入的另外一個問題是一些數據庫的特性在實現時可能變得很困難睬辐,比如說多表的JOIN在單庫時可以通過一個SQL語句完成挠阁,但是拆分到多個數據庫之后就無法跨庫執(zhí)行SQL了。再比如說未分庫分表之前查詢總數只需要count()即可溯饵,現在數據被分散到多個庫表中侵俗,我們可能要考慮其他方案,比如說將計數的數據單獨存儲在一張表中或者記錄在redis里丰刊。
雖然分庫分表會對我們使用數據庫帶來一些不便隘谣,但是相比它帶來的擴展性和性能方面的提升,還是需要做的。經歷過分庫分表后的系統(tǒng)寻歧,才能夠突破單機的容量和請求量的瓶頸掌栅。
課堂小結
在面對數據庫容量瓶頸和寫并發(fā)量大的問題時,可以采用垂直拆分和水平拆分來解決码泛,需要注意猾封,這兩種方式會引入諸如查詢數據必須帶上分區(qū)鍵,列表總數需要單獨冗余存儲等問題
而且在實現分庫分表的過程中噪珊,數據從單庫單表遷移多庫多表是一件既繁雜又容易出錯的事情晌缘,如果初期沒有規(guī)劃得當,后面要繼續(xù)增加數據庫數量和表數量時痢站,我們還要經歷這個遷移的過程磷箕,所以,分庫分表的原則主要有以下幾點:
1.如果在性能上沒有瓶頸點盡量不做分庫分表阵难;
2.如果要做岳枷,就盡量一次到位,比如說16個庫呜叫,每個庫64個表就基本能夠滿足未來幾年內的業(yè)務需求空繁。
3.很多的NoSQL數據庫,例如Hbase怀偷,MongoDB都提供auto sharding的特性家厌,如果你的團隊對于這些組件比較熟悉播玖,有較強的運維能力椎工,那么也可以考慮使用這些NoSQL數據庫代替?zhèn)鹘y(tǒng)的關系型數據庫。
有很多人并沒有真正從根本上搞懂為什么要拆分蜀踏,拆分后會帶來哪些問題维蒙,只是一味的學習大廠現有的拆分方法,從而導致問題頻出果覆。