一口氣搞懂分庫分表 12 種分片算法,大廠都在用

大家好,我是小富~

前言

本文是《ShardingSphere5.x分庫分表原理與實戰》系列的第五篇文章,我們一起梳理下ShardingSphere框架中的核心部分分片策略分片算法,其內部針爲我們提供了多種分片策略和分片算法,來應對不同的業務場景,本着拿來即用的原則。

這次將詳細介紹如何在ShardingSphere-jdbc中實戰 5 種分片策略和 12 種分片算法,自定義分片算法,比較它們的應用場景以及優劣。

全部demo案例 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-algorithms

分片策略

分片策略是分片鍵分片算法的組合策略,真正用於實現數據分片操作的是分片鍵與相應的分片算法。在分片策略中,分片鍵確定了數據的拆分依據,分片算法則決定了如何對分片鍵值運算,將數據路由到哪個物理分片中。

由於分片算法的獨立性,使得分片策略具有更大的靈活性和可擴展性。這意味着可以根據具體需求選擇不同的分片算法,或者開發自定義的分片算法,以適應各種不同的分片場景。在分表和分庫時使用分片策略和分片算法的方式是一致的

注意:如果在某種分片策略中使用了不受支持的SQL操作符,比如 MYSQL 某些函數等,那麼系統將無視分片策略,進行全庫表路由操作。這個在使用時要慎重!

ShardingSphere對外提供了standardcomplexhintinlinenone5種分片策略。不同的分片策略可以搭配使用不同的分片算法,這樣可以靈活的應對複雜業務場景。

標準分片策略

標準分片策略(standard)適用於具有單一分片鍵的標準分片場景。該策略支持精確分片,即在SQL中包含=in操作符,以及範圍分片,包括BETWEEN AND><>=<=等範圍操作符。

該策略下有兩個屬性,分片字段shardingColumn和分片算法名shardingAlgorithmName

spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order: # 邏輯表名稱
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${1..10}
            # 分庫策略
            databaseStrategy: # 分庫策略
              standard: # 用於單分片鍵的標準分片場景
                shardingColumn: order_id # 分片列名稱
                shardingAlgorithmName: # 分片算法名稱
           tableStrategy: # 分表策略,同分庫策略

行表達式分片策略

行表達式分片策略(inline)適用於具有單一分片鍵的簡單分片場景,支持SQL語句中=in操作符。

它的配置相當簡潔,該分片策略支持在配置屬性algorithm-expression中書寫Groovy表達式,用來定義對分片健的運算邏輯,無需單獨定義分片算法了。

spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order: # 邏輯表名稱
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${1..10}
            # 分庫策略
            databaseStrategy: # 分庫策略
              inline:   # 行表達式類型分片策略
                algorithm-expression: db$->{order_id % 2} Groovy表達式
            tableStrategy: # 分表策略,同分庫策略

複合分片策略

複合分片策略(complex)適用於多個分片鍵的複雜分片場景,屬性shardingColumns中多個分片健以逗號分隔。支持 SQL 語句中有>>=<=<=INBETWEEN AND 等操作符。

比如:我們希望通過user_idorder_id等多個字段共同運算得出數據路由到具體哪個分片中,就可以應用該策略。

spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order: # 邏輯表名稱
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${1..10}
            # 分庫策略
            databaseStrategy: # 分庫策略
              complex: # 用於多分片鍵的複合分片場景
                shardingColumns: order_id,user_id # 分片列名稱,多個列以逗號分隔
                shardingAlgorithmName: # 分片算法名稱
            tableStrategy: # 分表策略,同分庫策略

Hint分片策略

Hint強制分片策略相比於其他幾種分片策略稍有不同,該策略無需配置分片健,由外部指定分庫和分表的信息,可以讓SQL在指定的分庫、分表中執行。

使用場景:

  • 分片字段不存在SQL和數據庫表結構中,而存在於外部業務邏輯。
  • 強制在指定數據庫進行某些數據操作。

比如,我們希望用user_id做分片健進行路由訂單數據,但是t_order表中也沒user_id這個字段啊,這時可以通過Hint API手動指定分片庫、表等信息,強制讓數據插入指定的位置。

spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order: # 邏輯表名稱
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${1..10}
            # 分庫策略
            databaseStrategy: # 分庫策略
              hint: # Hint 分片策略
                shardingAlgorithmName: # 分片算法名稱
            tableStrategy: # 分表策略,同分庫策略

不分片策略

不分片策略比較好理解,設置了不分片策略,那麼對邏輯表的所有操作將會執行全庫表路由。

spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order: # 邏輯表名稱
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${1..10}
            # 分庫策略
            databaseStrategy: # 分庫策略
              none: # 不分片
           tableStrategy: # 分表策略,同分庫策略

分片算法

ShardingSphere 內置了多種分片算法,按照類型可以劃分爲自動分片算法標準分片算法複合分片算法Hint 分片算法,能夠滿足我們絕大多數業務場景的需求。

此外,考慮到業務場景的複雜性,內置算法也提供了自定義分片算法的方式,我們可以通過編寫 Java代碼來完成複雜的分片邏輯。下邊逐個算法實踐一下,看看每種算法的實際執行效果。

開始前,我要吐槽下官方文檔,對於算法這種至關重要的內容,解釋描述的過於潦草,對於新手入門不友好,學習成本偏高啊

準備工作

給邏輯表配置完算法後,先執行創建表的SQL,這樣就可以依據你的算法在db內快速生成分片表,所以不要總是問我要表結構了,哈哈哈。如果有不明白的小夥伴可以看我上一篇對於autoTable的介紹 分庫分表如何管理不同實例中幾萬張分片表?

自動分片算法

1、MOD

取模分片算法是內置的一種比較簡單的算法,定義算法時類型MOD,表達式大致(分片健/數據庫實例) % sharding-count,它只有一個 props 屬性sharding-count代表分片表的數量。

這個 sharding-count 數量使用時有點小坑,比如db0db1都有分片表t_order_1,那麼實際上數量只能算一個。YML核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 自動分片表規則配置
        auto-tables:
          t_order:
            actual-data-sources: db$->{0..1}
            sharding-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_table_mod
        # 分片算法定義
        sharding-algorithms:
          t_order_table_mod:
            type: MOD # 取模分片算法
            props:
              # 指定分片數量
              sharding-count: 6
        tables:
          t_order: # 邏輯表名稱
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
            ....
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_table_mod

2、HASH_MOD

哈希取模分片算法是內置取模分片算法的一個升級版本,定義算法時類型HASH_MOD,也只有一個props屬性sharding-count代表分片的數量。表達式hash(分片健/數據庫實例) % sharding-count

YML核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 自動分片表規則配置
        auto-tables:
          t_order:
            actual-data-sources: db$->{0..1}
            sharding-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_table_hash_mod
        # 分片算法定義
        sharding-algorithms:
          t_order_table_hash_mod:
            type: HASH_MOD # 哈希取模分片算法
            props:
              # 指定分片數量
              sharding-count: 6
        tables:
          t_order: # 邏輯表名稱
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
            ....
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_table_hash_mod

3、VOLUME_RANGE

基於分片容量的範圍分片算法,依據數據容量來均勻分佈到分片表中。

它適用於數據增長趨勢相對均勻,按分片容量將數據均勻地分佈到不同的分片表中,可以有效避免數據傾斜問題;由於數據已經被按照範圍進行分片,支持頻繁進行範圍查詢場景。

不僅如此,該算法支持動態的分片調整,可以根據實際業務數據的變化動態調整分片容量和範圍,使得系統具備更好的擴展性和靈活性。

VOLUME_RANGE算法主要有三個屬性:

看完是不是一臉懵逼,上界下界都是什麼含義,我們實際使用一下就清晰了。爲t_order邏輯表設置VOLUME_RANGE分片算法,range-lower下界數爲 2,range-upper上界數爲 20,分量容量sharding-volume 10。

yml核心配置如下:

# 分片算法定義
spring:
  shardingsphere:
    rules:
      sharding:
        # 自動分片表規則配置
        auto-tables:
          t_order:
            actual-data-sources: db$->{0..1}
            sharding-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_table_volume_range
        sharding-algorithms:
          t_order_table_volume_range:
            type: VOLUME_RANGE
            props:
              range-lower: 2 # 範圍下界,超過邊界的數據會報錯
              range-upper: 20 # 範圍上界,超過邊界的數據會報錯
              sharding-volume: 10 # 分片容量
        tables:
          t_order: # 邏輯表名稱
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
            ....
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_table_volume_range

這個配置的意思就是說,分片健t_order_id的值在界值 [range-lower,range-upper) 範圍內,每個分片表最大存儲 10 條數據;低於下界的值 [ 1,2 ) 數據分佈到 t_order_0,在界值範圍內的數據 [ 2,20 ) 遵循每滿足 10 條依次放入 t_order_1、t_order_2;超出上界的數據[ 20,∞ ) 即便前邊的分片表裏未滿 10條剩下的也全部放在 t_order_3。

那麼它的數據分佈應該如下:

  • [ 0,2 )數據分佈到 t_order_0

  • [ 2,12 )數據分佈到 t_order_1

  • [ 12,20 )數據分佈到 t_order_2

  • [ 20,∞ )數據分佈到 t_order_3

基於分片容量的範圍分片算法

接着準備插入40條數據,其中分片健字段t_order_id值從1~40,我們看到實際插入庫的數據和上邊配置的規則是一致的。超出range-lowerrange-upper邊界的部分數據,比如:t_order_2表未滿 10條也不再插入,全部放入了t_order_3分片表中。

4、BOUNDARY_RANGE

基於分片邊界的範圍分片算法,和分片容量算法不同,這個算法根據數據的取值範圍進行分片,特別適合按數值範圍頻繁查詢的場景。該算法只有一個屬性sharding-ranges爲分片健值的範圍區間。

比如,我們配置sharding-ranges=10,20,30,40,它的範圍默認是從 0開始,範圍區間前閉後開。配置算法以後執行建表語句,生成數據節點分佈如:

db0-
   |_t_order_0
   |_t_order_2
   |_t_order_4
db1-
   |_t_order_1
   |_t_order_3

那麼它的數據分佈應該如下:

[ 0,10 )數據分佈到t_order_0,

[ 10,20 )數據分佈到t_order_1,

[ 20,30 )數據分佈到t_order_2,

[ 30,40 )數據分佈到t_order_3,

[ 40,∞ )數據分佈到t_order_4。

基於分片邊界的範圍分片算法

BOUNDARY_RANGE算法的YML核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 自動分片表規則配置
        auto-tables:
          t_order:
            actual-data-sources: db$->{0..1}
            sharding-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_table_boundary_range
        sharding-algorithms:
          # 基於分片邊界的範圍分片算法
          t_order_table_boundary_range:
            type: BOUNDARY_RANGE
            props:
              sharding-ranges: 10,20,30,40 # 分片的範圍邊界,多個範圍邊界以逗號分隔
        tables:
          t_order: # 邏輯表名稱
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
            ....
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_table_boundary_range

也插入40條數據,其中分片健字段t_order_id值從1~40,和上邊分析的數據分佈結果大致相同。看到第一張分片表中 t_order_0 只有 9 條數據,這是因爲咱們插入數據的分片健值是從 1 開始,但算法是從 0 開始計算。

5、AUTO_INTERVAL

自動時間段分片算法,適用於以時間字段作爲分片健的分片場景,和VOLUME_RANGE基於容量的分片算法用法有點類似,不同的是AUTO_INTERVAL依據時間段進行分片。主要有三個屬性datetime-lower分片健值開始時間(下界)、datetime-upper分片健值結束時間(上界)、sharding-seconds單一分片表所能容納的時間段。

這裏分片健已經從t_order_id替換成了order_date。現在屬性 datetime-lower 設爲 2023-01-01 00:00:00,datetime-upper 設爲 2025-01-01 00:00:00,sharding-seconds爲 31536000 秒(一年)。策略配置上有些改動,將分庫和分表的算法全替換成AUTO_INTERVAL

YML核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 自動分片表規則配置
        auto-tables:
          t_order:
            actual-data-sources: db$->{0..1}
            sharding-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_table_auto_interval
        # 分片算法定義
        sharding-algorithms:
          # 自動時間段分片算法
          t_order_table_auto_interval:
            type: AUTO_INTERVAL
            props:
              datetime-lower: '2023-01-01 00:00:00' # 分片的起始時間範圍,時間戳格式:yyyy-MM-dd HH:mm:ss
              datetime-upper: '2025-01-01 00:00:00' #  分片的結束時間範圍,時間戳格式:yyyy-MM-dd HH:mm:ss
              sharding-seconds: 31536000 # 單一分片所能承載的最大時間,單位:秒,允許分片鍵的時間戳格式的秒帶有時間精度,但秒後的時間精度會被自動抹去
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
              standard:
                sharding-column: order_date
                sharding-algorithm-name: t_order_table_auto_interval
            # 分表策略
#            table-strategy:
#              standard:
#                sharding-column: order_date
#                sharding-algorithm-name: t_order_table_auto_interval

只要你理解了上邊 VOLUME_RANGE 算法的數據分佈規則,那麼這個算法也很容易明白,分片健值在界值範圍內 [datetime-lower,datetime-upper) 遵循每滿足 sharding-seconds 時間段的數據放入對應分片表,超出界值的數據上下順延到其他分片中。

它的數據分佈應該如下:

  • [ 2023-01-01 00:00:00,2024-01-01 00:00:00 )數據分佈到 t_order_0,

  • [ 2024-01-01 00:00:00,2025-01-01 00:00:00 )數據分佈到 t_order_1,

  • [ 2025-01-01 00:00:00,2026-01-01 00:00:00 )數據分佈到 t_order_2。

  • [ 2026-01-01 00:00:00,∞ )數據分佈到 t_order_3。

爲了方便測試,手動執行插入不同日期的數據,按照上邊配置的規則應該t_order_0會有一條 23 年的數據,t_order_1 中有兩條 24 年的數據,t_order_2 中有兩條 25 年的數據,t_order_3 中有兩條 26、27 年的數據。

// 放入 t_order_0 分片表
INSERT INTO `t_order` VALUES (1, '2023-03-20 00:00:00', 1, '1', 1, 1.00);
// 放入 t_order_1 分片表
INSERT INTO `t_order` VALUES (2, '2024-03-20 00:00:00', 2, '2', 2,1.00);
INSERT INTO `t_order` VALUES (3, '2024-03-20 00:00:00', 3, '3', 3, 1.00);
// 放入 t_order_2 分片表
INSERT INTO `t_order` VALUES (4,'2025-03-20 00:00:00',4, '4', 4, 1.00);
INSERT INTO `t_order` VALUES (5,'2025-03-20 00:00:00',5, '5', 5,  1.00);
// 放入 t_order_3 分片表
INSERT INTO `t_order` VALUES (6,'2026-03-20 00:00:00',6, '6', 6,  1.00);
INSERT INTO `t_order` VALUES (7,'2027-03-20 11:19:58',7, '7', 7,  1.00);

查看實際的數據分佈情況和預想的結果完全一致,至此內置算法全部使用大成。

標準分片算法

6、INLINE

行表達式分片算法,適用於比較簡單的分片場景,利用Groovy表達式在算法屬性內,直接書寫分片邏輯,省卻了配置和代碼開發,只支持SQL語句中的 = 和 IN 的分片操作,只支持單分片鍵

該算法有兩屬性:

  • algorithm-expression:編寫Groovy的表達式,比如: t_order_$->{t_order_id % 3} 表示根據分片健 t_order_id 取模獲得 3 張 t_order 分片表 t_order_0 到 t_order_2。

  • allow-range-query-with-inline-sharding:由於該算法只支持含有 = 和 IN 操作符的SQL,一旦SQL使用了範圍查詢 >、< 等操作會報錯。要想執行範圍查詢成功,該屬性開啓爲true即可,一旦開啓範圍查詢會無視分片策略,進行全庫表路由查詢,這個要慎重開啓

YML核心配置如下:

spring:
  shardingsphere:
    # 具體規則配置
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          # 標準分片算法
          # 行表達式分片算法
          t_order_table_inline:
            type: INLINE
            props:
              algorithm-expression:	t_order_$->{order_id % 3} # 分片算法的行表達式
              allow-range-query-with-inline-sharding: false # 是否允許範圍查詢。注意:範圍查詢會無視分片策略,進行全路由,默認 false
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_database_algorithms
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_table_inline

7、INTERVAL

時間範圍分片算法,針對於時間字段(字符串類型)作爲分片健的範圍分片算法,適用於按照天、月、年這種固定區間的數據分片。
上邊使用其它時間分片算法時,用的都是t_order_n後綴編號格式的分片表。但業務上往往需要的可能是按月、年t_order_yyyyMM的這種分片表格式。

時間範圍分片算法(INTERVAL),可以輕鬆實現這種場景,它的屬性比較多,逐個解釋下:

  • datetime-pattern:分片健值的時間格式,必須是Java DateTimeFormatter類支持的轉換類型
  • datetime-lower:分片健值的下界,超過會報錯,格式必須與datetime-pattern一致
  • datetime-upper:分片健值的上界,超過會報錯,格式必須與datetime-pattern一致
  • sharding-suffix-pattern:分片表後綴名格式,yyyyMM、yyyyMMdd等格式,分片表格式的定義要結合datetime-interval-unit的單位,比如:t_order_yyyyMM格式表示分片表存的月的數據,t_order_yyyy格式表示分片表存的年的數據;
  • datetime-interval-unit:分片間隔單位,超過該時間間隔將進入下一分片。它遵循 Java ChronoUnit 枚舉,比如:MONTHSDAYS等;
  • datetime-interval-amount:分片間隔數,和datetime-interval-unit是緊密配合使用;

接下來實現個按月存儲數據的場景,用t_order_202401t_order_202406 6張分片表存儲前半年的數據,每張分片表存儲一個月的數據。interval_value字段作爲分片健,時間字符串類型,允許的分片值時間範圍 2024-01-01 00:00:00~2024-06-30 23:59:59 不在範圍內插入報錯。

spring:
  shardingsphere:
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          t_order_database_mod:
            type: MOD
            props:
              sharding-count: 2 # 指定分片數量
          t_order_table_interval:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd HH:mm:ss"  # 分片字段格式
              datetime-lower: "2024-01-01 00:00:00"  # 範圍下限
              datetime-upper: "2024-06-30 23:59:59"  # 範圍上限
              sharding-suffix-pattern: "yyyyMM"  # 分片名後綴,可以是MM,yyyyMMdd等。
              datetime-interval-amount: 1  # 分片間隔,這裏指一個月
              datetime-interval-unit: "MONTHS" # 分片間隔單位
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${202401..202406}
            # 分庫策略
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_database_mod
            # 分表策略
            table-strategy:
              standard:
                sharding-column: interval_value
                sharding-algorithm-name: t_order_table_interval
            keyGenerateStrategy:
              column: id
              keyGeneratorName: t_order_snowflake

配置完成後插入測試數據 1月~7月,正常情況下前 6 個月的數據會正常插入,超過界值的 7月數據應該會報錯。

// 放入 t_order_202401 分片表
INSERT INTO `t_order` VALUES (1, 1, '1', 1, 1.00, '2024-01-01 00:00:00', 1);
// 放入 t_order_202402 分片表
INSERT INTO `t_order` VALUES (2, 2, '2', 2, 1.00, '2024-02-01 00:00:00', 1);
// 放入 t_order_202403 分片表
INSERT INTO `t_order` VALUES (3, 3, '3', 3, 1.00, '2024-03-01 00:00:00', 1);
// 放入 t_order_202404 分片表
INSERT INTO `t_order` VALUES (4, 4, '4', 4, 1.00, '2024-04-01 00:00:00', 1);
// 放入 t_order_202405 分片表
INSERT INTO `t_order` VALUES (5, 5, '5', 5, 1.00, '2024-05-01 00:00:00', 1);
// 放入 t_order_202406 分片表
INSERT INTO `t_order` VALUES (6, 6, '6', 6, 1.00, '2024-06-01 00:00:00', 1);

// 插入會報錯
INSERT INTO `t_order` VALUES (7, 7, '7', 7, 1.00, '2024-07-01 00:00:00', 1);

看到實際的入庫的效果和預期的一致,一月的數據存到t_order_202401,二月的數據存到t_order_202402~,在插入 7月數據的時候報錯了。

COSID 類型算法

ShardingSphere 提供了三種基於散列散列算法的CosId(它是一款性能極高分佈式ID生成器)分片算法,這個算法的核心思想是通過散列算法對CosId生成的分佈式ID和分片鍵值進行處理,以確定數據應該存放在哪個具體的數據節點上。

使用散列算法的優勢,可以將數據按照一定規則映射到不同的數據節點上,能夠確保數據的均勻分佈,避免某些節點負載過重或者數據傾斜的情況。

三個算法與其他分片算法主要區別在於底層的實現,在配置上使用上基本沒太多區別。

8、COSID_MOD

基於 CosId 的取模分片算法和普通的MOD算法使用上略有不同,mod爲分片數量,logic-name-prefix分片數據源或真實表的前綴格式。

yml核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          t_order_database_mod:
            type: MOD
            props:
              sharding-count: 2 # 指定分片數量
          # 8、基於 CosId 的取模分片算法
          t_order_table_cosid_mod:
            type: COSID_MOD
            props:
              mod: 3  # 分片數量
              logic-name-prefix: t_order_ # 分片數據源或真實表的前綴格式
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_database_mod
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_table_cosid_mod
            keyGenerateStrategy:
              column: id
              keyGeneratorName: t_order_snowflake

9、COSID_INTERVAL

基於 CosId 的固定時間範圍的分片算法,和INTERVAL算法的用法很相似,不同點在於增加了zone-id時區屬性,logic-name-prefix分片數據源或真實表的前綴格式,上下界datetime-lowerdatetime-upper範圍的時間格式是固定的yyyy-MM-dd HH:mm:ss

yml核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          t_order_database_mod:
            type: MOD
            props:
              sharding-count: 2 # 指定分片數量
          # 基於 CosId 的固定時間範圍的分片算法
          t_order_table_cosid_interval:
            type: COSID_INTERVAL
            props:
              zone-id: "Asia/Shanghai" # 時區,必須遵循 java.time.ZoneId 的所含值。 例如:Asia/Shanghai
              logic-name-prefix: t_order_ # 分片數據源或真實表的前綴格式
              sharding-suffix-pattern: "yyyyMM" # 分片數據源或真實表的後綴格式,必須遵循 Java DateTimeFormatter 的格式,必須和 datetime-interval-unit 保持一致。例如:yyyyMM
              datetime-lower: "2024-01-01 00:00:00" # 時間分片下界值,格式與 yyyy-MM-dd HH:mm:ss 的時間戳格式一致
              datetime-upper: "2024-12-31 00:00:00" # 時間分片上界值,格式與 yyyy-MM-dd HH:mm:ss 的時間戳格式一致
              datetime-interval-unit: "MONTHS" # 分片鍵時間間隔單位,必須遵循 Java ChronoUnit 的枚舉值。例如:MONTHS
              datetime-interval-amount: 1 # 分片鍵時間間隔,超過該時間間隔將進入下一分片
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${202401..202412}
            # 分庫策略
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_database_mod
            # 分表策略
            table-strategy:
              standard:
                sharding-column: interval_value
                sharding-algorithm-name: t_order_table_cosid_interval
            keyGenerateStrategy:
              column: id
              keyGeneratorName: t_order_snowflake

10、COSID_INTERVAL_SNOWFLAKE

基於 CosId 的雪花ID固定時間範圍的分片算法,和上邊的COSID_INTERVAL算法不同之處在於,底層用於散列的COSID的生成方式是基於雪花算法(Snowflake),內部結合了時間戳、節點標識符和序列號等,這樣有助於數據分佈更均勻些。

使用除了type類型不同COSID_INTERVAL_SNOWFLAKE外,其他屬性用法和COSID_INTERVAL完全一致。yml核心配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          t_order_database_mod:
            type: MOD
            props:
              sharding-count: 2 # 指定分片數量
          # 基於 CosId 的固定時間範圍的分片算法
          t_order_table_cosid_interval_snowflake:
            type: COSID_INTERVAL_SNOWFLAKE
            props:
              zone-id: "Asia/Shanghai" # 時區,必須遵循 java.time.ZoneId 的所含值。 例如:Asia/Shanghai
              logic-name-prefix: t_order_ # 分片數據源或真實表的前綴格式
              sharding-suffix-pattern: "yyyyMM" # 分片數據源或真實表的後綴格式,必須遵循 Java DateTimeFormatter 的格式,必須和 datetime-interval-unit 保持一致。例如:yyyyMM
              datetime-lower: "2024-01-01 00:00:00" # 時間分片下界值,格式與 yyyy-MM-dd HH:mm:ss 的時間戳格式一致
              datetime-upper: "2024-12-31 00:00:00" # 時間分片上界值,格式與 yyyy-MM-dd HH:mm:ss 的時間戳格式一致
              datetime-interval-unit: "MONTHS" # 分片鍵時間間隔單位,必須遵循 Java ChronoUnit 的枚舉值。例如:MONTHS
              datetime-interval-amount: 1 # 分片鍵時間間隔,超過該時間間隔將進入下一分片
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${202401..202412}
            # 分庫策略
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: t_order_database_mod
            # 分表策略
            table-strategy:
              standard:
                sharding-column: interval_value
                sharding-algorithm-name: t_order_table_cosid_interval_snowflake
            keyGenerateStrategy:
              column: id
              keyGeneratorName: t_order_snowflake

複合分片算法

11、COMPLEX_INLINE

複合行表達式分片算法,適用於多分片健的簡單分片場景,和行表達式分片算法使用的方式基本一樣。多了一個屬性sharding-columns分片列名稱,多個列用逗號分隔。特別注意:使用多分片鍵複合算法,一定要基於複合分片策略進行設置

我們對現有的分庫分表算法進行了改進,將分片策略修改爲complexsharding-columns單個分片鍵升級爲多個分片鍵逗號分隔。例如,將分庫表達式從db$->{order_id % 2}調整爲db$->{(order_id + user_id) % 2},就實現了多個分片鍵的應用。

yml核心的配置如下:

spring:
  shardingsphere:
    # 具體規則配置
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          t_order_database_complex_inline_algorithms:
            type: COMPLEX_INLINE
            props:
              sharding-columns: order_id, user_id # 分片列名稱,多個列用逗號分隔。
              algorithm-expression: db$->{(order_id + user_id) % 2} # 分片算法的行表達式
              allow-range-query-with-inline-sharding: false # 是否允許範圍查詢。注意:範圍查詢會無視分片策略,進行全路由,默認 false
          # 11、複合行表達式分片算法
          t_order_table_complex_inline:
            type: COMPLEX_INLINE
            props:
              sharding-columns: order_id, user_id # 分片列名稱,多個列用逗號分隔。
              algorithm-expression: t_order_$->{ (order_id + user_id) % 3 } # 分片算法的行表達式
              allow-range-query-with-inline-sharding: false # 是否允許範圍查詢。注意:範圍查詢會無視分片策略,進行全路由,默認 false
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
              complex:
                shardingColumns: order_id, user_id
                sharding-algorithm-name: t_order_database_complex_inline_algorithms
            # 分表策略
            table-strategy:
              complex:
                shardingColumns: order_id, user_id
                sharding-algorithm-name: t_order_table_complex_inline
            keyGenerateStrategy:
              column: id
              keyGeneratorName: t_order_snowflake

Hint 分片算法

12、HINT_INLINE

Hint 行表達式分片算法(強制路由分片算法),允許我們指定數據分佈的分片庫和分表的位置。這個算法只有一個屬性algorithm-expression,直接利用Groovy表達式在其中書寫分片邏輯。

如果想要向db0.t_order_1分片表中插入一條數據,但我的 Insert SQL 中並沒有分片健呀,執意執行插入操作可能就會導致全庫表路由,插入的數據就會重複,顯然是不能接受的。Hint 算法可以很好的解決此場景。

HINT_INLINE 算法一定要在 HINT 分片策略內使用,否則會報錯。

核心的配置如下:其中兩個表達式db$->{Integer.valueOf(value) % 2}t_order_$->{Integer.valueOf(value) % 3}中的value值分別是我們通過 Hint API 傳入的分庫值和分表值。

spring:
  shardingsphere:
    rules:
      sharding:
        # 分片算法定義
        sharding-algorithms:
          # Hint 行表達式分片算法
          t_order_database_hint_inline:
            type: HINT_INLINE
            props:
              algorithm-expression: db$->{Integer.valueOf(value) % 2} # 分片算法的行表達式,默認值${value}
          t_order_table_hint_inline:
            type: HINT_INLINE
            props:
              algorithm-expression: t_order_$->{Integer.valueOf(value) % 3} # 分片算法的行表達式,默認值${value}
        tables:
          # 邏輯表名稱
          t_order:
            # 數據節點:數據庫.分片表
            actual-data-nodes: db$->{0..1}.t_order_${0..2}
            # 分庫策略
            database-strategy:
              hint:
                sharding-algorithm-name: t_order_database_hint_inline
            # 分表策略
            table-strategy:
              hint:
                sharding-algorithm-name: t_order_table_hint_inline
            keyGenerateStrategy:
              column: id
              keyGeneratorName: t_order_snowflake

配置完分片算法,如何將value值傳遞進來?通過HintManager設置邏輯表的分庫addDatabaseShardingValue、分表addTableShardingValue,強制數據分佈到指定位置。

@DisplayName("測試 hint_inline 分片算法插入數據")
@Test
public void insertHintInlineTableTest() {
      HintManager hintManager = HintManager.getInstance();
      hintManager.clearShardingValues();
      // 設置邏輯表 t_order 的分庫值
      hintManager.addDatabaseShardingValue("t_order", 0);
      // 設置邏輯表 t_order 的分表值
      hintManager.addTableShardingValue("t_order", 1);
      // 1%3 = 1 所以放入 db0.t_order_1 分片表
      jdbcTemplate.execute("INSERT INTO `t_order`(`id`,`order_date`,`order_id`, `order_number`, `customer_id`, `total_amount`, `interval_value`, `user_id`) VALUES (1, '2024-03-20 00:00:00', 1, '1', 1, 1.00, '2024-01-01 00:00:00', 1);");
      hintManager.close();
}

ShardingSphere 通過使用ThreadLocal管理強制路由配置,可以通過編程的方式向HintManager中添加分片值,該分片值僅在當前線程內生效。

  • HintManager.getInstance() 獲取 HintManager 實例;
  • HintManager.addDatabaseShardingValue,HintManager.addTableShardingValue 方法設置分片鍵值;
  • 執行 SQL 語句完成路由和執行;
  • 最後調用 HintManager.close 清理 ThreadLocal 中的內容。

按我們設定的數據節點位置,插入一條測試數據,看到確實存在了db0.t_order_1中,完美!

總結

本文中我們講解了ShardingSphere-jdbc所支持的12種分片算法,每種算法都具有獨特的特點。在實際應用中,需要結合具體的業務場景來靈活選擇和應用適合的分片算法。

由於篇幅已經夠長了,剩下的 3 種自定義分片算法會在下一篇中詳細介紹。

全部demo案例 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-algorithms

我是小富~ 下期見

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章