摘自 https://zhuanlan.zhihu.com/p/50650224
參考
https://tech.meituan.com/2016/11/18/dianping-order-db-sharding.html
https://crossoverjie.top/2019/07/24/framework-design/sharding-db-03/
https://zhuanlan.zhihu.com/p/50652826
需要考慮分庫(kù)分表的場(chǎng)景
規(guī)劃期內(nèi)的數(shù)據(jù)量和性能問題,嘗試能否用下列方式解決:
- 當(dāng)前數(shù)據(jù)量:如果沒有達(dá)到幾百萬(wàn)至千萬(wàn)卓箫,通常無(wú)需分庫(kù)分表而账;
- 數(shù)據(jù)量問題:增加磁盤铐殃、增加分庫(kù)(不同的業(yè)務(wù)功能表,整表拆分至不同的數(shù)據(jù)庫(kù))探颈;
- 性能問題:升級(jí)CPU/內(nèi)存败匹、讀寫分離、優(yōu)化數(shù)據(jù)庫(kù)系統(tǒng)配置僻肖、優(yōu)化數(shù)據(jù)表/索引、優(yōu)化 SQL卢鹦、分區(qū)檐涝、數(shù)據(jù)表的垂直切分;
- 最后數(shù)據(jù)表的水平切分。
建議的規(guī)劃期:預(yù)估未來(lái)三年的數(shù)據(jù)量
分庫(kù)分表引入的問題
分布式事務(wù)
若兩階段/三階段提交對(duì)性能損耗過(guò)大谁榜,可改用事務(wù)補(bǔ)償機(jī)制。
跨節(jié)點(diǎn) JOIN
規(guī)避方案
全局表: 一些穩(wěn)定的共用數(shù)據(jù)表凡纳,在各個(gè)數(shù)據(jù)庫(kù)中都保存一份窃植;
字段冗余: 一些常用的共用字段,在各個(gè)數(shù)據(jù)表中都保存一份荐糜;
應(yīng)用組裝:應(yīng)用獲取數(shù)據(jù)后再組裝巷怜。
最近關(guān)聯(lián):某個(gè) ID 的用戶信息在哪個(gè)節(jié)點(diǎn),他的關(guān)聯(lián)數(shù)據(jù)(比如訂單)也在哪個(gè)節(jié)點(diǎn)暴氏,可以避免分布式查詢延塑。
跨節(jié)點(diǎn)聚合
只能在應(yīng)用程序端完成。
但對(duì)于分頁(yè)查詢答渔,每次大量聚合后再分頁(yè)关带,性能欠佳。
節(jié)點(diǎn)擴(kuò)容
節(jié)點(diǎn)擴(kuò)容后沼撕,新的分片規(guī)則導(dǎo)致數(shù)據(jù)所屬分片有變宋雏,因而需要遷移數(shù)據(jù)。
全局ID生成策略
DB自動(dòng)增長(zhǎng)列
- 設(shè)置自增偏移和步長(zhǎng)
## 假設(shè)總共有 10 個(gè)分表
## 級(jí)別可選: SESSION(會(huì)話級(jí)), GLOBAL(全局)
SET @@SESSION.auto_increment_offset = 1; ## 起始值, 分別取值為 1~10
SET @@SESSION.auto_increment_increment = 10; ## 步長(zhǎng)增量
- 全局ID映射表
在全局 Redis 中為每張數(shù)據(jù)表創(chuàng)建一個(gè) ID 的鍵务豺,記錄該表當(dāng)前最大 ID磨总;
每次申請(qǐng) ID 時(shí),都自增 1 并返回給應(yīng)用笼沥;
Redis 要定期持久至全局?jǐn)?shù)據(jù)庫(kù)蚪燕。
COMB
參考資料:The Cost of GUIDs as Primary Keys
組合 GUID(10字節(jié)) 和時(shí)間(6字節(jié)),達(dá)到有序的效果奔浅,提高索引性能馆纳。
Snowflake
1bit: 符號(hào)位,總是 0(為了保證數(shù)值是正數(shù))乘凸。
41bit: 毫秒數(shù)(可用 69 年)厕诡;
10bit: 節(jié)點(diǎn)ID(5bit數(shù)據(jù)中心 + 5bit節(jié)點(diǎn)ID,支持 32 * 32 = 1024 個(gè)節(jié)點(diǎn))
12bit: 流水號(hào)(每個(gè)節(jié)點(diǎn)每毫秒內(nèi)支持 4096 個(gè) ID营勤,相當(dāng)于 409萬(wàn)的 QPS灵嫌,相同時(shí)間內(nèi)如 ID 遇翻轉(zhuǎn),則等待至下一毫秒)
擴(kuò)展開源庫(kù)
UidGenerator
Leaf
分片策略
分片策略 | 原理 | 缺點(diǎn) | 優(yōu)點(diǎn) |
---|---|---|---|
連續(xù)分片 | 根據(jù)特定字段(比如用戶ID葛作、訂單時(shí)間)的范圍寿羞,值在該區(qū)間的,劃分到特定節(jié)點(diǎn)赂蠢。 | 如果按時(shí)間劃分绪穆,數(shù)據(jù)熱點(diǎn)分布不均(歷史數(shù)冷當(dāng)前數(shù)據(jù)熱),導(dǎo)致節(jié)點(diǎn)負(fù)荷不均。 | 集群擴(kuò)容后玖院,指定新的范圍落在新節(jié)點(diǎn)即可菠红,無(wú)需進(jìn)行數(shù)據(jù)遷移。 |
MOD | 根據(jù)ID取模 | 擴(kuò)容后需要遷移數(shù)據(jù) | |
一致性Hash算法 | 環(huán)形hash空間;映射數(shù)據(jù)到環(huán);映射節(jié)點(diǎn)到環(huán);數(shù)據(jù)順時(shí)針取最近節(jié)點(diǎn)存儲(chǔ) | 擴(kuò)容后無(wú)需遷移數(shù)據(jù) |
分庫(kù)分表方案
方案 | 原理 | 中間件 |
---|---|---|
代理層 | 部署一臺(tái)代理服務(wù)器偽裝成 MySQL 服務(wù)器难菌,代理服務(wù)器負(fù)責(zé)與真實(shí) MySQL 節(jié)點(diǎn)的對(duì)接试溯,應(yīng)用程序只和代理服務(wù)器對(duì)接 | MyCAT; Sharding-Sphere |
應(yīng)用層 | 業(yè)務(wù)層和 JDBC 層中間,是以 JAR 包方式提供給應(yīng)用調(diào)用郊酒,對(duì)代碼有侵入性 | Sharding-Sphere |
節(jié)點(diǎn)擴(kuò)容方案
常規(guī)方案
如果增加的節(jié)點(diǎn)數(shù)和擴(kuò)容操作沒有規(guī)劃遇绞,那么絕大部分?jǐn)?shù)據(jù)所屬的分片都有變化,需要在分片間遷移:
- 預(yù)估遷移耗時(shí)燎窘,發(fā)布停服公告摹闽;
- 停服(用戶無(wú)法使用服務(wù)),使用事先準(zhǔn)備的遷移腳本褐健,進(jìn)行數(shù)據(jù)遷移付鹿;
- 修改為新的分片規(guī)則;
- 啟動(dòng)服務(wù)器铝量。
免遷移擴(kuò)容
采用雙倍擴(kuò)容策略倘屹,避免數(shù)據(jù)遷移。擴(kuò)容前每個(gè)節(jié)點(diǎn)的數(shù)據(jù)慢叨,有一半要遷移至一個(gè)新增節(jié)點(diǎn)中纽匙,對(duì)應(yīng)關(guān)系比較簡(jiǎn)單。
具體操作如下(假設(shè)已有 2 個(gè)節(jié)點(diǎn) A/B拍谐,要雙倍擴(kuò)容至 A/A2/B/B2 這 4 個(gè)節(jié)點(diǎn)):
- 無(wú)需停止應(yīng)用服務(wù)器烛缔;
- 新增兩個(gè)數(shù)據(jù)庫(kù) A2/B2 作為從庫(kù),設(shè)置主從同步關(guān)系為:A=>A2轩拨、B=>B2践瓷,直至主從數(shù)據(jù)同步完畢(早期數(shù)據(jù)可手工同步);
- 調(diào)整分片規(guī)則并使之生效:
原 ID%2=0 => A 改為 ID%4=0 => A, ID%4=2 => A2亡蓉;
原 ID%2=1 => B 改為 ID%4=1 => B, ID%4=3 => B2晕翠。 - 解除數(shù)據(jù)庫(kù)實(shí)例的主從同步關(guān)系,并使之生效砍濒;
- 至此完成擴(kuò)容
- 擇機(jī)清除冗余數(shù)據(jù)