大家好,我是小富~
前言
本文是《ShardingSphere5.x分庫分表原理與實戰》系列的第五篇文章,我們一起梳理下ShardingSphere
框架中的核心部分分片策略和分片算法,其內部針爲我們提供了多種分片策略和分片算法,來應對不同的業務場景,本着拿來即用的原則。
這次將詳細介紹如何在ShardingSphere-jdbc
中實戰 5 種分片策略和 12 種分片算法,自定義分片算法,比較它們的應用場景以及優劣。
全部demo案例 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-algorithms
分片策略
分片策略是分片鍵
和分片算法
的組合策略,真正用於實現數據分片操作的是分片鍵與相應的分片算法。在分片策略中,分片鍵確定了數據的拆分依據,分片算法則決定了如何對分片鍵值運算,將數據路由到哪個物理分片中。
由於分片算法的獨立性,使得分片策略具有更大的靈活性和可擴展性。這意味着可以根據具體需求選擇不同的分片算法,或者開發自定義的分片算法,以適應各種不同的分片場景。在分表和分庫時使用分片策略和分片算法的方式是一致的。
注意:如果在某種分片策略中使用了不受支持的SQL操作符,比如 MYSQL 某些函數等,那麼系統將無視分片策略,進行全庫表路由操作。這個在使用時要慎重!
ShardingSphere
對外提供了standard
、complex
、hint
、inline
、none
5種分片策略。不同的分片策略可以搭配使用不同的分片算法,這樣可以靈活的應對複雜業務場景。
標準分片策略
標準分片策略(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 語句中有>
、>=
、<=
、<
、=
、IN
和 BETWEEN AND
等操作符。
比如:我們希望通過user_id
和order_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 數量使用時有點小坑,比如db0
和db1
都有分片表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-lower
、range-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 枚舉,比如:
MONTHS
、DAYS
等; - datetime-interval-amount:分片間隔數,和
datetime-interval-unit
是緊密配合使用;
接下來實現個按月存儲數據的場景,用t_order_202401
~t_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-lower
、datetime-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
分片列名稱,多個列用逗號分隔。特別注意:使用多分片鍵複合算法,一定要基於複合分片策略進行設置。
我們對現有的分庫分表算法進行了改進,將分片策略修改爲complex
,sharding-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
我是小富~ 下期見