完整分庫分表流程demo
一、 技術
- 選用sharding-jdbc做項目主要分庫分表技術, sharding-proxy做運維輔助技術
- 選用uid-generator做唯一id生成器
- 選用datax做單表數據遷移到分庫分表同步工具, 需要整合datax和sharding-jdbc
二、 模擬訂單表分庫分表遷移流程
1. 確認分庫分表鍵
- 可以統計查詢訂單表的where條件
選用命中率高的, 減輕主要查詢壓力
- 需要考慮分布式事務
t_order表和t_order_item表, 保存一次訂單,確保分庫后order和order_item都放在同一個庫中,
這樣就可以使用本地事務了。一般選用user_id, 選用order_id也可以, 本demo使用user_id注意事項,選用了user_id做分片鍵,則原先使用order_id查詢的接口會遍歷各個分庫查詢數據 a. 如果數據量不是特別大,可以不處理 b. 本demo將user_id和order_id映射關系保存到t_order_mapping表中,使用order_id查詢時先查詢一次user_id,然后帶上user_id查詢訂單 c. 基因id 處理,參考美團訂單分庫分表實現,定制order_id,中間嵌入user_id后幾位數字,分片查詢時根據user_id基因數字路由,這種改造工程比較大 d. 多列分庫分表,在將user_id做一次分庫分表的同時,也將order_id做一次分庫分表,存了兩份數據,存儲較大
2. 分片算法
-
range
按時間或id范圍划分,如[1-10000]放到1庫,[10001-20000]放到2庫
優點:單表大小可控,天然水平擴展。
缺點:無法解決集中寫入瓶頸的問題。 -
hash
hash取模 2^n
分2(庫) * 4 (表), 即分2個庫,每個庫4張表
則 路由規則是 庫號= user_id % 2, 表號= (user_id/2) % 4優點: 易擴展,解決集中寫入瓶頸的問題
缺點: 擴容麻煩,需要重新hash,(注意避免在同一張表的數據,重新hash到不同的表), 需要遷移數據(本demo采用該方法)
3. 確定容量,考慮擴容
-
生產中一次性分夠,夠用好幾年,不用考慮擴容
分32(庫) * 32 (表) , 分成32個庫,每個庫32張表, 共1024張表(也可以16*64)
路由規則是 庫號= user_id % 32, 表號= (user_id/32) % 32 -
初始邏輯分庫,實際上可以部署4台機器,每台機器8庫, 每個庫32表
當表數據超過單機限制后,可以部署8台機器,每台機器4各庫,每個庫32表 最多可以部署1024台機器,每台機器1張表
擴容時,僅僅是改一下配置,遷移數據就可以了, 原來在同一張表的數據,擴容之后,還在同一張表
更具體的介紹看美團實現
4. 唯一id
-
uuid 字符串
占用空間較多, 不能做到遞增
-
雪花算法
可以做到遞增,性能較好
維護workId比較麻煩
存在時鍾回撥問題
其他的實現一般都是基於雪花算法進行改造 -
比較復雜,暫不考慮
-
比較簡單,解決workId維護問題,解決了時鍾回撥問題,暫未提供workId重用實現
本demo采用該實現,已實現復用workId
ecp-uid整合了美團leaf、百度UidGenerator、原生snowflake 實現,可以參考,
由於直接使用uid-generator足夠簡單,且有效使用,暫不采用ecp-uid
5. 單庫表 遷移 到分庫
參考美團雙寫實現
-
① 原系統中將需要訂單表的關聯查詢(join)去掉
改成在應用中用代碼處理join, 為后續分庫分表做准備
-
② 改造系統中訂單id的實現,統一使用uid-generator生成的唯一id
-
③ 數據庫雙寫(先寫單庫,后寫分庫,事務以單庫為主,讀數據以單庫為主 ),同步訂單歷史數據到分庫,然后校驗補償分庫數據
a. 先寫單庫,后寫分庫,事務以單庫為主,讀數據以單庫為主
b. 同時,使用datax同步歷史訂單數據到分庫
c. 校驗單庫和分庫數據,補償數據到分庫(插入或根據更新時間比較更新)
-
④ 在③步驟數據補平后,數據仍雙寫(先寫分庫,后寫單庫,事務以分庫為主,讀數據以分庫為主),同時校驗補償單庫數據
a. 先寫分庫,后寫單庫,事務以分庫為主,讀數據以分庫為主
(如果服務眾多,可以切一小部分服務灰度嘗試讀寫, 如果有問題,可以回退到③)b. 校驗單庫和分庫數據,補償數據到單庫(插入或根據更新時間比較更新)
-
⑤ 觀察幾天沒問題后,下線單庫訂單表
6. 分庫分表中間件
-
mycat
基於數據庫代理實現,存在單點問題
-
目前成為apache頂級項目, 從前景上說,采用該技術應是最好的
-
sharding-jdbc
shardingsphere子項目, jdbc代理
a. 不存在單點問題
b. 可能會存在多份配置
c. 升級比較麻煩,需要統一升級
d. 只能應用於java語言 -
sharding-proxy
shardingsphere子項目, 數據庫代理
a. 存在單點問題
b. 兼容性問題
c. 優點是沒有語言限制
目前demo采用sharding-jdbc實現為主,sharding-proxy運維為輔
-
三、本demo使用流程
1. 創建單庫
create database db_lab;
2. 運行數據初始數據庫腳本
db_lab_table.sql 用於創建數據表
db_lab_data.sql 用於創建測試數據,里面有3萬訂單(t_order)數據,和9萬訂單項(t_order_item)數據
3. 創建2個分庫
create database db_lab_orders_0; create database db_lab_orders_1;
4. 配置uid-generator數據庫
-
創建uid數據庫
create database uid;
-
創建uid數據表
DROP TABLE IF EXISTS WORKER_NODE; CREATE TABLE WORKER_NODE ( ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id', HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name', PORT VARCHAR(64) NOT NULL COMMENT 'port', TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER', LAUNCH_DATE DATE NOT NULL COMMENT 'launch date', MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time', CREATED TIMESTAMP NOT NULL COMMENT 'created time', PRIMARY KEY(ID) ) COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;
-
修改uid數據庫配置用戶名密碼
5. 修改對應數據庫配置,用戶名密碼等
- sharding-config中的分庫配置application-sharding.yml和config-sharding.yaml
- single-datasource-lab單庫配置
- transform-validate單庫配置
6. 搭建sharding-proxy后, 復制proxy配置, 啟動運行
使用shrding-proxy創建分庫分表后的訂單表
CREATE TABLE `t_order` ( `order_id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `address_id` bigint(20) NOT NULL, `status` varchar(50) DEFAULT NULL, `create_time` datetime NOT NULL, `update_time` datetime NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
CREATE TABLE `t_order_item` ( `order_item_id` bigint(20) NOT NULL AUTO_INCREMENT, `order_id` bigint(20) DEFAULT NULL, `user_id` int(11) NOT NULL, `status` varchar(50) DEFAULT NULL, `create_time` datetime NOT NULL, `update_time` datetime NOT NULL, PRIMARY KEY (`order_item_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
7. 啟動single-datasource-lab項目,用於模擬原使用單庫web應用
注意以下配置,用於分庫分表遷移的切換
# 1 使用uid的初始下單 2 下單雙寫,以單庫讀寫為主 3 下單雙寫, 以分庫讀寫為主 4 下單只寫在分庫 transfrom.step=2
可以打包放到idea外部部署運行,高配機器可以忽略
8. 啟動single-datasource-lab中的模擬客戶端
模擬用戶訪問訂單服務
9. 下載datax部署同步訂單歷史數據
本項目使用datax+sharding-jdbc同步訂單歷史數據,查看使用方法
- 復制配置到本機datx/job目錄下,並修改對應賬號密碼
- 使用命令運行'job-order.json','job-order_item.json'同步歷史數據到分庫,其中'job-order_mapping.json'是用於同步order_id和User_id的關系數據
10. 數據庫雙寫(step=2)時,啟動validate-step2校驗補償訂單
在single-datasource-lab項目運行時配置是'transfrom.step=2'時, 啟動 validate-step2項目,用於校驗補償分庫訂單數據
11. 校驗補平分庫數據后,切換數據庫雙寫,此時以分庫事務為主,讀數據以分庫為主
修改single-datasource-lab配置'transfrom.step=3',並重啟 觀察是否有問題,如果有問題,則回退到step=2
12. 重啟validate-step2項目,做分庫最后一次校驗
因為step2啟動時,會讀取當前最大的訂單id, 進行校驗,重啟是為了防止遺漏
13. 啟動 validate-step3項目,進行驗證單庫數據校驗
這里主要是為了可回滾
14. 繼續觀察single-datasource-lab,確認沒問題后,下線單庫訂單表
真實項目需持續觀察幾天, 沒有問題,則修改配置為'transfrom.step=4', 重啟下線單庫訂單
有問題,則回退到step2, 注意,回退后,validate-step2也需重新運行校驗
四、常見問題
- no table route info
根據路由規則得到的表名在數據庫中不存在,檢查配置
可以開啟sharding日志進行排查props: sql: show: true # 打印 SQL
后記
分庫分表是突破單機性能的重要手段,但分庫分表實現比較復雜,不到萬不得已不可輕易使用
本demo尚未在真實項目中使用,僅供學習和參考
參考資料