MySQL 分庫分表及其平滑擴容方案

作者:王克鋒
出處:https://kefeng.wang/2018/07/22/mysql-sharding/

衆所周知,數據庫很容易成爲應用系統的瓶頸。單機數據庫的資源和處理能力有限,在高併發的分佈式系統中,可採用分庫分表突破單機侷限。本文總結了分庫分表的相關概念、全局 ID 的生成策略、分片策略、平滑擴容方案、以及流行的方案。

1 分庫分表概述

在業務量不大時,單庫單表即可支撐。
當數據量過大存儲不下、或者併發量過大負荷不起時,就要考慮分庫分表。

1.1 分庫分表相關術語

1.2 真的要採用分庫分表?

需要注意的是,分庫分表會爲數據庫維護和業務邏輯帶來一系列複雜性和性能損耗,除非預估的業務量大到萬不得已,切莫過度設計、過早優化
規劃期內的數據量和性能問題,嘗試能否用下列方式解決:

2 全局 ID 生成策略

2.1 自動增長列

優點:數據庫自帶功能,有序,性能佳。
缺點:單庫單表無妨,分庫分表時如果沒有規劃,ID 可能重複。解決方案:

2.1.1 設置自增偏移和步長
### 假設總共有 10 個分表
### 級別可選: SESSION(會話級), GLOBAL(全局)
SET @@SESSION.auto_increment_offset = 1; ### 起始值, 分別取值爲 1~10
SET @@SESSION.auto_increment_increment = 10; ### 步長增量

如果採用該方案,在擴容時需要遷移已有數據至新的所屬分片。

2.1.2 全局 ID 映射表

在全局 Redis 中爲每張數據表創建一個 ID 的鍵,記錄該表當前最大 ID;
每次申請 ID 時,都自增 1 並返回給應用;
Redis 要定期持久至全局數據庫。

2.2 UUID(128 位)

在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。通常平臺會提供生成 UUID 的 API。
UUID 由 4 個連字號 (-) 將 32 個字節長的字符串分隔後生成的字符串,總共 36 個字節長。形如:550e8400-e29b-41d4-a716-446655440000。
UUID 的計算因子包括:以太網卡地址、納秒級時間、芯片 ID 碼和許多可能的數字。
UUID 是個標準,其實現有幾種,最常用的是微軟的 GUID(Globals Unique Identifiers)。

優點:簡單,全球唯一;
缺點:存儲和傳輸空間大,無序,性能欠佳。

2.3 COMB(組合)

參考資料:The Cost of GUIDs as Primary Keys
組合 GUID(10 字節) 和時間 (6 字節),達到有序的效果,提高索引性能。

2.4 Snowflake(雪花) 算法

參考資料:twitter/snowflake,Snowflake 算法詳解
Snowflake 是 Twitter 開源的分佈式 ID 生成算法,其結果爲 long(64bit) 的數值。
其特性是各節點無需協調、按時間大致有序、且整個集羣各節點單不重複。
該數值的默認組成如下 (符號位之外的三部分允許個性化調整):

3 分片策略

3.1 連續分片

根據特定字段 (比如用戶 ID、訂單時間) 的範圍,值在該區間的,劃分到特定節點。
優點:集羣擴容後,指定新的範圍落在新節點即可,無需進行數據遷移。
缺點:如果按時間劃分,數據熱點分佈不均 (歷史數冷當前數據熱),導致節點負荷不均。

3.3 ID 取模分片

缺點:擴容後需要遷移數據。

3.2 一致性 Hash 算法

優點:擴容後無需遷移數據。

3.4 Snowflake 分片

優點:擴容後無需遷移數據。

4 分庫分表引入的問題

4.1 分佈式事務

參見 分佈式事務的解決方案
由於兩階段 / 三階段提交對性能損耗大,可改用事務補償機制。

4.2 跨節點 JOIN

對於單庫 JOIN,MySQL 原生就支持;
對於多庫,出於性能考慮,不建議使用 MySQL 自帶的 JOIN,可以用以下方案避免跨節點 JOIN:

另外,某個 ID 的用戶信息在哪個節點,他的關聯數據 (比如訂單) 也在哪個節點,可以避免分佈式查詢。

4.3 跨節點聚合

只能在應用程序端完成。
但對於分頁查詢,每次大量聚合後再分頁,性能欠佳。

4.4 節點擴容

節點擴容後,新的分片規則導致數據所屬分片有變,因而需要遷移數據。

5 節點擴容方案

相關資料: 數據庫秒級平滑擴容架構方案

5.1 常規方案

如果增加的節點數和擴容操作沒有規劃,那麼絕大部分數據所屬的分片都有變化,需要在分片間遷移:

5.2 免遷移擴容

採用雙倍擴容策略,避免數據遷移。擴容前每個節點的數據,有一半要遷移至一個新增節點中,對應關係比較簡單。
具體操作如下 (假設已有 2 個節點 A/B,要雙倍擴容至 A/A2/B/B2 這 4 個節點):

6 分庫分表方案

6.1 代理層方式

部署一臺代理服務器僞裝成 MySQL 服務器,代理服務器負責與真實 MySQL 節點的對接,應用程序只和代理服務器對接。對應用程序是透明的。
比如 MyCAT,官網,源碼,參考文檔:MyCAT+MySQL 讀寫分離部署
MyCAT 後端可以支持 MySQL, SQL Server, Oracle, DB2, PostgreSQL 等主流數據庫,也支持 MongoDB 這種新型 NoSQL 方式的存儲,未來還會支持更多類型的存儲。
MyCAT 不僅僅可以用作讀寫分離,以及分表分庫、容災管理,而且可以用於多租戶應用開發、雲平臺基礎設施,讓你的架構具備很強的適應性和靈活性。

6.2 應用層方式

處於業務層和 JDBC 層中間,是以 JAR 包方式提供給應用調用,對代碼有侵入性。主要方案有:
(1) 淘寶網的 TDDL: 已於 2012 年關閉了維護通道,建議不要使用。
(2) 噹噹網的 Sharding-JDBC: 仍在活躍維護中:
是噹噹應用框架 ddframe 中,從關係型數據庫模塊 dd-rdb 中分離出來的數據庫水平分片框架,實現透明化數據庫分庫分表訪問,實現了 Snowflake 分片算法;
Sharding-JDBC 定位爲輕量 Java 框架,使用客戶端直連數據庫,無需額外部署,無其他依賴,DBA 也無需改變原有的運維方式。
Sharding-JDBC 分片策略靈活,可支持等號、between、in 等多維度分片,也可支持多分片鍵。
SQL 解析功能完善,支持聚合、分組、排序、limit、or 等查詢,並支持 Binding Table 以及笛卡爾積表查詢。

Sharding-JDBC 直接封裝 JDBC API,可以理解爲增強版的 JDBC 驅動,舊代碼遷移成本幾乎爲零:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/8IFBCUIoFQxkk5l93FsxIQ