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); }
运行日志:
两个库中的数据都被删除。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。