天天看點

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

轉自:https://kefeng.wang/2018/07/22/mysql-sharding/ 

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

1 分庫分表概述

在業務量不大時,單庫單表即可支撐。

當資料量過大存儲不下、或者并發量過大負荷不起時,就要考慮分庫分表。

1.1 分庫分表相關術語

讀寫分離: 不同的資料庫,同步相同的資料,分别隻負責資料的讀和寫;

分區: 指定分區清單達式,把記錄拆分到不同的區域中(必須是同一伺服器,可以是不同硬碟),應用看來還是同一張表,沒有變化;

分庫:一個系統的多張資料表,存儲到多個資料庫執行個體中;

分表: 對于一張多行(記錄)多列(字段)的二維資料表,又分兩種情形:

(1) 垂直分表: 豎向切分,不同分表存儲不同的字段,可以把不常用或者大容量、或者不同業務的字段拆分出去;

(2) 水準分表(最複雜): 橫向切分,按照特定分片算法,不同分表存儲不同的記錄。

1.2 真的要采用分庫分表?

需要注意的是,分庫分表會為資料庫維護和業務邏輯帶來一系列複雜性和性能損耗,除非預估的業務量大到萬不得已,切莫過度設計、過早優化。

規劃期内的資料量和性能問題,嘗試能否用下列方式解決:

目前資料量:如果沒有達到幾百萬,通常無需分庫分表;

資料量問題:增加磁盤、增加分庫(不同的業務功能表,整表拆分至不同的資料庫);

性能問題:更新CPU/記憶體、讀寫分離、優化資料庫系統配置、優化資料表/索引、優化 SQL、分區、資料表的垂直切分;

如果仍未能奏效,才考慮最複雜的方案:資料表的水準切分。

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; ## 步長增量

1

2

3

4

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

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) 的數值。

其特性是各節點無需協調、按時間大緻有序、且整個叢集各節點單不重複。

該數值的預設組成如下(符号位之外的三部分允許個性化調整):

1bit: 符号位,總是 0(為了保證數值是正數)。

41bit: 毫秒數(可用 69 年);

10bit: 節點ID(5bit資料中心 + 5bit節點ID,支援 32 * 32 = 1024 個節點)

12bit: 流水号(每個節點每毫秒内支援 4096 個 ID,相當于 409萬的 QPS,相同時間内如 ID 遇翻轉,則等待至下一毫秒)

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 個節點):

無需停止應用伺服器;

新增兩個資料庫 A2/B2 作為從庫,設定主從同步關系為:A=>A2、B=>B2,直至主從資料同步完畢(早期資料可手工同步);

調整分片規則并使之生效:

原 ID%2=0 => A 改為 ID%4=0 => A, ID%4=2 => A2;

原 ID%2=1 => B 改為 ID%4=1 => B, ID%4=3 => B2。

解除資料庫執行個體的主從同步關系,并使之生效;

此時,四個節點的資料都已完整,隻是有備援(多存了和自己配對的節點的那部分資料),擇機清除即可(過後随時進行,不影響業務)。

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驅動,舊代碼遷移成本幾乎為零:

可适用于任何基于Java的ORM架構,如JPA、Hibernate、Mybatis、Spring JDBC Template或直接使用JDBC。

可基于任何第三方的資料庫連接配接池,如DBCP、C3P0、 BoneCP、Druid等。

理論上可支援任意實作JDBC規範的資料庫。雖然目前僅支援MySQL,但已有支援Oracle、SQLServer等資料庫的計劃。

轉自:https://kefeng.wang/2018/07/22/mysql-sharding/ 

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