【ShardingSphere技術專題】「ShardingJDBC」(2) 進階領略一下數據分片的核心概念

前提介紹

承接上一篇文章:(【ShardingSphere技術專題】「ShardingJDBC」(1)帶你一同認識一下ShardingJDBC是什么?(高手勿入)),讓我們了解了相關ShardingJDBC的作用和功能定位,以及技術本質,針對于它最核心的分片技術,接下來我們需要針對于ShardingJDBC的分片的核心概念進行介紹!

ShardingJDBC-表模型

ShardingJDBC的邏輯表概念,屏蔽下面的分片技術!

邏輯表

  • 水平拆分的數據庫(表)的相同邏輯和數據結構表的總稱。例:用戶數據根據主鍵尾數拆分為 10 張表,分別是userinfo_0 到 userinfo_9,他們的邏輯表名為 userinfo。

物理表

  • 在分片的數據庫中真實存在的物理表。即上個示例中的 userinfo_0 到 userinfo_9。

數據節點

  • 數據分片的最小單元。由數據源名稱和數據表組成,例:ds0.userinfo_0。

綁定表

指分片規則一致的主表和子表。

  • 例如:userInfo 表和 userInfo_extend 表,均按照 user_id 分片,則此兩張表互為綁定表關系。

  • 綁定表之間的多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。

舉例說明,如果SQL 為:
SELECT i.* FROM userInfo o JOIN userInfo_extend i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);
  • 在不配置綁定表關系時,假設分片鍵user_id 將數值 10 路由至第 0 片,將數值 11 路由至第 1 片,那么路由后的 SQL 應該為 4 條,它們呈現為笛卡爾積:
SELECT i.* FROM userInfo_0 o JOIN userInfo_extend_0 i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);

SELECT i.* FROM userInfo_0 o JOIN userInfo_extend_1 i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);

SELECT i.* FROM userInfo_1 o JOIN userInfo_extend_0 i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);

SELECT i.* FROM userInfo_1 o JOIN userInfo_extend_1 i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);
  • 在配置綁定表關系后,路由的 SQL 應該為 2 條:
SELECT i.* FROM userInfo_0 o JOIN userInfo_extend_0 i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);

SELECT i.* FROM userInfo_1 o JOIN userInfo_extend_1 i ON o.user_id=i.user_id WHERE o.user_id in (10, 11);
  • 其中 userInfo 在 FROM 的最左側,ShardingSphere將會以它作為整個綁定表的主表。

  • 所有路由計算將會只使用主表的策略,那么 userInfo_extend 表的分片計算將會使用 userInfo 的條件。故綁定表之間的分區鍵要完全相同。

廣播表

指所有的分片數據源中都存在的表,表結構和表中的數據在每個數據庫中均完全一致。適用于數據量不大且需要與海量數據的表進行關聯查詢的場景,例如:字典表、配置信息表。

單表

指所有的分片數據源中只存在唯一一張的表。適用于數據量不大且不需要做任何分片操作的場景。

ShardingJDBC-分片配置

分片策略配置

對于分片策略存有數據源分片策略和表分片策略兩種維度。

分片鍵

主要用于分片的數據庫字段,是將數據庫(表)水平拆分的關鍵字段。例:將用戶表中的user主鍵的尾數取模分片,則user主鍵為分片字段。

SQL中如果無分片字段,將執行全路由,性能較差。 除了對單分片字段的支持,Apache ShardingSphere 也支持根據多個字段進行分片。

分片算法

通過分片算法將數據分片,支持通過 =、>=、<=、>、<、BETWEEN 和 IN 分片。 分片算法需要應用方開發者自行實現,可實現的靈活度非常高。

  • 目前提供3種分片算法。 由于分片算法和業務實現緊密相關,因此并未提供內置分片算法,而是通過分片策略將各種場景提煉出來,提供更高層級的抽象,并提供接口讓應用開發者自行實現分片算法。
標準分片算法(StandardShardingAlgorithm)

用于處理使用單一鍵作為分片鍵的 =、IN、BETWEEN AND、>、<、>=、<= 進行分片的場景,需要配合StandardShardingStrategy使用。

復合分片算法(ComplexKeysShardingAlgorithm)

用于處理使用多鍵作為分片鍵進行分片的場景,包含多個分片鍵的邏輯較復雜,需要應用開發者自行處理其中的復雜度。需要配合 ComplexShardingStrategy 使用。

Hint分片算法(HintShardingAlgorithm)

用于處理使用 Hint 行分片的場景。需要配合 HintShardingStrategy 使用,具體未來會進行詳細介紹!

分片策略

  • 包含分片鍵和分片算法,由于分片算法的獨立性,將其獨立抽離。

  • 真正可用于分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供 4 種分片策略。

標準分片策略

對應 StandardShardingStrategy:提供對 SQL 語句中的 =, >, <, >=, <=, IN 和 BETWEEN AND 的分片操作支持。

  • StandardShardingStrategy 只支持單分片鍵,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 兩個分片算法。
    • PreciseShardingAlgorithm 是必選的,用于處理 = 和 IN 的分片。
    • RangeShardingAlgorithm 是可選的,用于處理 BETWEEN AND, >, <, >=, <= 分片,如果不配置 RangeShardingAlgorithm,SQL 中的 BETWEEN AND 將按照全庫路由處理。
復合分片策略

對應ComplexShardingStrategy,復合分片策略,提供對 SQL 語句中的 =, >, <, >=, <=, IN和BETWEEN AND 的分片操作支持。

ComplexShardingStrategy支持多分片鍵,由于多分片鍵之間的關系復雜,因此并未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應用開發者實現,提供最大的靈活度。

Hint分片策略

對應 HintShardingStrategy。通過 Hint 指定分片值而非從 SQL 中提取分片值的方式進行分片的策略。

不分片策略

對應 NoneShardingStrategy。不分片的策略。

SQL Hint

對于分片字段非SQL決定,而由其他外置條件決定的場景,可使用 SQL Hint 靈活的注入分片字段。 例:內部系統,按照員工登錄主鍵分庫,而數據庫中并無此字段。SQL Hint 支持通過 Java API 和 SQL 注釋(待實現)兩種方式使用。

實現動機

  • 通過解析 SQL 語句提取分片鍵列與值并進行分片是 Apache ShardingSphere 對 SQL 零侵入的實現方式。若 SQL 語句中沒有分片條件,則無法進行分片,需要全路由。

  • 在一些應用場景中,分片條件并不存在于 SQL,而存在于外部業務邏輯。因此需要提供一種通過外部指定分片結果的方式,在 Apache ShardingSphere 中叫做 Hint。

實現機制

  • Apache ShardingSphere 使用 ThreadLocal 管理分片鍵值。可以通過編程的方式向 HintManager 中添加分片條件,該分片條件僅在當前線程內生效。

  • 除了通過編程的方式使用強制分片路由,Apache ShardingSphere 還計劃通過 SQL 中的特殊注釋的方式引用 Hint,使開發者可以采用更加透明的方式使用該功能。

  • 指定了強制分片路由的 SQL 將會無視原有的分片邏輯,直接路由至指定的真實數據節點。

分片規則

分片規則配置的總入口。包含數據源配置、表配置、綁定表配置以及讀寫分離配置等。

數據源配置

系統配置的相關的數據源信息列表。

表配置

  • 邏輯表名稱、數據節點與分表規則的配置。

數據節點配置

  • 用于配置邏輯表與真實表的映射關系。可分為均勻分布和自定義分布兩種形式。

均勻分布

  • 指數據表在每個數據源內呈現均勻分布的態勢,例如:
db0
  ├── userInfo_0 
  └── userInfo_1 
db1
  ├── userInfo_0 
  └── userInfo_1
  • 那么數據節點的配置如下:
db0.userInfo_0, db0.userInfo_1, db1.userInfo_0, db1.userInfo_1

自定義分布

指數據表呈現有特定規則的分布,例如:

db0
  ├── userInfo_0 
  └── userInfo_1 
db1
  ├── userInfo_3
  └── userInfo_4

那么數據節點的配置如下:

db0.userInfo_0, db0.userInfo_1, db1.userInfo_3, db1.userInfo_4
數據源分片策略

對應于DatabaseShardingStrategy。用于配置數據被分配的目標數據源。

表分片策略

對應于TableShardingStrategy。用于配置數據被分配的目標表,該目標表存在于該數據的目標數據源內。故表分片策略是依賴于數據源分片策略的結果的。

兩種策略的 API 完全相同。

自增主鍵生成策略

通過在客戶端生成自增主鍵替換以數據庫原生自增主鍵的方式,做到分布式主鍵無重復。

分片計算表達式

實現動機

配置的簡化與一體化是行表達式所希望解決的兩個主要問題。

  • 在繁瑣的數據分片規則配置中,隨著數據節點的增多,大量的重復配置使得配置本身不易被維護。通過行表達式可以有效地簡化數據節點配置工作量。

  • 對于常見的分片算法,使用 Java 代碼實現并不有助于配置的統一管理。通過行表達式書寫分片算法,可以有效地將規則配置一同存放,更加易于瀏覽與存儲。

語法說明

目前支持數據節點和分片算法這兩個部分的配置。

行表達式的內容使用的是 Groovy 的語法,Groovy 能夠支持的所有操作,行表達式均能夠支持。只需要在配置中使用 { expression } 或->{ expression } 標識行表達式即可。例如:

${begin..end} 表示范圍區間

${[unit1, unit2, unit_x]} 表示枚舉值

行表達式中如果出現連續多個{ expression } 或->{ expression } 表達式,整個表達式最終的結果將會根據每個子表達式的結果進行笛卡爾組合。

行表達式案例
${['a', 'b']}_table${1..3}

最終會解析為:

a_table1, a_table2, a_table3, b_table1, b_table2, b_table3
配置數據節點

對于均勻分布的數據節點,如果數據結構如下:

db0
  ├── userInfo_0 
  └── userInfo_1 
db1
  ├── userInfo_3
  └── userInfo_4
db0.userinfo_${0..1},db1.userinfo_${0..1}

用行表達式可以簡化為:

db${0..1}.userinfo_${0..1}
  • 或者
db$->{0..1}.userinfo_$->{0..1}

對于自定義的數據節點,如果數據結構如下:

db0
  ├── userInfo_0 
  └── userInfo_1 
db1
  ├── userInfo_0
  └── userInfo_1

可以使用分開配置的方式,先配置包含前綴的數據節點,再配置不含前綴的數據節點,再利用行表達式笛卡爾積的特性,自動組合即可。 上面的示例,用行表達式可以簡化為:

db${0..1}.t_order_0${0..9}, db${0..1}.t_order_${10..20}
或者
db$->{0..1}.t_order_0$->{0..9}, db$->{0..1}.t_order_$->{10..20}

配置分片算法

  • 對于只有一個分片鍵的使用 = 和 IN 進行分片的 SQL,可以使用行表達式代替編碼方式配置。

  • 行表達式內部的表達式本質上是一段 Groovy 代碼,可以根據分片鍵進行計算的方式,返回相應的真實數據源或真實表名稱。

例如:分為 10 個庫,尾數為 0 的路由到后綴為 0 的數據源, 尾數為 1 的路由到后綴為 1 的數據源,以此類推。用于表示分片算法的行表達式為:

ds${id % 10}

或者

ds$->{id % 10}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容