Springboot整合ShardingSphere分库分表

单库单表

假设现在有一个存储商品信息的数据库db_product,db_product里面有一张Product商品信息表,里面存储了商品ID和商品名称,先

使用Springboot搭建一个项目,Mysql作为数据库,对Product表进行操作。

初始化数据库

创建数据库db_product,和product商品表:

CREATE DATABASE IF NOT EXISTS db_product DEFAULT CHARACTER SET utf8;
USE db_product;
CREATE TABLE product(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建SpringBoot项目

Pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.4.4</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.shardingshpere</groupId>
   <artifactId>demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>demo</name>
   <description>Demo project for Spring Boot</description>
   <properties>
      <java.version>8</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
      </dependency>
     
      <!--mybatis-->
      <dependency>
         <groupId>org.mybatis.spring.boot</groupId>
         <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
      </dependency>
      <!--mysql-->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <!--druid-->
      <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.16</version>
        </dependency>
     <!--单元测试相关-->
        <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                </exclusion>
            </exclusions>
         <scope>test</scope>
      </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>
</project>
数据源配置:
## 数据源配置(单库单表)
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/db_product?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root
Product
/**
 * 商品信息
 */
public class Product {

    /**
     * id
     */
    private long productId;

    /**
     * name
     */
    private String productName;

    // 省略了get set
}

Mapper文件
@Mapper
public interface ProductMapper {

    /**
     * 新增
     * @param product
     */
    void insert(Product product);

    /**
     * 根据id查找
     * @param productId
     * @return
     */
    Product selectById(@Param("productId") long productId);
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shardingsphere.demo.mapper.ProductMapper">

    <resultMap id="product" type="com.shardingsphere.demo.entity.Product">
        <result column="product_id" jdbcType="INTEGER" property="productId" />
        <result column="product_name" jdbcType="VARCHAR" property="productName" />
    </resultMap>

    <select id="selectById" resultMap="product">
        select * from product where product_id = #{productId}
    </select>

    <insert id="insert">
        INSERT INTO product(product_id, product_name) values (#{productId}, #{productName});
    </insert>
</mapper>

整个项目的结构:
在这里插入图片描述

单元测试
/**
 * 单库单表单元测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductSingleTest {

    @Autowired
    ProductMapper productMapper;

    @Test
    public void insertTest() {
        // 新增数据
        Product product = new Product();
        product.setProductName("手机");
        product.setProductId(1);
        productMapper.insert(product);
    }  
}

查询数据库:

mysql> select * from product;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
|          1 | 手机         |
+------------+--------------+
1 row in set (0.00 sec)

分库分表

接下来我们要对项目进行改造,对数据进行分库分表。

Pom文件引入shardingsphere依赖:

      <!--shardingsphere-->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
       </dependency>

水平分表

首先,对Product进行水平分表,先拆分成两个表product_0和product_1。

拆分规则:

根据product_id进行水平拆分,product_id对2进行取模,结果为0的时候进入product_0表,结果为1的时候进入product_1。

  1. 数据库修改

在db_product下创建product_0和product_1数据表。

USE db_product;

CREATE TABLE product_0(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE product_1(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. 配置文件修改,引入shardingsphere
    • 配置shardingsphere的数据源
    • 指定表的数据源和表名称的规则
    • 指定主键和主键生成规则
    • 指定分表的规则
# 配置数据源,当前只有一个ds1
spring.shardingsphere.datasource.names=ds1

# 数据源ds1,数据库db_product
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/db_product?useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

# 指定表的数据源和表名称的规则,这里指定product表的数据源是ds1,表名称是product_0、product_1
spring.shardingsphere.sharding.tables.product.actual-data-nodes=ds1.product_$->{0..1}
# 指定主键,product的主键为product_id
spring.shardingsphere.sharding.tables.product.key-generator.column=product_id
# 指定用来执行分片规则的字段,这里使用product_id来作为分片的字段
spring.shardingsphere.sharding.tables.product.table-strategy.inline.sharding-column=product_id
# 主键生成规则,使用SNOWFLAKE生成主键
spring.shardingsphere.sharding.tables.product.key-generator.type=SNOWFLAKE
# 指定分表规则,这里根据product_id对2进行取模来决定数据落到哪个表,对2取模为0的话落到_0结尾的表,取模结果为1落到_1的表
spring.shardingsphere.sharding.tables.product.table-strategy.inline.algorithm-expression=product_$->{product_id % 2}

spring.main.allow-bean-definition-overriding=true

key-generator.type用来设置主键的生成规则,这里使用了SNOWFLAKE算法进行生成,但是单元测试进行数据添加的时候如果不设置主键,并没有使用SNOWFLAKE生成,待研究。

spring.shardingsphere.sharding.tables.product.key-generator.type=SNOWFLAKE
  1. 编写单元测试
/**
 * 水平拆分单元测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductHroizontalTest {

    @Autowired
    ProductMapper productMapper;

    @Test
    public void insertTest() {
        // 新增数据
        Product product = new Product();
        product.setProductName("手机");
        product.setProductId(1);// 1对2取模为1,应该落到product_1表
        Product product1 = new Product();
        product1.setProductName("电脑");
        product1.setProductId(2);// 2对2取模为0,应该落到product_0表
        productMapper.insert(product);
        productMapper.insert(product1);
    }

    @Test
    public void selectTest(){
        Product product = productMapper.selectById(1);
        System.out.println(product.getProductName());
    }

}

  1. 查询数据库进行验证:
mysql> select * from product_1;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
|          1 | 手机         |
+------------+--------------+
1 row in set (0.00 sec)

mysql> select * from product_0;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
|          2 | 电脑         |
+------------+--------------+
1 row in set (0.00 sec)

可以看到id为1的落到了product_1表,id为2的落到了product_0表。

水平分库

水平分表已经完成,接下来我们尝试水平分库,先改造数据库,创建两个商品信息库,分别为db_product_0和db_product_1,每个库中的表相同,都有两张表product_0和product_1,水平分表的规则不变,依旧是使用product_id对2进行取模,水平分库的规则也采用这种方式,根据取模结果来判断落到db_product_0还是db_product_1。

  1. 数据库创建
# 创建数据库db_product_0
CREATE DATABASE IF NOT EXISTS db_product_0 DEFAULT CHARACTER SET utf8;

USE db_product_0;

CREATE TABLE product_0(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE product_1(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 创建数据库db_product_1
CREATE DATABASE IF NOT EXISTS db_product_1 DEFAULT CHARACTER SET utf8;

USE db_product_1;

CREATE TABLE product_0(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE product_1(
   product_id int,
   product_name VARCHAR(900) NOT NULL,
   PRIMARY KEY (product_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. 配置文件修改
    • 由于我们增加了数据库,所以需要在配置文件中再增加一个数据源ds1
    • actual-data-nodes需要将product表指向两个数据源,所以修改为ds$->{0…1}
    • 增加database-strategy配置,配置分库策略
# 所有的数据源,当前有两个数据源
spring.shardingsphere.datasource.names=ds0,ds1

# 数据源ds1,连接db_product_1
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/db_product_1?useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root
# 数据源ds0,连接db_product_0
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/db_product_0?useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root


# 指定表的数据源和表名称的规则,这里指定product表的数据源是ds0和ds1,product表名称是以_0、_1结尾的:product_0、product_1
spring.shardingsphere.sharding.tables.product.actual-data-nodes=ds$->{0..1}.product_$->{0..1}

# 主键配置
# 指定主键,product的主键为product_id
spring.shardingsphere.sharding.tables.product.key-generator.column=product_id
# 主键生成规则,使用SNOWFLAKE生成主键
spring.shardingsphere.sharding.tables.product.key-generator.type=SNOWFLAKE

# 分表规则配置
# 指定用来执行分片的字段,这里设置product的product_id来作为分片的字段
spring.shardingsphere.sharding.tables.product.table-strategy.inline.sharding-column=product_id
# 指定分片规则,这里根据product_id对2进行取模来决定数据落到哪个表,对2取模为0的话落到_0结尾的表,取模结果为1落到_1的表
spring.shardingsphere.sharding.tables.product.table-strategy.inline.algorithm-expression=product_$->{product_id % 2}

# 分库规则配置
# 指定用来执行分库规则的字段,这里同样设置product的product_id来作为分库的字段
spring.shardingsphere.sharding.tables.product.database-strategy.inline.sharding-column=product_id
# 指定分库规则,这里根据product_id对2进行取模来决定数据落到哪个库,对2取模为0的话落到ds0数据源,取模结果为1落到ds1数据源
spring.shardingsphere.sharding.tables.product.database-strategy.inline.algorithm-expression=ds$->{product_id % 2}

spring.main.allow-bean-definition-overriding=true
  1. 编写单元测试
    @Test
    public void insertTest() {
        // 新增数据
        Product product = new Product();
        product.setProductName("手机");
        product.setProductId(1);// 应该落到db_product_1的product_1表
        Product product1 = new Product();
        product1.setProductName("电脑");
        product1.setProductId(2);// 应该落到db_product_0的product_0表
        productMapper.insert(product);
        productMapper.insert(product1);
    }
  1. 查询数据库进行验证:
mysql> use db_product_1;
Database changed
mysql> select * from product_1;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
|          1 | 手机         |
+------------+--------------+
1 row in set (0.00 sec)

mysql> use db_product_0;
Database changed
mysql> select * from product_0;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
|          2 | 电脑         |
+------------+--------------+
1 row in set (0.00 sec)

可以看到id为1的数据落到了db_product_1下的product_1表,id为2的落到了db_product_0下的product_0表。

垂直分库

我们已经对商品完成了水平分库分表,假设现在有了商品的订单信息,订单信息想要存到订单库中,不与商品使用一个库,接下来看一下如何实现。

  1. 数据库改造,增加订单库db_order和订单表,订单库先不拆分,只对订单表进行水平拆分,所以增加order_0和order_1表,水平分表规则同样使用id对2进行取模。
CREATE DATABASE IF NOT EXISTS db_order DEFAULT CHARACTER SET utf8;
USE db_order;
CREATE TABLE order_0(
   order_id int,
   order_info VARCHAR(900) NOT NULL,
   PRIMARY KEY (order_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE order_1(
   order_id int,
   order_info VARCHAR(900) NOT NULL,
   PRIMARY KEY (order_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. 增加Order相关文件

    /**
     * 订单信息
     */
    public class Order {
    
        /**
         * id
         */
        private long orderId;
    
        /**
         * 信息
         */
        private String orderInfo;
    
        public long getOrderId() {
            return orderId;
        }
    
        public void setOrderId(long orderId) {
            this.orderId = orderId;
        }
    
        public String getOrderInfo() {
            return orderInfo;
        }
    
        public void setOrderInfo(String orderInfo) {
            this.orderInfo = orderInfo;
        }
    }
    

    Mapper

    @Mapper
    public interface OrderMapper {
    
        /**
         * 新增
         * @param order
         */
        void insert(Order order);
    }
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.shardingsphere.demo.mapper.OrderMapper">
    
        <resultMap id="order" type="com.shardingsphere.demo.entity.Order">
            <result column="order_id" jdbcType="INTEGER" property="orderId" />
            <result column="order_info" jdbcType="VARCHAR" property="orderInfo" />
        </resultMap>
    
        <insert id="insert">
            INSERT INTO order(order_id, order_info) values (#{orderId}, #{orderInfo});
        </insert>
    </mapper>
    
  2. 修改配置文件

    • 增加订单库的数据源
    • 增加订单库的水平分表配置
    • 增加订单库的水平分库配置(这里订单先不进行水平分库)
# 所有的数据源
spring.shardingsphere.datasource.names=ds0,ds1,order0

# 数据源ds1,连接db_product_1
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/db_product_1?useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root
# 数据源ds0,连接db_product_0
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/db_product_0?useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root

# 数据源order0,连接db_order
spring.shardingsphere.datasource.order0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.order0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.order0.url=jdbc:mysql://localhost:3306/db_order?useUnicode=true&characterEncoding=utf-8
spring.shardingsphere.datasource.order0.username=root
spring.shardingsphere.datasource.order0.password=root

# product表的配置

# 指定表的数据源和表名称的规则,这里指定product表的数据源是ds0和ds1,product表名称是以_0、_1结尾的:product_0、product_1
spring.shardingsphere.sharding.tables.product.actual-data-nodes=ds$->{0..1}.product_$->{0..1}

# 主键配置
# 指定主键,product的主键为product_id
spring.shardingsphere.sharding.tables.product.key-generator.column=product_id
# 主键生成规则,使用SNOWFLAKE生成主键
spring.shardingsphere.sharding.tables.product.key-generator.type=SNOWFLAKE

# 分表规则配置
# 指定用来执行分片的字段,这里设置product的product_id来作为分片的字段
spring.shardingsphere.sharding.tables.product.table-strategy.inline.sharding-column=product_id
# 指定分片规则,这里根据product_id对2进行取模来决定数据落到哪个表,对2取模为0的话落到_0结尾的表,取模结果为1落到_1的表
spring.shardingsphere.sharding.tables.product.table-strategy.inline.algorithm-expression=product_$->{product_id % 2}

# 分库规则配置
# 指定用来执行分库规则的字段,这里同样设置product的product_id来作为分库的字段
spring.shardingsphere.sharding.tables.product.database-strategy.inline.sharding-column=product_id
# 指定分库规则,这里根据product_id对2进行取模来决定数据落到哪个库,对2取模为0的话落到ds0数据源,取模结果为1落到ds1数据源
spring.shardingsphere.sharding.tables.product.database-strategy.inline.algorithm-expression=ds$->{product_id % 2}


# order表的配置

# 指定表的数据源和表名称的规则,这里指定order表的数据源是order0, 同样使用order_1 order_0格式的表名
spring.shardingsphere.sharding.tables.order.actual-data-nodes=order$->{0}.order_$->{0..1}

# 主键配置
# 指定主键,order主键设置为order_id
spring.shardingsphere.sharding.tables.order.key-generator.column=order_id
# 主键生成规则,使用SNOWFLAKE生成主键
spring.shardingsphere.sharding.tables.order.key-generator.type=SNOWFLAKE

# 水平分表配置
# 指定用来执行分片规则的字段,这里设置order的order_id来作为分片的字段
spring.shardingsphere.sharding.tables.order.table-strategy.inline.sharding-column=order_id
# 指定水平分表规则,这里根据order_id对2进行取模来决定数据落到哪个表,对2取模为0的话落到_0结尾的表,取模结果为1落到_1的表
spring.shardingsphere.sharding.tables.order.table-strategy.inline.algorithm-expression=order_$->{order_id % 2}

# 水平分库的配置
# 指定用来执行分库规则的字段,这里同样设置order的order_id来作为分库的字段
spring.shardingsphere.sharding.tables.order.database-strategy.inline.sharding-column=order_id
# 指定分库规则,order表先不分库,直接指定order0数据源即可
spring.shardingsphere.sharding.tables.order.database-strategy.inline.algorithm-expression=order0

spring.main.allow-bean-definition-overriding=true
  1. 编写单元测试
/**
 * 垂直分库单元测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderVerticalTest {

    @Autowired
    OrderMapper orderMapper;

    @Test
    public void insertTest() {
        // 新增数据
        Order order = new Order();
        order.setOrderId(1);
        order.setOrderInfo("下单了一部手机");
        orderMapper.insert(order);
        // 新增数据
        Order order2 = new Order();
        order2.setOrderId(2);
        order2.setOrderInfo("下单了一台电脑");
        orderMapper.insert(order2);
    }
}
  1. 查询验证
mysql> select * from order_0;
+----------+-----------------------+
| order_id | order_info            |
+----------+-----------------------+
|        2 | 下单了一台电脑        |
+----------+-----------------------+
1 row in set (0.00 sec)

mysql> select * from order_1;
+----------+-----------------------+
| order_id | order_info            |
+----------+-----------------------+
|        1 | 下单了一部手机        |
+----------+-----------------------+
1 row in set (0.00 sec)

可以发现对Order的操作,数据会落到db_order下的表中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值