目录

1 前言

2 分库分表方式

2.1 垂直切分

2.1.1 垂直分表

2.1.2 垂直分库

2.2 水平切分

2.2.1 水平分库

2.2.2 水平分表

3 Sharding-JDBC

3.1 概述

3.2 环境搭建

3.3 水平拆分

3.3.1水平分表

3.3.2 水平分库

3.4 垂直拆分

3.4.1 垂直分库

4 Sharding-JDBC公共表



1 前言

随着公司业务的发展,数据库中的表中的数据量也在不断的增多,那么对于我们开发来说,查询性能也就变慢了。于是我们可能会采取一些措施,比如:做缓存处理,读写分离或者是加索引的结构。但是当这些措施都做完了之后,随着时间的发展,发现这些措施并不能彻底解决我们的问题,于是就不得不进行分库分表。这也是我们为什么要进行分库分表的原因?

2 分库分表方式

分库分表有两种方式:垂直切分和水平切分

既然知道了垂直分库分表的方式,那么肯定就要知道它们各自有什么区别?以及各自有什么作用?什么时候用哪种方式对数据库进行优化。只有这样,我们才能更好的掌握这项技术。

2.1 垂直切分

2.1.1 垂直分表

描述:操作数据库中的某张表,把这张表中一部分字段数据存到一张新表里,再把这张表另一部分字段数据存到另外一张表里。

产生场景:

java分库分表实现 jdbc分库分表_数据

 当我们在网上浏览商品的时候,就拿手机而言,它肯定包含许多属性,像一些:价格,图片、型号、参数、以及商品的描述什么的。但是用户在初步浏览时,不需要过多的关注它的一些商品细节,只需要观察,手机的价格以及外观是否感兴趣,如果感兴趣的话才会点击商品观察商品的一些参数以及商品描述什么的。而且对于这些商品参数,商品描述之类的属性,它们的访问频率较低,而且占用空间比较大,访问时间肯定相对较长。

所以对于一种商品而言,可以将它的属性分为两种:一种访问频率较低或者占用内存空间较大的,另外一种访问频率较高或者占用内存空间较小的。将这商品属性按照上述拆分成两张表如下:

java分库分表实现 jdbc分库分表_database_02

当遇到某些实体中各个数据项的访问频率不一样,而且部分数据项还是占用较大的BLOB或者是TEXT。所以当数据量很大已经影响到我们的sql效率时,可以考虑将热门字段,冷门字段分开放置在不同的表中。

 垂直拆分建议:

1)将不常用的字段单独放一张表

2)将一些大字段,像text,blob这种类型的字段拆分出来放在另外一张表中

3)将经常组合查询的一些字段放在一张表中

2.1.2 垂直分库

描述:将单一数据库按照业务进行划分,每个库放在不同的服务器上面,做到专库专表

垂直拆分中有一个垂直分表了,那么为什么还有一个垂直分库呢?本着存在即合理的原则,垂直分表只是将表进行了拆分,还是在同一个数据库里面。而且本来一张表拆分成了多张表,数据库的压力也会随之增大。

产生场景:

假如购买手机,点击手机进入详情页,然后付款之后会生成一笔订单 ,肯定也会有一张订单表。那么这个时候商品表和订单表都在一个数据库里面。于是可以将不同业务的表放到不同的业务库中,每个库放在不同的服务器上面,这样在进行数据库操作时,做到专库专用,减少数据库压力。

java分库分表实现 jdbc分库分表_java分库分表实现_03

 优点:

  • 解决业务层面的耦合,业务清晰
  • 能对不同业务的数据进行分级管理、维护、监控、扩展等
  • 高并发场景下,垂直分库一定程度的提升IO、数据库连接数、降低单机硬件资源的瓶颈

 垂直分库通过将表按业务分类,然后分布在不同数据库,并且可以将这些数据库部署在不同服务器上,从而达到多个服务器共同分摊压力的效果,但是依然没有解决单表数据量过大的问题。

2.2 水平切分

可能对于我们的业务而言,在经过了垂直切分之后,依然没有改变数据量大的问题,于是这个时候可以考虑下水平切分。

2.2.1 水平分库

描述:创建多个相同结构的数据库

产生场景:就拿刚刚的商品来说,已经经过了水平垂直切分,但是数据量过大的原因依然没有得到妥善的解决。于是可以将我们分好的库拆分成多个相同结构的,如下图所示:

java分库分表实现 jdbc分库分表_java分库分表实现_04

注意:水平分库并不是将原本数据库的所有数据全部迁移到拆分后的数据库,而是将原本数据库的数据经过业务场景拆分筛选(比如:商品编号为奇数拆分到数据库A,商品编号为偶数拆分到数据库B) ,拆分后数据库中数据的数量总和应和原本数据库中的数据量相等。

2.2.2 水平分表

描述:在同一个库中,将表再拆分成多个相同结构的表。

产生场景:当我们的数据经过水平分库之后发现,每个数据库中的数据量依然是很大,比如:我将一个数量总量为5000万数据库经过水平分库,拆分成两个数据量分别为2500w的数据库,但是发现这样sql执行的效率依然是很慢,于是就有同学想为什么水平分库的时候不多分几个呢?分它十个库,那么每一个库中的数据量不就只有500w了,其实这样多个数据库中都有数据对于维护的成本很高的,一般不优先这样做。于是就想到了水平分表,就是将一个数据库的表再拆分成多个相同结构的表。

java分库分表实现 jdbc分库分表_spring_05

注意:水平分表是将原本数据库中的表拆分成多个相同结构的表,拆分后的数据量相加等于原本数据库数据量 。

分库分表概念总结:

垂直分表:将原本表中的数据字段按照访问频率进行拆分,拆分成多个表(每张表的结构是不一样的),这样能提升部分性能

垂直分库:按照业务将不同业务的表放在 不同的数据库中,做到专库专表。每个数据库都放存放与自己业务相关的表。

水平分库:将一个表中的数据按照业务分到不同的数据库(表的结构是不变的)。

水平分表:在同一个数据库中将表拆分成多个相同结构的表(表的结构是不变的)

3 Sharding-JDBC

上面已经说了分库分表的概念,那么如何进行分库分表后如何对数据进行操作呢?那就要用到了中间件Sharding-JDBC了。

官网:ShardingSphere

3.1 概述

        定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。 注意: Sharding-JDBC不是做分库分表的,主要是简化分库分表之后数据相关操作(数据分片和读写分离)。换句说法就是:工程师已经将想要分的库和表都已经建好,Sharding-JDBC就是将数据分片到建好的库和表中。

3.2 环境搭建

1)SpringBoot2.2.1 + MyBatisPlus + Sharding-JDBC + Druid

2)创建分库分表所用的库和表

相关依赖:

<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>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.2</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.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

项目结构路径如下:

java分库分表实现 jdbc分库分表_数据库_06

3.3 水平拆分

3.3.1水平分表

事先创建好数据库和表

1)创建水平数据库goods_db

2)在创建好的数据库中创建表goods_1和goods_2

3)约定规则:如果添加课程id是偶数就把数据添加goods_1,如果是奇数就添加到goods_2。

数据库及表:

java分库分表实现 jdbc分库分表_数据库_07

表结构:

java分库分表实现 jdbc分库分表_数据库_08

application.properties配置文件:

#shardingjdbc分片策略
# 配置真实数据源 给数据源取名称,几个数据源就取几个名称
spring.shardingsphere.datasource.names=m1

#一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

# 配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/goods_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=lj123456

#指定goods表的分布情况,配置表在哪个数据库里面,表名称都是什么 m1.goods_1,m1.goods_2
spring.shardingsphere.sharding.tables.goods.actual-data-nodes=m1.goods_$->{1..2}

# 指定goods表里面的主键生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.goods.key-generator.column= gid
spring.shardingsphere.sharding.tables.goods.key-generator.type= SNOWFLAKE

#指定分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.sharding-column=gid
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.algorithm-expression=goods_$->{gid % 2 + 1}

#打开sql输出日志
spring.shardingsphere.props.sql.show=true

现在一切的准备工作已经准备就绪,我们就用代码来测试下水平分表

插入:根据配置文件中配置的分片策略,插入十条数据,看他们是否能够插入对应的库中。

#指定分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表

测试插入代码:

@Test
    void addGoods() {
        for (int i = 0; i < 10; i++) {
            Goods goods = new Goods();
//        goods.setGid(); 配置文件中已经配置根据雪花算法自动生成
            goods.setGname("华为手机"+i);
            goods.setUserId(100L);
            goods.setGstatus("Normal"+i);
            goodsMapper.insert(goods);
        }
    }

运行之后控制台已经输出sql:因为数据太多只截取最后两条进行粘贴

java分库分表实现 jdbc分库分表_数据_09

 可以发现当数据gid为偶数时插入了表一,gid为奇数时插入了表2,是按照我们配置的分片策略进行插入的。

看一下插入数据库中的十条数据:

goods_1:

java分库分表实现 jdbc分库分表_database_10

goods_2:

java分库分表实现 jdbc分库分表_数据_11

 以上是插入数据,那么查询数据是否也会从对应表中进行查询呢?

测试查询代码:

@Test
    void findGoods() {
        QueryWrapper<Goods> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("gid",709707628699189249L);
        Goods goods = goodsMapper.selectOne(queryWrapper);
        System.out.println(goods);

    }

运行结果:

2022-03-13 10:21:18.980  INFO 15172 --- [           main] ShardingSphere-SQL                       : Actual SQL: m1 ::: SELECT  gid,gname,user_id,gstatus  FROM goods_2 
 
 WHERE (gid = ?) ::: [709707628699189249]
Goods(gid=709707628699189249, gname=华为手机0, userId=100, gstatus=Normal0)

发现其的确是从我们的goods_2表中查询的。以上就是我们的水平分表。

3.3.2 水平分库

水平分库就是将单一的数据库创建多个相同结构的数据库

准备工作:

1. 创建两个数据库edu_db_1,edu_db_2(表结构与3.3表结构一样)

java分库分表实现 jdbc分库分表_数据库_12

2.配置规则:

        1)userid为偶数添加到edu_db_1数据库,userid为奇数添加到edu_db_2数据库

        2)gid为偶数添加到goods_1表,gid为奇数添加到goods_2表

application.properties:

#shardingjdbc分片策略
# 配置真实数据源 给数据源取名称,水平分库,配置两个数据库
spring.shardingsphere.datasource.names=m1,m2

#一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

# 配置m1数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=lj123456

# 配置m2数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=lj123456

#指定数据库中分布情况,数据库里面表分布情况
spring.shardingsphere.sharding.tables.goods.actual-data-nodes=m$->{1..2}.goods_$->{1..2}

# 指定goods表里面的主键生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.goods.key-generator.column= gid
spring.shardingsphere.sharding.tables.goods.key-generator.type= SNOWFLAKE

#指定表分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.sharding-column=gid
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.algorithm-expression=goods_$->{gid % 2 + 1}

#指定数据库分片策略,约定user_id是偶数添加到m1数据库中,是奇数添加到m2数据库中(这样指定就是默认的,所有的数据库都采用这种)
#spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}

#对特定表数据库采用特定分片策略
spring.shardingsphere.sharding.tables.goods.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.goods.database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}



#打开sql输出日志
spring.shardingsphere.props.sql.show=true

测试插入:

@Test
    void addGoods() {
            Goods goods = new Goods();
//        goods.setGid(); 配置文件中已经配置根据雪花算法自动生成
            goods.setGname("华为手机");
            goods.setUserId(101L);
            goods.setGstatus("Normal");
            goodsMapper.insert(goods);

    }

先想一下:上面那条插入记录user_id为奇数,那么肯定会向我们的数据2中插入,然后再根据gid的值看是插入哪张表中。

测试结果:

测试输出sql:

Actual SQL: m2 ::: INSERT INTO goods_2   (gname, user_id, gstatus, gid) VALUES (?, ?, ?, ?) ::: [华为手机, 101, Normal, 709729270099869697]

插入数据(发现其与我们的猜想是一致的):

java分库分表实现 jdbc分库分表_数据库_13

此时其他库和表中是没有数据的。

以上是插入,那么再来测试下查询:

测试代码:

@Test
    void findGoods() {
        QueryWrapper<Goods> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",101L);
        queryWrapper.eq("gid",709729270099869697L);
        Goods goods = goodsMapper.selectOne(queryWrapper);
        System.out.println(goods);
    }

测试结果:

2022-03-13 11:37:18.578  INFO 15468 --- [           main] ShardingSphere-SQL                       : Actual SQL: m2 ::: SELECT  gid,gname,user_id,gstatus  FROM goods_2 
 
 WHERE (user_id = ? AND gid = ?) ::: [101, 709729270099869697]
Goods(gid=709729270099869697, gname=华为手机, userId=101, gstatus=Normal)

查询时它也是只会查询我们的第二个数据库。

3.4 垂直拆分

3.4.1 垂直分库

垂直拆分就是实现专库专表,每个库都有属于自己的业务表,不会牵扯到其他的表

准备工作:

1)创建库和表

java分库分表实现 jdbc分库分表_数据_14

2)项目结构(增加了user实体类和对应mapper):

java分库分表实现 jdbc分库分表_java分库分表实现_15

 application.properties配置文件:

#shardingjdbc分片策略
# 配置真实数据源 给数据源取名称,水平分库,配置两个数据库
spring.shardingsphere.datasource.names=m1,m2,m0

#一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

# 配置m1数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=lj123456

# 配置m2数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=lj123456

# 配置m0数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=lj123456

#指定user_db数据库里面t_user 专库专表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user

# 指定t_user表里面的主键生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.key-generator.column= user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type= SNOWFLAKE

#指定表分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user



#指定数据库中分布情况,数据库里面表分布情况
spring.shardingsphere.sharding.tables.goods.actual-data-nodes=m$->{1..2}.goods_$->{1..2}

# 指定goods表里面的主键生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.goods.key-generator.column= gid
spring.shardingsphere.sharding.tables.goods.key-generator.type= SNOWFLAKE

#指定表分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.sharding-column=gid
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.algorithm-expression=goods_$->{gid % 2 + 1}

#指定数据库分片策略,约定user_id是偶数添加到m1数据库中,是奇数添加到m2数据库中(这样指定就是默认的,所有的数据库都采用这种)
#spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}

#对特定表数据库采用特定分片策略
spring.shardingsphere.sharding.tables.goods.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.goods.database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}



#打开sql输出日志
spring.shardingsphere.props.sql.show=true

插入数据测试代码:

@Test
    void addUser() {
        User user = new User();
        user.setUsername("lucy");
        user.setUstatus("正常");
        userMapper.insert(user);
    }

测试结果:

2022-03-13 15:26:02.621  INFO 17340 --- [           main] ShardingSphere-SQL                       : Actual SQL: m0 ::: INSERT INTO t_user   (username, ustatus, user_id) VALUES (?, ?, ?) ::: [lucy, 正常, 709788487288619009]

java分库分表实现 jdbc分库分表_数据_16

 查询数据测试代码:

@Test
        void findUser() {
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id",709788487288619009L);
            User user = userMapper.selectOne(queryWrapper);
            System.out.println(user);
        }

测试结果:

2022-03-13 15:29:29.693  INFO 15528 --- [           main] ShardingSphere-SQL                       : Actual SQL: m0 ::: SELECT  user_id,username,ustatus  FROM t_user 
 
 WHERE (user_id = ?) ::: [709788487288619009]
User(userId=709788487288619009, username=lucy, ustatus=正常)

可以发现它只操作了我们的mo数据源即只对我们的user_db数据库进行了操作。

4 Sharding-JDBC公共表

描述:存储固定数据的表,表数据很少发生变化,查询时经常进行关联

在3个数据源中都建立个数据结构相同的公共表

java分库分表实现 jdbc分库分表_java分库分表实现_17

java分库分表实现 jdbc分库分表_database_18

java分库分表实现 jdbc分库分表_数据库_19

 配置公共表:

#配置公共表
spring.shardingsphere.sharding.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column= dictid
spring.shardingsphere.sharding.tables.t_udict.key-generator.type= SNOWFLAKE

 在application.properties中位置:

#shardingjdbc分片策略
# 配置真实数据源 给数据源取名称,水平分库,配置两个数据库
spring.shardingsphere.datasource.names=m1,m2,m0

#一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

# 配置m1数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=lj123456

# 配置m2数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=lj123456

# 配置m0数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=lj123456

#配置公共表
spring.shardingsphere.sharding.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column= dictid
spring.shardingsphere.sharding.tables.t_udict.key-generator.type= SNOWFLAKE

#指定user_db数据库里面t_user 专库专表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user

# 指定t_user表里面的主键生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.key-generator.column= user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type= SNOWFLAKE

#指定表分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user



#指定数据库中分布情况,数据库里面表分布情况
spring.shardingsphere.sharding.tables.goods.actual-data-nodes=m$->{1..2}.goods_$->{1..2}

# 指定goods表里面的主键生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.goods.key-generator.column= gid
spring.shardingsphere.sharding.tables.goods.key-generator.type= SNOWFLAKE

#指定表分片策略 约定gid值偶数添到goods_1表,如果gid是奇数添加到goods_2表
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.sharding-column=gid
spring.shardingsphere.sharding.tables.goods.table-strategy.inline.algorithm-expression=goods_$->{gid % 2 + 1}

#指定数据库分片策略,约定user_id是偶数添加到m1数据库中,是奇数添加到m2数据库中(这样指定就是默认的,所有的数据库都采用这种)
#spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}

#对特定表数据库采用特定分片策略
spring.shardingsphere.sharding.tables.goods.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.goods.database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}



#打开sql输出日志
spring.shardingsphere.props.sql.show=true

测试插入代码(正常结果3个数据源中都会被插入数据):

@Test
    void addDict() {
        Udict udict = new Udict();
        udict.setUstatus("正常");
        udict.setUvalue("已启用");
        udictMapper.insert(udict);
    }

测试结果:

控制台输出sql:

2022-03-13 16:27:45.047  INFO 21500 --- [           main] ShardingSphere-SQL                       : Actual SQL: m1 ::: INSERT INTO t_udict   (ustatus, uvalue, dictid) VALUES (?, ?, ?) ::: [正常, 已启用, 709804016372023297]
2022-03-13 16:27:45.047  INFO 21500 --- [           main] ShardingSphere-SQL                       : Actual SQL: m2 ::: INSERT INTO t_udict   (ustatus, uvalue, dictid) VALUES (?, ?, ?) ::: [正常, 已启用, 709804016372023297]
2022-03-13 16:27:45.047  INFO 21500 --- [           main] ShardingSphere-SQL                       : Actual SQL: m0 ::: INSERT INTO t_udict   (ustatus, uvalue, dictid) VALUES (?, ?, ?) ::: [正常, 已启用, 709804016372023297]

三个数据源中已经被插入数据:

java分库分表实现 jdbc分库分表_数据_20

java分库分表实现 jdbc分库分表_数据_21

java分库分表实现 jdbc分库分表_数据库_22

 测试删除代码(正常结果3个数据源中该数据都会被删除):

@Test
        void deleteDict() {
            QueryWrapper<Udict> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("dictid",709804016372023297L);
            udictMapper.delete(queryWrapper);
        }

控制后台输出:

2022-03-13 16:35:39.534  INFO 14112 --- [           main] ShardingSphere-SQL                       : Actual SQL: m1 ::: DELETE FROM t_udict 
 
 WHERE (dictid = ?) ::: [709804016372023297]
2022-03-13 16:35:39.534  INFO 14112 --- [           main] ShardingSphere-SQL                       : Actual SQL: m2 ::: DELETE FROM t_udict 
 
 WHERE (dictid = ?) ::: [709804016372023297]
2022-03-13 16:35:39.534  INFO 14112 --- [           main] ShardingSphere-SQL                       : Actual SQL: m0 ::: DELETE FROM t_udict

3个数据源中的sql已经被删除:

java分库分表实现 jdbc分库分表_数据_23

java分库分表实现 jdbc分库分表_spring_24

java分库分表实现 jdbc分库分表_数据_25