SpringBoot集成Sharding-JDBC实现分库分表方式
作者:DanceDonkey
一、环境搭建
1.创建一个springboot项目,引入以下依赖。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.20</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.0.0-RC1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
2.创建一个数据库user和两张表user_1,user_2
CREATE DATABASE `user`; CREATE TABLE `user_1` ( `id` bigint(30) NOT NULL, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `gender` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `user_2` ( `id` bigint(30) NOT NULL, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `gender` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

3.分别创建user表对应的实体类和mapper
实体类代码
@Data
public class User {
private Long id;
private String username;
private String password;
private String gender;
}
mapper代码
public interface UserMapper extends BaseMapper<User>{
}结构图:

二、实现水平分表
需求:现在有两个user表,一个是user_1,一个是user_2,当id为偶数的时候向user_1表中插入数据,当id为奇数的时候向user_2表中插入数据,实现水平分表。
1.配置application.properties
#指定当前应用的所有数据源标识(由于后期可能会涉及到多个数据源,或读写分离等,这里要为每个数据源
# 起一个标识名,然后为每个数据源具体配置)
spring.shardingsphere.datasource.names=d1
#配置d1这个数据源,由于这里是水平分表,所以只需要一个数据源即可 其中d1代表该数据源的一个标识
spring.shardingsphere.datasource.d1.type=com.alibaba.druid.pool.DruidDataSource
#指定链接驱动
spring.shardingsphere.datasource.d1.driver-class-name=com.mysql.cj.jdbc.Driver
#指定链接url
spring.shardingsphere.datasource.d1.url=jdbc:mysql://localhost:3308/user?useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
#指定链接用户名
spring.shardingsphere.datasource.d1.username=root
#指定链接密码
spring.shardingsphere.datasource.d1.password=root
#由于是水平分表,所以会涉及到多张相同的表,这里指定这些表的分布情况:在哪个数据源上,以及一共有几张表
#指定在d1数据源,且有user_1和user_2两张表
#$->{1..2}该表达式是与前面的user_相拼接的,不能乱写,例如我们现在的两张表名是user_1和user_2,那这里就是user_$->{1..2}
#如果我们分布了3张表 user_5,user_6,user_7,那这里就是user_$->{5..6..7}
spring.shardingsphere.sharding.tables.user.actual-data-nodes=d1.user_$->{1..2}
# 指定 user 表里面主键id 以及id的生成策略 SNOWFLAKE:表示用雪花算法生成该id
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE
# 指定分片策略 约定 id 值偶数添加到 user_1 表,如果 id 是奇数添加到 user_2表
#指定要根据哪个字段进行分表,这里根据id进行分表
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
#执行分表的规则:注意这里是$->{id% 2 + 1},由于偶数%2的结果为0,但是我们并没有user_0这样表
#所以我们要在取模的结果上+1 这样就实现了如果是偶数那么取模的结果就是user_1,如果是奇数就是user_2
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{id% 2 + 1}
#开启sql 输出日志
spring.shardingsphere.props.sql.show=true
# 由于一个实体类对应两张表,所以会产生覆盖操作,加上这个配置解决覆盖问题
spring.main.allow-bean-definition-overriding=true
2.编写测试代码
@Test
void testAddUser() {
for (int i = 0;i < 6;i++){
User user = new User();
user.setGender("0");
user.setPassword(UUID.randomUUID().toString().substring(0,5));
user.setUsername("-->" + i);
userMapper.insert(user);
}
}
3.日志查看:

可以看到当user_id的值为偶数的时候,是向user_1表中插入的。

再看当user_id的值为奇数的时候,该条数据插入了user_2这个表。
三、实现水平分库
需求:现在有两个user库,一个是user_db1,一个是user_db2,每个user库中都有两张相同的表,user_1,user_2,当user对象的gender属性值为1的时候向user_db1库中插入,当user对象的gender属性值为0的时候向user_db2库中插入,这是分库的规则,分表的规则是,当id为偶数的时候向user_1表中插入数据,当id为奇数的时候向user_2表中插入数据。(建表语句和上面是相同的)
结构如下:

2.配置application.properties
#指定当前应用的所有数据源标识(由于后期可能会涉及到多个数据源,或读写分离等,这里要为每个数据源
# 起一个标识名,然后为每个数据源具体配置)
spring.shardingsphere.datasource.names=d1,d2
#配置d1这个数据源,由于这里是水平分表,所以只需要一个数据源即可 其中d1代表该数据源的一个标识
spring.shardingsphere.datasource.d1.type=com.alibaba.druid.pool.DruidDataSource
#指定链接驱动
spring.shardingsphere.datasource.d1.driver-class-name=com.mysql.cj.jdbc.Driver
#指定链接url
spring.shardingsphere.datasource.d1.url=jdbc:mysql://localhost:3308/user_db1?useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
#指定链接用户名
spring.shardingsphere.datasource.d1.username=root
#指定链接密码
spring.shardingsphere.datasource.d1.password=root
#由于是两个数据库,所以要配置两个数据源
#配置d2数据源
spring.shardingsphere.datasource.d2.type=com.alibaba.druid.pool.DruidDataSource
#指定链接驱动
spring.shardingsphere.datasource.d2.driver-class-name=com.mysql.cj.jdbc.Driver
#指定链接url
spring.shardingsphere.datasource.d2.url=jdbc:mysql://localhost:3308/user_db2?useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
#指定链接用户名
spring.shardingsphere.datasource.d2.username=root
#指定链接密码
spring.shardingsphere.datasource.d2.password=root
#指定数据库和表的分布情况
#指定数据库分布情况,数据库里面表分布情况
#d$->{1..2}.user_$->{1..2} 表示在d1,d2数据源上都有user_1和user_2两张表
spring.shardingsphere.sharding.tables.user.actual-data-nodes=d$->{1..2}.user_$->{1..2}
# 指定 user 表里面主键id 以及id的生成策略 SNOWFLAKE:表示用雪花算法生成该id
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE
# 指定分片策略 约定 id 值偶数添加到 user_1 表,如果 id 是奇数添加到 user_2表
#指定要根据哪个字段进行分表,这里根据id进行分表
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
#执行分表的规则:注意这里是$->{id% 2 + 1},由于偶数%2的结果为0,但是我们并没有user_0这样表
#所以我们要在取模的结果上+1 这样就实现了如果是偶数那么取模的结果就是user_1,如果是奇数就是user_2
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{id% 2 + 1}
#============================================================================================
#以上指定了分表策略,下面指定分库策略,也就是gender为0则向user_db1中插入,为1则向user_db2中插入
spring.shardingsphere.sharding.tables.user.database-strategy.inline..sharding-column=gender
#如果gender为0 则表达式{gender=='0'?1:2}的结果就是1,则d$->{gender=='0'?1:2} = d1,就会向d1数据源也就是user_db1库中添加
#如果gender为1 则表达式{gender=='0'?1:2}的结果就是2,则d$->{gender=='0'?1:2} = d2,就会向d1数据源也就是user_db2库中添加
spring.shardingsphere.sharding.tables.user.database-strategy.inline.algorithm-expression=d$->{gender=='0'?1:2}
#开启sql 输出日志
spring.shardingsphere.props.sql.show=true
# 由于一个实体类对应两张表,所以会产生覆盖操作,加上这个配置解决覆盖问题
spring.main.allow-bean-definition-overriding=true
2.编写测试代码
@Test
void testAddUser() {
for (int i = 0;i < 10;i++){
User user = new User();
user.setPassword(UUID.randomUUID().toString().substring(0,5));
user.setUsername("-->" + i);
if (i % 2 == 0) user.setGender("0");
else user.setGender("1");
userMapper.insert(user);
}
}
3.数据库查看

可以看到:gender为0,且id为偶数插入到了user_db1库中的,user_1表。

可以看到:gender为0,且id为奇数插入到了user_db1库中的,user_2表。

可以看到:gender为1,且id为偶数插入到了user_db2库中的,user_1表。

可以看到:gender为1,且id为奇数插入到了user_db2库中的,user_2表。
四、公共表的配置
有些表的数据量不大,需要在每个数据库中都有,这时需要配置公共表。
向user_db1,和user_db2两个数据库都创建一个t_dict表
CREATE table t_dicit( id BIGINT(30) PRIMARY key, `dstatus` VARCHAR(255) not null, `description` VARCHAR(255) not null )
status和desc是mysql的两个关键字,表的字段名尽量避开这些关键字,否则会发生运行时异常。

1.配置application.properties
#指定当前应用的所有数据源标识(由于后期可能会涉及到多个数据源,或读写分离等,这里要为每个数据源
# 起一个标识名,然后为每个数据源具体配置)
spring.shardingsphere.datasource.names=d1,d2
#配置d1这个数据源,由于这里是水平分表,所以只需要一个数据源即可 其中d1代表该数据源的一个标识
spring.shardingsphere.datasource.d1.type=com.alibaba.druid.pool.DruidDataSource
#指定链接驱动
spring.shardingsphere.datasource.d1.driver-class-name=com.mysql.cj.jdbc.Driver
#指定链接url
spring.shardingsphere.datasource.d1.url=jdbc:mysql://localhost:3308/user_db1?useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
#指定链接用户名
spring.shardingsphere.datasource.d1.username=root
#指定链接密码
spring.shardingsphere.datasource.d1.password=root
#由于是两个数据库,所以要配置两个数据源
#配置d2数据源
spring.shardingsphere.datasource.d2.type=com.alibaba.druid.pool.DruidDataSource
#指定链接驱动
spring.shardingsphere.datasource.d2.driver-class-name=com.mysql.cj.jdbc.Driver
#指定链接url
spring.shardingsphere.datasource.d2.url=jdbc:mysql://localhost:3308/user_db2?useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
#指定链接用户名
spring.shardingsphere.datasource.d2.username=root
#指定链接密码
spring.shardingsphere.datasource.d2.password=root
#指定数据库和表的分布情况
#指定数据库分布情况,数据库里面表分布情况
#d$->{1..2}.user_$->{1..2} 表示在d1,d2数据源上都有user_1和user_2两张表
spring.shardingsphere.sharding.tables.user.actual-data-nodes=d$->{1..2}.user_$->{1..2}
# 指定 user 表里面主键id 以及id的生成策略 SNOWFLAKE:表示用雪花算法生成该id
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE
# 指定分片策略 约定 id 值偶数添加到 user_1 表,如果 id 是奇数添加到 user_2表
#指定要根据哪个字段进行分表,这里根据id进行分表
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
#执行分表的规则:注意这里是$->{id% 2 + 1},由于偶数%2的结果为0,但是我们并没有user_0这样表
#所以我们要在取模的结果上+1 这样就实现了如果是偶数那么取模的结果就是user_1,如果是奇数就是user_2
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{id% 2 + 1}
#============================================================================================
#以上指定了分表策略,下面指定分库策略,也就是gender为0则向user_db1中插入,为1则向user_db2中插入
spring.shardingsphere.sharding.tables.user.database-strategy.inline..sharding-column=gender
#如果gender为0 则表达式{gender=='0'?1:2}的结果就是1,则d$->{gender=='0'?1:2} = d1,就会向d1数据源也就是user_db1库中添加
#如果gender为1 则表达式{gender=='0'?1:2}的结果就是2,则d$->{gender=='0'?1:2} = d2,就会向d1数据源也就是user_db2库中添加
spring.shardingsphere.sharding.tables.user.database-strategy.inline.algorithm-expression=d$->{gender=='0'?1:2}
#============================================================================================
#配置公共表 在向t_dicit表插入数据时 这些默认的数据源(d1,d2)的t_dicit表都会插入该数据
spring.shardingsphere.sharding.broadcast-tables=t_dicit
spring.shardingsphere.sharding.tables.t_dicit.key-generator.column=id
spring.shardingsphere.sharding.tables.t_dicit.key-generator.type=SNOWFLAKE
#开启sql 输出日志
spring.shardingsphere.props.sql.show=true
# 由于一个实体类对应两张表,所以会产生覆盖操作,加上这个配置解决覆盖问题
spring.main.allow-bean-definition-overriding=true
2.编写实体类和mapper
@TableName("t_dicit")
@Data
public class Dict {
@TableId
private Long id;
private String dstatus;
private String description;
}
public interface DictMapper extends BaseMapper<Dict> {
}3.编写测试程序
@Test
public void testDict(){
Dict dict = new Dict();
dict.setDescription("启用状态");
dict.setDstatus("A");
dictMapper.insert(dict);
}
4.查看日志发现分别向两个数据源发送了sql

5.查看表


两个库的t_dicit表都有了该数据。
同样如果是删除公共表的数据也是同时将两个数据库中的记录删除。
@Test
public void testDictDel(){
dictMapper.deleteById(1270356973266165762L);
}
运行日志:

两个库中的数据都被删除。


总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
