整理了一些Java方面的架構、面試資料(微服務、叢集、分散式、中介軟體等),有需要的小夥伴可以關注公眾號【程式設計師內點事】,無套路自行領取
更多優選
- 一口氣說出 9種 分散式ID生成方式,面試官有點懵了
- 3萬字總結,Mysql優化之精髓
- 為了不復制貼上,我被逼著學會了JAVA爬蟲
- 技術部突然宣佈:JAVA開發人員全部要會介面自動化測試框架
- Redis 5種資料結構及對應使用場景,全會面試要加分的
引言
微服務、分散式大行其道的當下,中、高階Java工程師面試題中高併發、大資料量、分庫分表等已經成
了面試的高頻詞彙,這些知識不瞭解面試通過率不會太高。你可以不會用,但你不能不知道,就是這麼
一種現狀。技術名詞大多晦澀難懂,不要死記硬背理解最重要,當你捅破那層窗戶紙,發現其實它也就
那麼回事。
一、為什麼要分庫分表
關係型資料庫以MySQL為例,單機的儲存能力、連線數是有限的,它自身就很容易會成為系統的瓶
頸。當單表資料量在百萬以裡時,我們還可以通過新增從庫、優化索引提升效能。一旦資料量朝著千萬
以上趨勢增長,再怎麼優化資料庫,很多操作效能仍下降嚴重。為了減少資料庫的負擔,提升資料庫響
應速度,縮短查詢時間,這時候就需要進行分庫分表
。
二、如何分庫分表
分庫分表就是要將大量資料分散到多個資料庫中,使每個資料庫中資料量小響應速度快,以此來提升數
據庫整體效能。核心理念就是對資料進行切分(Sharding
),以及切分後如何對資料的快速定位與整合。
針對資料切分型別,大致可以分為:垂直(縱向)切分和水平(橫向)切分兩種。
1、垂直切分
垂直切分又細分為垂直分庫
和垂直分表
垂直分庫
垂直分庫是基於業務分類的,和我們常聽到的微服務治理觀念很相似,每一個獨立的服務都擁有自己的
資料庫,需要不同業務的資料需介面呼叫。而垂直分庫也是按照業務分類進行劃分,每個業務有獨立數
據庫,這個比較好理解。
垂直分表
垂直分表
是基於資料表的列為依據切分的,是一種大表拆小表的模式。
例如:一個order
表有很多欄位,把長度較大且訪問不頻繁的欄位,拆分出來建立一個單獨的擴充套件表work_extend
進行儲存。
order
表:
id | workNo | price | describe | ..... |
---|---|---|---|---|
int(12) | int(2) | int(15) | varchar(2000) |
拆分後
order
核心表:
id | workNo | price | ..... |
---|---|---|---|
int(12) | int(2) | int(15) |
work_extend
表:
id | workNo | describe | ..... |
---|---|---|---|
int(12) | int(2) | varchar(2000) |
資料庫是以行為單位將資料載入到記憶體中,這樣拆分以後核心表大多是訪問頻率較高的欄位,而且欄位
長度也都較短,可以載入更多資料到記憶體中,增加查詢的命中率,減少磁碟IO,以此來提升資料庫效能。
優點:
- 業務間解耦,不同業務的資料進行獨立的維護、監控、擴充套件
- 在高併發場景下,一定程度上緩解了資料庫的壓力
缺點:
- 提升了開發的複雜度,由於業務的隔離性,很多表無法直接訪問,必須通過介面方式聚合資料,
- 分散式事務管理難度增加
- 資料庫還是存在單表資料量過大的問題,並未根本上解決,需要配合水平切分
2、水平切分
前邊說了垂直切分還是會存在單表資料量過大的問題,當我們的應用已經無法在細粒度的垂直切分時,依舊存在單庫讀寫、儲存效能瓶頸,這時就要配合水平切分一起了。
水平切分將一張大資料量的表,切分成多個表結構相同,而每個表只佔原表一部分資料,然後按不同的條件分散到多個資料庫中。
假如一張order
表有2000萬資料,水平切分後出來四個表,order_1
、order_2
、order_3
、order_4
,每張表資料500萬,以此類推。
order_1
表:
水平切分又分有庫內分表
和分庫分表
庫內分表
庫內分表雖然將表拆分,但子表都還是在同一個資料庫例項中,只是解決了單一表資料量過大的問題,並沒有將拆分後的表分佈到不同機器的庫上,還在競爭同一個物理機的CPU、記憶體、網路IO。
分庫分表
分庫分表則是將切分出來的子表,分散到不同的資料庫中,從而使得單個表的資料量變小,達到分散式的效果。
優點:
- 解決高併發時單庫資料量過大的問題,提升系統穩定性和負載能力
- 業務系統改造的工作量不是很大
缺點:
- 跨分片的事務一致性難以保證
- 跨庫的join關聯查詢效能較差
- 擴容的難度和維護量較大,(拆分成幾千張子表想想都恐怖)
三、資料該往哪個庫的表存?
分庫分表以後會出現一個問題,一張表會出現在多個資料庫裡,到底該往哪個庫的表裡存呢?
1、根據取值範圍
按照時間區間
或ID區間
來切分,舉個例子:假如我們切分的是使用者表,可以定義每個庫的User表
裡只存10000條資料,第一個庫userId
從1 ~ 9999,第二個庫10000 ~ 20000,第三個庫20001~ 30000......以此類推。
優點:
- 單表資料量是可控的
- 水平擴充套件簡單隻需增加節點即可,無需對其他分片的資料進行遷移
- 能快速定位要查詢的資料在哪個庫
缺點:
- 由於連續分片可能存在資料熱點,如果按時間欄位分片,有些分片儲存最近時間段內的資料,可能會被頻繁的讀寫,而有些分片儲存的歷史資料,則很少被查詢
2、hash取模
hash取模mod(對hash結果取餘數 (hash() mod N))的切分方式比較常見,還拿User表
舉例,對資料庫從0到N-1進行編號,對User表
中userId
欄位進行取模,得到餘數i
,i=0
存第一個庫,i=1
存第二個庫,i=2
存第三個庫....以此類推。
這樣同一個使用者的資料都會存在同一個庫裡,用userId
作為條件查詢就很好定位了
優點:
- 資料分片相對比較均勻,不易出現某個庫併發訪問的問題
缺點:
- 但這種演算法存在一些問題,當某一臺機器當機,本應該落在該資料庫的請求就無法得到正確的處理,這時宕掉的例項會被踢出叢集,此時演算法變成hash(userId) mod N-1,使用者資訊可能就不再在同一個庫中。
四、分庫分表後會有哪些坑?
1、事務一致性問題
由於表分佈在不同庫中,不可避免會帶來跨庫事務問題。一般可使用"XA協議"和"兩階段提交"處理,但是這種方式效能較差,程式碼開發量也比較大。
通常做法是做到最終一致性的方案,往往不苛求系統的實時一致性,只要在允許的時間段內達到最終一致性即可,可採用事務補償的方式。
2、分頁、排序的坑
日常開發中分頁、排序是必備功能,而多庫進行查詢時limit
分頁、order by
排序,著實讓人比較頭疼。
分頁需按照指定欄位進行排序,如果排序欄位恰好是分片欄位時,通過分片規則就很容易定位到分片的位置;一旦排序欄位非分片欄位時,就需要先在不同的分片節點中將資料進行排序並返回,然後將不同分片返回的結果集進行彙總和再次排序,最終返回給使用者,過程比較複雜。
3、全域性唯一主鍵問題
由於分庫分表後,表中的資料同時存在於多個資料庫,而某個分割槽資料庫的自增主鍵已經無法滿足全域性
唯一,所以此時一個能夠生成全域性唯一ID的系統是非常必要的。那麼這個全域性唯一ID就叫分散式ID
。可
以參考我之前寫的這篇文章《一口氣說出 9種 分散式ID生成方式,面試官有點懵了》
五、分庫分表工具?
自己開發分庫分表工具的工作量是巨大的,好在業界已經有了很多比較成熟的分庫分表中介軟體,我們可
以將更多的時間放在業務實現上
- sharding-jdbc(噹噹)
- TSharding(蘑菇街)
- Atlas(奇虎360)
- Cobar(阿里巴巴)
- MyCAT(基於Cobar)
- Oceanus(58同城) Vitess(谷歌)
----
今天就說這麼多,如果本文對您有一點幫助,希望能得到您一個點贊?哦
您的認可才是我寫作的動力!
整理了一些Java方面的架構、面試資料(微服務、叢集、分散式、中介軟體等),有需要的小夥伴可以關注公眾號【程式設計師內點事】,無套路自行領取