大家好,我是小富~
ShardingSphere實現分庫分表,如何管理分佈在不同資料庫例項中的成千上萬張分片表?
上邊的問題是之前有個小夥伴看了我的分庫分表的文章,私下諮詢我的,看到他的提問我第一感覺就是這老鐵沒用過ShardingSphere,因為這個問題在ShardingSphere中已經有了很好的解決方案,接下來看看怎麼實現。
本文案例程式碼GitHub地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-autocreate-table
系列往期
往期系列文章(我佛系更新,無下限拖更):
本文是《ShardingSphere5.x分庫分表原理與實戰》系列的第四篇文章,在進行分庫分表設計時,確認好了資料節點數量和分片策略以後,接下來要做的就是管理大量的分片表。實際實施過程中可能存在上百個分片資料庫例項,每個例項中都可能有成千上萬個分片表,如果僅依靠人力來完成這些任務顯然是不現實的。所以,想要快速且自動化管理這些分片表,使用工具是十分必要滴。
前言
ShardingSphere
框架成員中的Shardingsphere-jdbc
和Shardingsphere-proxy
都提供了自動化管理分片表的功能auto-tables
,可以統一維護大量的分片表,避免了手動編寫指令碼和維護分片表的繁瑣工作,極大程度減少分庫分表的開發和維護成本,提升效率和可靠性。
這裡我們們先使用Shardingsphere-jdbc
來實際操作一下,Shardingsphere-proxy
方式後續會有單獨的文章詳細講解,就不在這裡展開了。
準備工作
假設我們要對t_order
表進行分庫分表,首先我們要做的就是確定好分片方案,這裡使用兩個資料庫例項db0
、db1
,每個例項中t_order
表分成1000張分片表t_order_1 ~ t_order_1000
,order_id
欄位作為分片鍵,分片演算法使用取模演算法order_id % n
,分散式主鍵生成策略採用snowflake
。
t_order
邏輯表的表結構如下:
CREATE TABLE `t_order` (
`order_id` BIGINT ( 20 ) NOT NULL COMMENT "訂單表分散式主健ID",
`order_number` VARCHAR ( 255 ) NOT NULL COMMENT "訂單號",
`customer_id` BIGINT ( 20 ) NOT NULL COMMENT "使用者ID",
`order_date` date NOT NULL COMMENT "下單時間",
`total_amount` DECIMAL ( 10, 2 ) NOT NULL COMMENT "訂單金額",
PRIMARY KEY ( `order_id` ) USING BTREE
);
有了這些基礎資訊,可以先來進行t_order
表的分片配置了,不考慮其他因素,這裡先Run起來!
分片規則配置
設定好分片規則,接著編寫邏輯表t_order
的分片規則的配置,我分別使用yml配置
和Java編碼
兩種方式做了實現。要注意的是兩種方式不要並存,不然啟動會報錯。
yml配置方式
使用yml配置相對簡單易用比較直觀,適合對分庫分表要求不太複雜的場景,完整配置如下:
spring:
shardingsphere:
datasource:
# 資料來源名稱,多資料來源以逗號分隔 ,放在第一個的資料來源為未配置分片規則表的預設資料來源
names: db0 , db1
# 名稱與上邊 names 保持一致
db0:
....
db1:
....
# 具體規則配置
rules:
sharding:
# 分片演算法定義
sharding-algorithms:
# 自定義分片演算法名稱
t_order_database_algorithms:
# 分片演算法型別
type: INLINE
# 自定義引數
props:
algorithm-expression: db$->{order_id % 2}
t_order_table_algorithms:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 1000}
t_order_mod:
type: MOD
props:
# 指定分片數量
sharding-count: 1000
# 分散式序列演算法配置
key-generators:
t_order_snowflake:
type: SNOWFLAKE
# 分散式序列演算法屬性配置
props:
worker-id: 1
tables:
# 邏輯表名稱
t_order:
# 資料節點:資料庫.分片表
actual-data-nodes: db$->{0..1}.t_order_$->{1..1000}
# 分庫策略
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_algorithms
# 主鍵生成策略
keyGenerateStrategy:
column: order_id
keyGeneratorName: t_order_snowflake
# 屬性配置
props:
# 展示修改以後的sql語句
sql-show: true
Java編碼方式
使用Java編碼方式更加靈活和可擴充套件,可以根據業務定製分片規則,適合對分庫分表有特殊需求或需要動態調整的場景。
/**
* 公眾號:程式設計師小富
*/
@Configuration
public class ShardingConfiguration {
/**
* 配置分片資料來源
* 公眾號:程式設計師小富
*/
@Bean
public DataSource getShardingDataSource() throws SQLException {
Map<String, DataSource> dataSourceMap = new HashMap<>();
dataSourceMap.put("db0", dataSource0());
dataSourceMap.put("db1", dataSource1());
// 分片rules規則配置
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
// 分片演算法
shardingRuleConfig.setShardingAlgorithms(getShardingAlgorithms());
// 配置 t_order 表分片規則
ShardingTableRuleConfiguration orderTableRuleConfig = new ShardingTableRuleConfiguration("t_order", "db${0..1}.t_order_${1..1000}");
orderTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "t_order_table_algorithms"));
orderTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "t_order_database_algorithms"));
shardingRuleConfig.getTables().add(orderTableRuleConfig);
// 是否在控制檯輸出解析改造後真實執行的 SQL
Properties properties = new Properties();
properties.setProperty("sql-show", "true");
// 建立 ShardingSphere 資料來源
return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig), properties);
}
/**
* 配置資料來源1
* 公眾號:程式設計師小富
*/
public DataSource dataSource0() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
/**
* 配置資料來源2
* 公眾號:程式設計師小富
*/
public DataSource dataSource1() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
/**
* 配置分片演算法
* 公眾號:程式設計師小富
*/
private Map<String, AlgorithmConfiguration> getShardingAlgorithms() {
Map<String, AlgorithmConfiguration> shardingAlgorithms = new LinkedHashMap<>();
// 自定義分庫演算法
Properties databaseAlgorithms = new Properties();
databaseAlgorithms.setProperty("algorithm-expression", "db$->{order_id % 2}");
shardingAlgorithms.put("t_order_database_algorithms", new AlgorithmConfiguration("INLINE", databaseAlgorithms));
// 自定義分表演算法
Properties tableAlgorithms = new Properties();
tableAlgorithms.setProperty("algorithm-expression", "db$->{order_id % 1000}");
shardingAlgorithms.put("t_order_table_algorithms", new AlgorithmConfiguration("INLINE", tableAlgorithms));
return shardingAlgorithms;
}
}
上面我們在應用中編寫好了分片規則,現在就差在資料庫例項中建立分片表了,手動建立和管理1000張分片表確實是一個又髒又累的活,反正我是不會幹的!
管理分片表
其實,ShardingSphere
內已經為我們提供了管理分片表的能力。
當一張邏輯表t_order
被配置了分片規則,那麼接下來對邏輯表的各種DDL
操作(例如建立表
、修改表結構
等),命令和資料會根據分片規則,執行和儲存到每個分片資料庫和分片庫中的相應分片表中,以此保持整個分片環境的一致性。
不過,使用Shardingsphere-jdbc
管理分片表的過程中,是需要我們手動編寫對邏輯表的DDL
操作的程式碼。我們來跑幾個單元測試用例來觀察實際的執行效果,直接使用jdbcTemplate
執行建立邏輯表t_order
的SQL。
/**
* @author 公眾號:程式設計師小富
* 自動建立分片表
* @date 2023/12/31 17:25
*/
@SpringBootTest
class AutoCreateTablesTests {
@Resource
private JdbcTemplate jdbcTemplate;
/**
* 執行建立邏輯表的SQL,會根據AutoTables的配置自動在對應的資料來源內建立分片表
* @author 公眾號:程式設計師小富
*/
@Test
public void autoCreateOrderTableTest() {
jdbcTemplate.execute("CREATE TABLE `t_order` (\n" +
" `order_id` bigint(20) NOT NULL,\n" +
" `order_number` varchar(255) NOT NULL,\n" +
" `customer_id` bigint(20) NOT NULL,\n" +
" `order_date` date NOT NULL,\n" +
" `total_amount` decimal(10,2) NOT NULL,\n" +
" PRIMARY KEY (`order_id`) USING BTREE\n" +
");");
}
}
根據之前配置的分片規則,將會在兩個資料庫例項 db0
和 db1
中,分別生成1000
張命名為t_order_1
到t_order_1000
的分片表,看到兩個資料庫均成功建立了1000張分片表。
在次執行更新t_order
表SQL,將欄位order_number
長度從 varchar(255)
擴充套件到 varchar(500)
,執行SQL看下效果。
/**
* @author 公眾號:程式設計師小富
* 自動建立分片表
* @date 2023/12/31 17:25
*/
@SpringBootTest
class AutoCreateTablesTests {
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void autoModifyOrderTableTest() {
jdbcTemplate.execute("ALTER TABLE t_order MODIFY COLUMN order_number varchar(500);");
}
}
透過檢視兩個分片庫,我們成功地將所有分片表的order_number
欄位長度更改為了varchar(500)
,在控制檯日誌中,可以看到它是透過在每個分片庫內依次執行了1000次命令實現的。
Shardingsphere-jdbc
實現分庫分表時,可以採用這種預設的方式來管理分片表。但要注意的是,由於涉及到不同的資料庫例項,如果不使用第三方的分散式事務管理工具(例如Seata
等),執行過程是無法保證事務一致性的。
自定義管理分片表
上邊為邏輯表配置分片規則,應用程式內執行對邏輯表的DDL操作,就可以很輕鬆的管理分片表。
自定義
不過,預設的分片管理還是有侷限性的,我們在設計分片規則時往往會根據不同的業務維度來劃分,例如按天、月、按季度生成分片表並分佈到不同資料來源中等。這樣就需要一些自定義的規則來實現。
ShardingSphere 5.X
版本後推出了一種新的管理分片配置方式:AutoTable
。設定了AutoTable
的邏輯表,將交由ShardingSphere
自動管理分片,使用者只需要指定分片數量和使用的資料庫例項,無需再關心表的具體分佈,配置格式如下:
spring:
shardingsphere:
# 資料來源配置
datasource:
......
# 具體規則配置
rules:
sharding:
# 邏輯表分片規則
tables:
# 邏輯表名稱
t_order:
.....
# 自動分片表規則配置
auto-tables:
t_order: # 邏輯表名稱
actual-data-sources: db$->{0..1}
sharding-strategy: # 切分策略
standard: # 用於單分片鍵的標準分片場景
sharding-column: order_id # 分片列名稱
sharding-algorithm-name: t_order_mod # 自動分片演算法名稱
ShardingSphere-Jdbc
中配置使用auto-tables
主要兩個引數,actual-data-sources
指定資料來源分佈,由於是管理分片表所以只需資料來源資訊即可;sharding-strategy
指具體採用何種演算法來進行分片。
對邏輯表的DDL操作,系統會首先檢查是否配置了AutoTable,如果已配置,則優先採用配置的規則;若未配置,則將使用預設的邏輯表分片規則。
AutoTable
支援ShardingSphere
內建的全部自動分片演算法,所謂自動分片演算法就是根據actualDataSources
設定的資料來源資訊,使用對應內建演算法自行解析處理。
- MOD:取模分片演算法
- HASH_MOD:雜湊取模分片演算法
- VOLUME_RANGE:基於分片容量的範圍分片演算法
- BOUNDARY_RANGE:基於分片邊界的範圍分片演算法
- AUTO_INTERVAL:自動時間段分片演算法
AutoTable使用
舉個例子,我們使用內建MOD
取模演算法作為AutoTable
的分片演算法,同樣是db0
、db1
兩個例項中各建立1000張分片表。那麼當對邏輯表的DDL操作時,ShardingSphere
會依據分片表編號t_order_0~t_order_1999 % 資料庫例項數
取模來確認DDL命令路由到哪個例項中執行。
spring:
shardingsphere:
# 資料來源配置
datasource:
.....
# 具體規則配置
rules:
sharding:
# 自動分片表規則配置
auto-tables:
t_order:
actual-data-sources: db$->{0..1}
sharding-strategy:
standard:
sharding-column: order_date
sharding-algorithm-name: t_order_mod
# 分片演算法定義
sharding-algorithms:
t_order_mod:
type: MOD
props:
# 指定分片數量
sharding-count: 2000
還是執行剛才建立表的單元測試,會發現db0
、db1
兩個例項中已經各自建立了1000張分片表,但你會發現1000張表已經不再是按照順序建立的了。
上邊使用的是內建自動分片演算法,它對於我們來說是黑盒,提供它方便我們拿來即用。不過,如果想要做到更細粒度的管理分片表,最好的辦法就是自定義分片演算法,後續章節會介紹所有內建分片演算法和自定義分片演算法的使用。
總結
在使用ShardingSphere
實現分庫分表的時候,要摒棄先建表、再配規則的傳統思維,要先確定規則在建表,管理表是一件很簡單的事,我們只要告訴ShardingSphere
分片數量和分佈規則,剩下的就讓框架來處理就好了。
本文案例程式碼GitHub地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/shardingsphere101/shardingsphere-autocreate-table
我是小富~ 下期見