大家好,我是工藤学编程 🦉 | 一个正在努力学习的小博主,期待你的关注 |
|---|---|
作业侠系列最新文章😉 | Java实现聊天程序 |
SpringBoot实战系列🐷 | 【SpringBoot实战系列】Sharding-Jdbc实现分库分表到分布式ID生成器Snowflake自定义wrokId实战 |
环境搭建大集合 | 环境搭建大集合(持续更新) |
在本栏中,我们之前已经完成了: 【SpringBoot实战系列】之发送短信验证码 【SpringBoot实战系列】之从Async组件应用实战到ThreadPoolTaskExecutor⾃定义线程池 【SpringBoot实战系列】之图形验证码开发并池化Redis6存储 【SpringBoot实战系列】阿里云OSS接入上传图片实战
本片速览: 1.ShardingSphere 下的Sharding-JDBC简介 2.分库分表和Sharding-Jdbc常⻅概念术语介绍 3.Sharding-Jdbc实现分库分表实战 4.分库分表暴露的问题-ID冲突及解决 5.分布式 ID ⽣成算法Snowflake原理 6.Snowflake自定义wrokId实战
ShardingSphere 下的Sharding-JDBC简介
Mycat和ShardingJdbc区别
分库分表和Sharding-Jdbc常⻅概念术语介绍
Sharding-Jdbc实现分库分表实战 先来建两个表
CREATE TABLE `traffic_0` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`account_no` bigint DEFAULT NULL COMMENT '账号'
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;CREATE TABLE `traffic_1` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`account_no` bigint DEFAULT NULL COMMENT '账号'
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<sharding-jdbc.version>4.1.1</sharding-jdbc.version>
</dependency>mybatis-plus
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version>
</dependency>设置配置:
shardingsphere:
datasource:
ds0:
connectionTimeoutMilliseconds: 30000
driver-class-name: com.mysql.cj.jdbc.Driver
idleTimeoutMilliseconds: 60000
jdbc-url: jdbc:mysql://127.0.0.1:3306/user_account?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
maintenanceIntervalMilliseconds: 30000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 50
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
names: ds0
props:
# 打印执⾏的数据库以及语句
sql:
show: true
sharding:
tables:
traffic:
actual-data-nodes: ds0.traffic_$->{0..1}
# ⽔平分表策略+⾏表达式分⽚
table-strategy:
inline:
algorithm-expression: traffic_$->{ account_no % 2 }
sharding-column: account_no对应实体类 只需要有account_no就行,应为分片策略需要使用,其他字段无所谓
测试:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AccountApplication.class)
@Slf4j
public class trafficTest {
@Resource
private TrafficMapper trafficMapper;
Random random = new Random();
@Test
public void insert(){
for(int i=0;i<10;i++){
TrafficDO trafficDO = new TrafficDO();
trafficDO.setAccountNo(Long.valueOf(random.nextInt(100)));
;
trafficMapper.insert(trafficDO);
}
}
}运行后使用navicat可视化工具查看如下:


根据我们的分区策略,%2来选择插入那个表
但是有个问题
两个表主键id互相重复
上面分库分表暴露的问题-ID冲突
解决方法:分布式id⽣成
分布式id生成需求
分布式 ID ⽣成算法Snowflake原理 什么是雪花算法Snowflake twitter⽤scala语⾔编写的⾼效⽣成唯⼀ID的算法 优点 ⽣成的ID不重复 算法性能⾼ 基于时间戳,基本保证有序递增 雪花算法⽣成的数字,long类,所以就是8个byte,64bit表示的值 -9223372036854775808(-2的63次⽅) ~9223372036854775807(2的63次⽅-1) ⽣成的唯⼀值⽤于数据库主键,不能是负数,所以值为0~9223372036854775807(2的63次⽅-1)

使用Sharding-Jdbc配置⽂件,设置主键生成使用雪花算法,配置变为如下
shardingsphere:
datasource:
ds0:
connectionTimeoutMilliseconds: 30000
driver-class-name: com.mysql.cj.jdbc.Driver
idleTimeoutMilliseconds: 60000
jdbc-url: jdbc:mysql://127.0.0.1:3306/user_account?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
maintenanceIntervalMilliseconds: 30000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 50
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
names: ds0
props:
# 打印执⾏的数据库以及语句
sql:
show: true
sharding:
tables:
traffic:
actual-data-nodes: ds0.traffic_$->{0..1}
# ⽔平分表策略+⾏表达式分⽚
table-strategy:
inline:
algorithm-expression: traffic_$->{ account_no % 2 }
sharding-column: account_no
#id⽣成策略
key-generator:
column: id
props:
worker:
id: 0
#id⽣成策略
type: SNOWFLAKE再次运行我们之前编写的测试代码,可在数据库中看到如下结果:

好了,主键id不一样了,我们分布式下我们还要保证workid不同和account_no不同,于是我们需要编写以下代码
@Configuration
@Slf4j
public class SnowFlakeWordIdConfig {
/**
* 动态指定sharding jdbc 的雪花算法中的属性work.id属 性
* 通过调⽤System.setProperty()的⽅式实现,可⽤容器的
id 或者机器标识位
* workId最⼤值 1L << 100,就是1024,即 0<= workId
< 1024
* {@link
SnowflakeShardingKeyGenerator#getWorkerId()}
*
*/
static {
try {
InetAddress ip4 = Inet4Address.getLocalHost();
String addressIp = ip4.getHostAddress();
String workerId = (Math.abs(addressIp.hashCode())%1024)+"";
System.setProperty("workerId", workerId);
} catch (UnknownHostException e) {
log.error("生成雪花id出错{}",e);
}
}
}并将配置中的
props:
worker:
id: 0
#id⽣成策略
type: SNOWFLAKE
改为
props:
worker:
id: ${workerId}
#id⽣成策略
type: SNOWFLAKE同样使用雪花算法生成acoount_no 代码:
public class IDUtil {
private static SnowflakeShardingKeyGenerator shardingKeyGenerator = new SnowflakeShardingKeyGenerator();
/**
* 雪花算法⽣成器,配置workId,避免重复
* <p>
* 10进制 654334919987691526
* 64位
* 0000100100010100101010100010010010010110000000000000
* 000000000110
* <p>
*
* @return
*/
public static Comparable<?> geneSnowFlakeID() {
return shardingKeyGenerator.generateKey();
}
}将测试代码中的随机生成换位idutil的生成即可
@Test
public void insert(){
for(int i=0;i<10;i++){
TrafficDO trafficDO = new TrafficDO();
//trafficDO.setAccountNo(Long.valueOf(random.nextInt(100)));
trafficDO.setAccountNo(Long.valueOf(IDUtil.geneSnowFlakeID().toString()));
trafficMapper.insert(trafficDO);
}
}
}运行后,如下:

本篇完!