通常而言,MySQL資料庫單表支援容量為1000萬條記錄,如果超過了這個量級,在查詢/更新資料庫表記錄的時候,效能會受到很大的影響。在這種情況下,我們往往會採用其他的解決方案以提高資料庫讀寫效能:
- 採用非關係型資料庫如ElasticSearch、Cassandra、HBase、Mongo等,非關係型資料庫提供了原生的水平擴充套件方式,因此往往是海量資料儲存的首選;
- 對關係型資料庫進行分庫分表,使之能水平擴充套件;
本章主要討論關係型資料庫的分庫分表方案。
基本概念
從拆分的維度來看,大致分為水平拆分和垂直拆分兩種:
垂直分佈 | 水平分佈 | |
分表 | 對一個表而言,把部分訪問頻率比較低的列遷移到另外一個表中,拆分出來的表和原表的記錄數量一致,但結構不同 | 將原表的部分記錄按照一定規則放置到新表中,新表的結構和原表保持一致,但記錄不同 |
分庫 | 將資料庫中的各個表按照業務進行拆分,使之分佈到不同的資料庫裡 | 將水平拆分之後的表分散放置到不同的資料庫中 |
從拆分的內容來看,可以分為兩類:
- 資料分片
資料分片是將所有資料按照水平拆分的方式均勻分佈到多個資料庫/表中,每個資料庫/表持有的資料都不一樣;資料分片有助於資料庫儲存資源和計算資源的負載均衡;
- 主從備份
主從備份分為熱備和冷備,目的都是為了防止資料的丟失;熱備中主資料庫和從資料庫都持有相同資料的副本以保證資料庫儲存的高可用,並可以通過讀寫分離提高資料庫的讀效能;
分庫分表的痛點
- 事務性問題
將原來的一個表/庫進行拆分之後,就不能使用資料庫自身的事務機制保證資料操作的ACID特性,因此需要利用分散式事務如基於兩階段提交的XA協議;
- 聯表查詢的問題
如果表A被拆分出了兩個表A1和A2,表B被拆分出了兩個表B1和B2,那麼原來的A INNER JOIN B查詢將需要(A1 INNER JOIN B1) UNION (A1 INNER JOIN B2) UNION (A2 INNER JOIN B1) UNION (A2 INNER JOIN B2)這樣的操作;
如果拆分得更多,最終的聯表查詢結果將是兩個表拆分數量的笛卡爾積的Union操作,查詢效能會變得異常低下;
- 排序分頁的問題
對資料庫表的查詢結果進行排序分頁,需要從各個分表查詢出結果進行彙總之後再進行排序分頁;
分庫分表的架構
常用的分庫分表架構大致可以分為兩類:元件模式和代理模式;
- 元件模式將分庫分表的邏輯以元件的方式提供給客戶端使用;客戶端開發人員需要以依賴的形式引用這些元件,並在客戶端程式碼中配置分庫分表的規則;Apache ShardingSphere、阿里巴巴的TDDL就是這類模式的代表;
- 代理模式是在客戶端和伺服器之間加入一個代理伺服器,這個代理伺服器負責對客戶端請求的資料進行分庫分表,這些分庫分表規則對客戶端開發人員而言是完全透明的,它遮蔽了分庫分表的複雜度,減輕了客戶端開發人員的學習曲線;MyCat、Sharding Proxy是這類模式的代表。
由於代理模式加入了一箇中間層,不像元件模式那樣客戶端直連資料庫管理系統,因此效能相對較差;但由於元件模式需要客戶端開發人員去了解分庫分表的規則,並保證這些規則在客戶端都能正確配置;因此代理模式漸漸成為分庫分表架構設計的主流。
水平分表的方式
- 順序分佈
順序分佈又分為時間順序或主鍵順序;時間順序如將每年或每月的資料單獨儲存在一個表中;主鍵順序如對於一個主鍵遞增的表A,將主鍵ID為1-10000範圍內的記錄儲存到A1表,將主鍵ID為10001-20000範圍內的記錄儲存到A2表......以此類推。
- Hash分佈
Hash分佈可以分為三類:Hash取模、一致性Hash、虛擬槽分割槽;
Hash取模是使用主鍵ID對資料庫節點數量進行取模,得到的值即是該記錄儲存節點的索引值;
一致性Hash和虛擬槽分割槽可以參考我以前寫的Cassandra介紹中的資料分佈部分。