大家好,我是工藤學編程 🦉 | 一個正在努力學習的小博主,期待你的關注 |
---|---|
實戰代碼系列最新文章😉 | C++實現圖書管理系統(Qt C++ GUI界面版) |
SpringBoot實戰系列🐷 | 【SpringBoot實戰系列】Sharding-Jdbc實現分庫分表到分布式ID生成器Snowflake自定義wrokId實戰 |
環境搭建大集合 | 環境搭建大集合(持續更新) |
分庫分表 | 分庫分表下的 ID 沖突問題與雪花算法講解 |
前情摘要:
1、數據庫性能優化
2、分庫分表之優缺點分析
3、分庫分表之數據庫分片分類
4、分庫分表之策略
5、分庫分表技術棧講解-Sharding-JDBC
6、分庫分表下的 ID 沖突問題與雪花算法講解
本文章目錄
- 分庫分表之實戰-sharding-JDBC
- 一、SpringBoot2.5+MybatisPlus+Sharding-Jdbc項目創建
- 1. 在創建好的Maven項目的pom文件中添加以下pom文件依賴:
- 2. 創建對應測試的數據庫表
- 3、創建實體類以及對應數據庫實體類
- 二、 Sharding-Jdbc常規數據源配置和水平分表
- 配置appliaction.properties
- ShardingSphere SQL 執行日志解析
- 1. Logic SQL(邏輯 SQL)
- 2. Actual SQL(實際 SQL)
- 3. 綁定參數(實際參數值)
- 4. 分片路由邏輯分析
- 三、分庫分表下的 ID 沖突問題解決
分庫分表之實戰-sharding-JDBC
一、SpringBoot2.5+MybatisPlus+Sharding-Jdbc項目創建
1. 在創建好的Maven項目的pom文件中添加以下pom文件依賴:
<properties><java.version>11</java.version><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><spring.boot.version>2.5.5</spring.boot.version><mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version><lombok.version>1.18.16</lombok.version><sharding-jdbc.version>4.1.1</sharding-jdbc.version><junit.version>4.12</junit.version><druid.version>1.1.16</druid.version><!--跳過單元測試--><skipTests>true</skipTests></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring.boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>${spring.boot.version}</version><scope>test</scope></dependency><!--mybatis plus和springboot整合--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.boot.starter.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>${sharding-jdbc.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring.boot.version}</version><configuration><fork>true</fork><addResources>true</addResources></configuration></plugin></plugins></build>
2. 創建對應測試的數據庫表
- 新建數據庫ccc_shop_order_0
CREATE DATABASE `ccc_shop_order_0`
CHARACTER SET utf8mb4
COLLATE utf8mb4_bin;
- 新建數據庫ccc_shop_order_1
CREATE DATABASE `ccc_shop_order_1`
CHARACTER SET utf8mb4
COLLATE utf8mb4_bin;
- 在數據庫ccc_shop_order_0中新建表product_order_0、product_order_1
use ccc_shop_order_0;CREATE TABLE `product_order_0` (`id` bigint NOT NULL AUTO_INCREMENT,`out_trade_no` varchar(64) DEFAULT NULL COMMENT '訂
單唯?標識',`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未?
付訂單,PAY已經?付訂單,CANCEL超時取消訂單',`create_time` datetime DEFAULT NULL COMMENT '訂單?
成時間',`pay_amount` decimal(16,2) DEFAULT NULL COMMENT '訂
單實際?付價格',`nickname` varchar(64) DEFAULT NULL COMMENT '昵稱',`user_id` bigint DEFAULT NULL COMMENT '?戶id',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;CREATE TABLE `product_order_1` (`id` bigint NOT NULL AUTO_INCREMENT,`out_trade_no` varchar(64) DEFAULT NULL COMMENT '訂
單唯?標識',`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未?
付訂單,PAY已經?付訂單,CANCEL超時取消訂單',`create_time` datetime DEFAULT NULL COMMENT '訂單?
成時間',`pay_amount` decimal(16,2) DEFAULT NULL COMMENT '訂
單實際?付價格',`nickname` varchar(64) DEFAULT NULL COMMENT '昵稱',`user_id` bigint DEFAULT NULL COMMENT '?戶id',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;
- 在數據庫ccc_shop_order_1中新建表product_order_0、product_order_1
use ccc_shop_order_1;CREATE TABLE `product_order_0` (`id` bigint NOT NULL AUTO_INCREMENT,`out_trade_no` varchar(64) DEFAULT NULL COMMENT '訂
單唯?標識',`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未?
付訂單,PAY已經?付訂單,CANCEL超時取消訂單',`create_time` datetime DEFAULT NULL COMMENT '訂單?
成時間',`pay_amount` decimal(16,2) DEFAULT NULL COMMENT '訂
單實際?付價格',`nickname` varchar(64) DEFAULT NULL COMMENT '昵稱',`user_id` bigint DEFAULT NULL COMMENT '?戶id',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;CREATE TABLE `product_order_1` (`id` bigint NOT NULL AUTO_INCREMENT,`out_trade_no` varchar(64) DEFAULT NULL COMMENT '訂
單唯?標識',`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未?
付訂單,PAY已經?付訂單,CANCEL超時取消訂單',`create_time` datetime DEFAULT NULL COMMENT '訂單?
成時間',`pay_amount` decimal(16,2) DEFAULT NULL COMMENT '訂
單實際?付價格',`nickname` varchar(64) DEFAULT NULL COMMENT '昵稱',`user_id` bigint DEFAULT NULL COMMENT '?戶id',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;
3、創建實體類以及對應數據庫實體類
ProductOrderDO
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("product_order")
public class ProductOrderDO {@TableId(value = "id", type = IdType.AUTO)private Long id;private String outTradeNo;private String state;private Date createTime;private Double payAmount;private String nickname;private Long userId;
}
ProductOrderMapper
public interface ProductOrderMapper extendsBaseMapper<ProductOrderDO> {
}
目前我的工程如下,大家可以對比一下
二、 Sharding-Jdbc常規數據源配置和水平分表
配置appliaction.properties
spring.application.name=ccc-sharding-jdbc
server.port=8080
# 打印執?的數據庫以及語句
spring.shardingsphere.props.sql.show=true
# 數據源 db0,如果有多個用逗號分隔,有多少個,下面具體數據庫就要配置多少個
spring.shardingsphere.datasource.names=ds0,ds1
# 第?個數據庫
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/ccc_shop_order_0?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456# 第二個數據庫
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/ccc_shop_order_1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456# 指定product_order表的數據分布情況,配置數據節點,?表達式標識符使? ${...} 或 $->{...},但前者與 Spring 本身的?件占位符沖突,所以在 Spring 環境中建議使? $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
# 指定product_order表的分?策略,分?策略包括【分?鍵和分?算法】
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{user_id % 2}
這里我們使用的是mysql8.0
- spring.shardingsphere.datasource.ds0.password=123456 這里大家記得修改為自己數據庫的密碼
- spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/ccc_shop_order_0?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true 這里大家記得把ip改為自己的安排(如果數據庫不在本地)
ShardingSphere SQL 執行日志解析
編寫單元測試DbTest
@RunWith(SpringRunner.class) //底層?junitSpringJUnit4ClassRunner
@SpringBootTest(classes = DemoApplication.class)
@Slf4j
public class DbTest {@Autowiredprivate ProductOrderMapper productOrderMapper;@Testpublic void testSaveProductOrder(){for(int i=0;i<10;i++){ProductOrderDO productOrder = new ProductOrderDO();productOrder.setCreateTime(new Date());productOrder.setNickname("ccc_i="+i);productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));productOrder.setPayAmount(100.00);productOrder.setState("PAY");productOrder.setUserId(Long.valueOf(i+""));productOrderMapper.insert(productOrder);}}
}
執行結果如下
我們查看控制臺輸出,展示了從邏輯 SQL 到實際 SQL 的路由過程。我來逐行解釋:
1. Logic SQL(邏輯 SQL)
INSERT INTO product_order (out_trade_no, state, create_time, pay_amount, nickname, user_id)
VALUES (?, ?, ?, ?, ?, ?)
這是應用程序實際執行的 SQL 語句:
- 操作對象:
product_order
表(邏輯表名) - 參數占位符:
?
代表實際參數會在執行時傳入
2. Actual SQL(實際 SQL)
ds0 ::: INSERT INTO product_order_0 (out_trade_no, state, create_time, pay_amount, nickname, user_id)
VALUES (?, ?, ?, ?, ?, ?)
這是 ShardingSphere 根據分片規則路由后實際執行的 SQL:
ds0
:目標數據源(數據庫實例)product_order_0
:物理表名- 可以看到,邏輯表
product_order
被路由到了物理表product_order_0
3. 綁定參數(實際參數值)
::: [d861e3e2-720a-4d16-bb67-12f96d31, PAY, 2025-06-30 16:24:23.075, 100.0, ccc_i=0, 0]
這些是占位符 ?
對應的實際參數值:
out_trade_no
:d861e3e2-720a-4d16-bb67-12f96d31
(交易號)state
:PAY
(支付狀態)create_time
:2025-06-30 16:24:23.075
(創建時間)pay_amount
:100.0
(支付金額)nickname
:ccc_i=0
(用戶昵稱)user_id
:0
(用戶ID,分片鍵)
4. 分片路由邏輯分析
根據我們之前的配置:
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{user_id % 2}
當 user_id=0
時:
0 % 2 = 0
→ 路由到product_order_0
表- 所以這條記錄被寫入
ds0
庫的product_order_0
表
三、分庫分表下的 ID 沖突問題解決
在 分庫分表下的 ID 沖突問題與雪花算法講解中,我們提到了,同時我們前面的結果表明使用的時候,也確實存在ID沖突問題。因此我們需要使用雪花算法解決這個問題。
- 方式?
訂單id使用MybatisPlus的配置,ProductOrder類配置@TableId(value = “id”, type = IdType.ASSIGN_ID)默認實現類為DefaultIdentifierGenerator雪花算法
- 方式?
使?Sharding-Jdbc配置?件,注釋DO類里面的id分配策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=idspring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
這里我使用的是方式2,讓我們再重新運行測試函數,查看結果
覺得有用請點贊收藏!
如果有相關問題,歡迎評論區留言討論~