分库分表-ShardingSphere 4.x(1)

❤️作者简介:2022新星计划第三季云原生与云计算赛道Top5🏅、华为云享专家🏅、云原生领域潜力新星🏅

💛博客首页:C站个人主页🌞

💗作者目的:如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门,共同进步!

文章目录

分库分表-ShardingSphere 4.x(1)

项目所在仓库

ShardingSphere概述

Apache ShardingSphere 是一款开源的分布式数据库生态项目,由 JDBC 和 Proxy 两款产品组成。 其核心采用可插拔架构,通过组件扩展功能。 对上以数据库协议及 SQL 方式提供诸多增强功能,包括数据分片、访问路由、数据安全等;对下原生支持 MySQL、PostgreSQL、SQL Server、Oracle 等多种数据存储引擎。 Apache ShardingSphere 项目理念,是提供数据库增强计算服务平台,进而围绕其上构建生态。 充分利用现有数据库的计算与存储能力,通过插件化方式增强其核心能力,为企业解决在数字化转型中面临的诸多使用难点,为加速数字化应用赋能。

Sharding-JDBC实战教程(上)

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

ShardingSphere-JDBC 的优势在于极致性能和对 Java 应用的友好度。

Sharding-JDBC分库分表(⭐)

必备环境(数据库和SpringBoot项目⭐)
数据库(orderdb1和orderdb2),内容都为如下
sql 复制代码
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `order`;
CREATE TABLE `order`  (
  `order_id` bigint(20) NOT NULL,
  `order_info` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` bigint(20) NOT NULL,
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


DROP TABLE IF EXISTS `order_1`;
CREATE TABLE `order_1`  (
  `order_id` bigint(20) NOT NULL,
  `order_info` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` bigint(20) NOT NULL,
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


DROP TABLE IF EXISTS `order_2`;
CREATE TABLE `order_2`  (
  `order_id` bigint(20) NOT NULL,
  `order_info` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` bigint(20) NOT NULL,
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


DROP TABLE IF EXISTS `t_config`;
CREATE TABLE `t_config`  (
  `config_id` bigint(20) NOT NULL,
  `config_info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  PRIMARY KEY (`config_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
Pom.xml
xml 复制代码
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ShardingSphere-Demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
<!--        springboot版本-->
        <spring-boot.version>2.5.9</spring-boot.version>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

<!--        springboot-web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

<!--        mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

<!--        jdbc依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!-- mybatis-plus依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

<!--        要使用下面这个druid依赖,不能使用druid-spring-boot-starter的依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>

<!--        shardingsphere-jdbc 依赖(最核心的依赖)-->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.1</version>
        </dependency>

<!--        单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

<!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

    </dependencies>


    <build>
        <!--        生成的jar包就是mytest.jar-->
        <finalName>ShardingSphere</finalName>
        <plugins>
            <!--            配置maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <!--     版本需要指定为你当前springboot版本-->
                <version>${spring-boot.version}</version>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                    <!--                    指定主类,也就是springboot启动类-->
                    <mainClass>com.boot.ShardingSphereApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
OrderMapper.class(mapper接口)
java 复制代码
package com.boot.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.boot.entity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface OrderMapper extends BaseMapper<Order> {

}
Order.class(实体类)
java 复制代码
package com.boot.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * TODO: 2022/8/2
 * @author youzhengjie
 */

//lombok注解简化开发
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true) //开启链式编程
//由于mybatis-plus是在mybatis的基础上建立的,所以也有下面的通病(就是order本身就是关键字,无法被解析,所以需要加上``符号)。
//如果不加上@TableName指定表名,默认的表名就是该类的名称。
@TableName("`order`")
public class Order implements Serializable {

    @TableField("order_id")
    private Long orderId; //订单id

    @TableField("order_info")
    private String orderInfo; //订单信息

    @TableField("user_id")
    private Long userId; //用户id
}
ShardingSphereApplication(SpringBoot启动类)
java 复制代码
package com.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ShardingSphereApplication {
    public static void main(String[] args) {

        SpringApplication.run(ShardingSphereApplication.class,args);

    }
}
实战1:使用shardingsphere代理JDBC数据源操作数据库(不分库、不分表)
application.properties
properties 复制代码
#配置ShardingSphere数据源,定义一个或多个数据源名称
spring.shardingsphere.datasource.names=ds1

#配置ds1的数据源(可以配置多个)
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/orderdb1?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=18420163207
junit测试类
测试1:插入10条记录
java 复制代码
    @Test
    void addOrder(){

        for (int i = 0; i < 10; i++) {

            Order order = new Order();

            order.setOrderId(Long.parseLong(""+(i+1)))
                 .setOrderInfo("订单信息"+(i+1))
                 .setUserId(Long.parseLong(""+(1001+i)));

            System.out.println(order);
            orderMapper.insert(order);
        }
    }
  • 查看数据库:
测试2:查询所有数据
java 复制代码
    @Test
    void selectOrderList(){

        List<Order> orders = orderMapper.selectList(null);

        orders.forEach(System.out::println);

    }
实战2:使用shardingsphere操作数据库(inline策略分表、但是不分库)⭐

效果是:插入的数据如果order_id是奇数则会分到order_2表,偶数会分到order_1表(数据所在的数据库都为orderdb1,因为我们暂时没有分库)

application.properties
properties 复制代码
#配置ShardingSphere数据源,定义一个或多个数据源名称
spring.shardingsphere.datasource.names=ds1

#配置ds1的数据源(可以配置多个)
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/orderdb1?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=18420163207


# 真实数据节点配置,采用Groovy表达式(这里就代表我们可以操作的节点有ds1.order_1和ds1.order_2两个表)
spring.shardingsphere.sharding.tables.order.actual-data-nodes=ds1.order_$->{1..2}


# 主键id生成策略。
# 指定主键列,shardingsphere会为这个主键列自动生成id
spring.shardingsphere.sharding.tables.order.key-generator.column=order_id
# 生成主键的算法(推荐使用雪花算法)
spring.shardingsphere.sharding.tables.order.key-generator.type=snowflake



# 开始正式的使用inline策略进行分表(缺点是这个策略会对mysql的BETWEEN AND、>、 <、 >=、 <= 操作失效!)-----分表。
# 指定一个分片键(这里指定指定一个)
spring.shardingsphere.sharding.tables.order.table-strategy.inline.sharding-column=order_id
# 分片算法(表达式)
spring.shardingsphere.sharding.tables.order.table-strategy.inline.algorithm-expression=order_$->{order_id % 2 + 1}




#开启ShardingSphere的SQL输出日志
spring.shardingsphere.props.sql.show=true
# 在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射 order_id ---> orderId
mybatis-plus.configuration.map-underscore-to-camel-case=true


# 这个配置一定要加(注意)
spring.main.allow-bean-definition-overriding= true
junit测试类
测试1:插入10条记录
java 复制代码
   //根据inline策略进行分表,但是不分库
    @Test
    void addOrderByinlineShardingTable(){

        for (int i = 0; i < 10; i++) {

            Order order = new Order();

            //orderid不需要自己手动插入了!
            order.setOrderInfo("订单信息"+(i+1))
                    .setUserId(Long.parseLong(""+(1001+i)));

            orderMapper.insert(order);
        }

    }
  • 查看输出日志:
  • 查看数据库:
测试2:查询所有数据
java 复制代码
	//查询全部数据
    @Test
    void selectAllByinlineShardingTable(){

        List<Order> orders = orderMapper.selectList(null);

        orders.forEach(System.out::println);

    }
  • 查看输出日志:
测试3:查询指定order_id的数据
java 复制代码
  //查询指定order_id的数据
    @Test
    void selectOrderByinlineShardingTable_orderid(){

        QueryWrapper<Order> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("order_id",762459329767931905L);
        List<Order> orders = orderMapper.selectList(objectQueryWrapper);

        orders.forEach(System.out::println);

    }
  • 输出日志:
实战3:使用shardingsphere操作数据库(inline策略分表、inline策略分库)⭐
application.properties
properties 复制代码
#配置ShardingSphere数据源,定义一个或多个数据源名称
spring.shardingsphere.datasource.names=ds1,ds2

#配置ds1的数据源(对应orderdb1数据库)
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/orderdb1?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=18420163207

#配置ds2的数据源(对应orderdb2数据库)
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/orderdb2?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=18420163207


# 真实数据节点配置,采用Groovy表达式(这里就代表我们可以操作的节点有ds1.order_1和ds1.order_2和ds2.order_1和ds2.order_2四张表)
spring.shardingsphere.sharding.tables.order.actual-data-nodes=ds$->{1..2}.order_$->{1..2}


# 主键id生成策略。
# 指定主键列,shardingsphere会为这个主键列自动生成id
spring.shardingsphere.sharding.tables.order.key-generator.column=order_id
# 生成主键的算法(推荐使用雪花算法)
spring.shardingsphere.sharding.tables.order.key-generator.type=snowflake



# 开始正式的使用inline策略进行分表(缺点是这个策略会对mysql的BETWEEN AND、>、 <、 >=、 <= 操作失效!)-----分表。
# 指定一个分片键(这里指定指定一个)
spring.shardingsphere.sharding.tables.order.table-strategy.inline.sharding-column=order_id
# 表的分片算法(表达式),下面这种算法会操作表的浪费,只有ds1.order_1和ds2.order_2才被使用,其他都被浪费
spring.shardingsphere.sharding.tables.order.table-strategy.inline.algorithm-expression=order_$->{order_id % 2 + 1}


# 开始正式的使用inline策略进行分库(缺点是这个策略会对mysql的BETWEEN AND、>、 <、 >=、 <= 操作失效!)------分库。
# 指定一个分片键(这里指定指定一个)
spring.shardingsphere.sharding.tables.order.database-strategy.inline.sharding-column=order_id
# 数据库的分片算法(表达式)
spring.shardingsphere.sharding.tables.order.database-strategy.inline.algorithm-expression=ds$->{order_id % 2 + 1}



#开启ShardingSphere的SQL输出日志
spring.shardingsphere.props.sql.show=true
# 在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射 order_id ---> orderId
mybatis-plus.configuration.map-underscore-to-camel-case=true


# 这个配置一定要加(注意)
spring.main.allow-bean-definition-overriding= true
junit测试类
测试1:插入10条记录
java 复制代码
	//根据inline策略进行分库和分表
    @Test
    void addOrderByinlineShardingTableAndDatabase(){

        for (int i = 0; i < 10; i++) {

            Order order = new Order();

            order.setOrderInfo("订单信息"+(i+1))
                    .setUserId(Long.parseLong(""+(1001+i)));

            orderMapper.insert(order);
        }

    }
  • 输出日志:
  • 查看数据库:
测试2:查询所有数据
java 复制代码
	//查询全部数据
    @Test
    void selectAllByinlineShardingTableAndDatabase(){

        List<Order> orders = orderMapper.selectList(null);

        orders.forEach(System.out::println);

    }
  • 输出日志:
测试3:查询指定order_id的数据
java 复制代码
	//查询指定order_id的数据
    @Test
    void selectOrderByinlineShardingTableAndDatabase_orderid(){

        QueryWrapper<Order> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("order_id",762681526788816896L);
        List<Order> orders = orderMapper.selectList(objectQueryWrapper);

        orders.forEach(System.out::println);

    }
  • 输出日志:
inline策略的缺点(不能使用between,否则会报错⭐)
java 复制代码
    //查询order_id在一个范围的数据
    @Test
    void selectOrderByinlineShardingTableAndDatabase_between(){

        QueryWrapper<Order> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.between("order_id",762681526704930816L,862681526830759936L);

        //lt是小于、gt是大于,这些inline策略都是不支持的
//        objectQueryWrapper.lt("order_id",762681526704930816L);

        List<Order> orders = orderMapper.selectList(objectQueryWrapper);

        orders.forEach(System.out::println);

    }
  • 这里我们使用了between,由于between是范围查询,而inline策略是不支持这种查询的,所以会报错。
    • 解决方法:就是使用其他分片策略,比如下面的standard/complex/Hint这些。(这些策略提供了精确分片和范围分片)
实战4:使用shardingsphere操作数据库(standard策略分表、standard策略分库)⭐
自定义分片策略类(standard策略)
MyStandardRangeTableAlgorithm类(自定义分表的standard策略的范围分片类)
java 复制代码
package com.boot.algorithm.standard.range;

import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


/**
 * 该类适用于BETWEEN AND、>、 <、 >=、 <= 操作
 */

//1:MyStandardRangeTableAlgorithm:自定义分表的standard策略的范围分片类
//2:RangeShardingAlgorithm接口的泛型是分片键的类型(我们的分片键是order_id它是Long类型的所以就是Long)
public class MyStandardRangeTableAlgorithm implements RangeShardingAlgorithm<Long> {

    /**
     * 由于是范围分片,所以返回的是一个集合(集合里面的值是表名)
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {

        //例如sql:select * from order where order_id between 200 and 300

        String logicTableName = rangeShardingValue.getLogicTableName(); //逻辑表名:也就是order
        String columnName = rangeShardingValue.getColumnName(); //分片键:也就是order_id
        Long lowerEndpoint = rangeShardingValue.getValueRange().lowerEndpoint(); //between的最小值:在上面的sql就是200
        Long upperEndpoint = rangeShardingValue.getValueRange().upperEndpoint(); //between的最大是:在上面的sql就是300


        List<String> tables = new ArrayList<>(); //需要sharding-jdbc查询的表

        //由于这里是range范围查询,所以要查询全部表,所以要把所有表名添加到集合中

        tables.add(logicTableName+"_1"); //表名:order_1
        tables.add(logicTableName+"_2"); //表名:order_2

        return tables;
    }

}
MyStandardPreciseTableAlgorithm类(自定义分表的standard策略的精准分片类)
java 复制代码
package com.boot.algorithm.standard.precise;

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.math.BigInteger;
import java.util.Collection;

/**
* 该类适用于=、in操作
*/

//1:MyStandardPreciseTableAlgorithm:自定义分表的standard策略的精准分片类
//2:PreciseShardingAlgorithm接口的泛型是分片键的类型(我们的分片键是order_id它是Long类型的所以就是Long)
public class MyStandardPreciseTableAlgorithm implements PreciseShardingAlgorithm<Long> {

   /**
    * 由于是精准分片,所以返回的是一个字符串(字符串就是指定的表名)
    * @param collection 真实表名集合。order_1和order_2
    */
   @Override
   public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {

       //例如sql:select * from order where order_id=201

       String logicTableName = preciseShardingValue.getLogicTableName(); //逻辑表名。也就是order
       String columnName = preciseShardingValue.getColumnName(); //分片键名。也就是order_id
       Long columnvalue = preciseShardingValue.getValue(); //分片键的值。比如上面的sql,那么这个value就是201


       //实现order_$->{order_id % 2 + 1} 分片算法


       BigInteger bigInteger = BigInteger.valueOf(columnvalue);
       BigInteger tableNumber = bigInteger.mod(new BigInteger("2")).add(new BigInteger("1"));

       //最终需要查询的表名
       String tableName=logicTableName+"_"+tableNumber; //如果tableNumber是1,那么tableName就是order_1
       //判断我们计算出来的表名是否存在
       if(collection.contains(tableName)){
           return tableName; //返回指定的表名
       }
       throw new UnsupportedOperationException("MyStandardPreciseTableAlgorithm没有找到指定的表名");
   }

}
MyStandardRangeDataBaseAlgorithm类(自定义分数据库的standard策略的范围分片类)
java 复制代码
package com.boot.algorithm.standard.range;

import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* 该类适用于BETWEEN AND、>、 <、 >=、 <= 操作
*/

// MyStandardRangeDataBaseAlgorithm:自定义分数据库的standard策略的范围分片类
public class MyStandardRangeDataBaseAlgorithm implements RangeShardingAlgorithm<Long> {

   /**
    * 由于是范围分片,所以返回的是一个集合(集合里面的值是数据源名)
    */

   @Override
   public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {

       List<String> dataSources = new ArrayList<>(); //数据源名称集合

       //由于这里是范围查询,所以全部数据源都要查询,故把所有数据源名称添加进去
       dataSources.add("ds1");
       dataSources.add("ds2");

       return dataSources;
   }
}
MyStandardPreciseDataBaseAlgorithm类(自定义分数据库的standard策略的精准分片类)
java 复制代码
package com.boot.algorithm.standard.precise;

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.math.BigInteger;
import java.util.Collection;

/**
* 该类适用于=、in操作
*/


// MyStandardPreciseDataBaseAlgorithm:自定义分数据库的standard策略的精准分片类
public class MyStandardPreciseDataBaseAlgorithm implements PreciseShardingAlgorithm<Long> {

   /**
    *
    * collection存储了所有表名。ds1和ds2
    * @return
    */
   @Override
   public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {

       //实现ds$->{order_id % 2 + 1}算法

       Long value = preciseShardingValue.getValue();//拿到分片键的值。也就是order_id的值

       BigInteger bigInteger = BigInteger.valueOf(value);

       BigInteger dataSourceNumber = bigInteger.mod(new BigInteger("2")).add(new BigInteger("1"));

       //最终查询的数据源名称
       String dataSourceName="ds"+dataSourceNumber; //ds1或者ds2

       if (collection.contains(dataSourceName)) { //判断dataSourceName是否真实存在
           return dataSourceName;
       }
       throw new UnsupportedOperationException("MyStandardPreciseDataBaseAlgorithm没有找到指定数据源名称");
   }
}
application.properties
properties 复制代码
#配置ShardingSphere数据源,定义一个或多个数据源名称
spring.shardingsphere.datasource.names=ds1,ds2

#配置ds1的数据源(对应orderdb1数据库)
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/orderdb1?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=18420163207

#配置ds2的数据源(对应orderdb2数据库)
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/orderdb2?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=18420163207


# 真实数据节点配置,采用Groovy表达式(这里就代表我们可以操作的节点有ds1.order_1和ds1.order_2和ds2.order_1和ds2.order_2四张表)
spring.shardingsphere.sharding.tables.order.actual-data-nodes=ds$->{1..2}.order_$->{1..2}


# 主键id生成策略。
# 指定主键列,shardingsphere会为这个主键列自动生成id
spring.shardingsphere.sharding.tables.order.key-generator.column=order_id
# 生成主键的算法(推荐使用雪花算法)
spring.shardingsphere.sharding.tables.order.key-generator.type=snowflake



# 开始正式的使用standard策略进行分表-----分表。
# 指定一个分片键(这里指定指定一个)
spring.shardingsphere.sharding.tables.order.table-strategy.standard.sharding-column=order_id
# 指定范围分片的自定义类的全类名 (比如BETWEEN AND、>、 <、 >=、 <= 操作就是范围分片)
spring.shardingsphere.sharding.tables.order.table-strategy.standard.range-algorithm-class-name=com.boot.algorithm.standard.range.MyStandardRangeTableAlgorithm
# 指定精准分片的自定义类的全类名 (比如=、in就是精准分片)
spring.shardingsphere.sharding.tables.order.table-strategy.standard.precise-algorithm-class-name=com.boot.algorithm.standard.precise.MyStandardPreciseTableAlgorithm


# 开始正式的使用standard策略进行分库------分库。
# 指定一个分片键(这里指定指定一个)
spring.shardingsphere.sharding.tables.order.database-strategy.standard.sharding-column=order_id
# 指定范围分片的自定义类的全类名 (比如BETWEEN AND、>、 <、 >=、 <= 操作就是范围分片)
spring.shardingsphere.sharding.tables.order.database-strategy.standard.range-algorithm-class-name=com.boot.algorithm.standard.range.MyStandardRangeDataBaseAlgorithm
# 指定精准分片的自定义类的全类名 (比如=、in就是精准分片)
spring.shardingsphere.sharding.tables.order.database-strategy.standard.precise-algorithm-class-name=com.boot.algorithm.standard.precise.MyStandardPreciseDataBaseAlgorithm



#开启ShardingSphere的SQL输出日志
spring.shardingsphere.props.sql.show=true
# 在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射 order_id ---> orderId
mybatis-plus.configuration.map-underscore-to-camel-case=true


# 这个配置一定要加(注意)
spring.main.allow-bean-definition-overriding= true
junit测试类
测试1:插入10条记录
java 复制代码
	//根据standard策略进行分库和分表
    @Test
    void addOrderBystandardShardingTableAndDatabase(){

        for (int i = 0; i < 10; i++) {

            Order order = new Order();

            order.setOrderInfo("订单信息"+(i+1))
                    .setUserId(Long.parseLong(""+(1001+i)));

            orderMapper.insert(order);
        }

    }
测试2:查询指定order_id的数据
java 复制代码
	//查询指定order_id的数据
    @Test
    void selectOrderBystandardShardingTableAndDatabase_orderid(){

        QueryWrapper<Order> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("order_id",763033347873046528L);
        List<Order> orders = orderMapper.selectList(objectQueryWrapper);

        orders.forEach(System.out::println);

    }
测试3:使用between范围查询,测试inline和standard的区别(inline策略会报错,但是standard策略不会)⭐
java 复制代码
	//查询order_id在一个范围的数据(inline策略会报错)
    @Test
    void selectOrderByStandardShardingTableAndDatabase_between(){

        QueryWrapper<Order> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.between("order_id",762681526704930816L,862681526830759936L);

        //lt是小于、gt是大于,这些inline策略都是不支持的
//        objectQueryWrapper.lt("order_id",762681526704930816L);

        List<Order> orders = orderMapper.selectList(objectQueryWrapper);

        orders.forEach(System.out::println);

    }
  • 测试结果:(standard策略不会报错。)
实战5:使用shardingsphere操作数据库(complex策略分表、complex策略分库)⭐
自定义分片策略类(complex策略)
MyComplexTableAlgorithm类(自定义分表的complex策略的分片类)
java 复制代码
package com.boot.algorithm.complex;

import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

import java.math.BigInteger;
import java.util.*;

public class MyComplexTableAlgorithm implements ComplexKeysShardingAlgorithm<Long> {

    //collection存储了所有表名
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Long> complexKeysShardingValue) {

        //select * from order where order_id between 663033347873046528L and 763033347873046528L and user_id=1003

        String logicTableName = complexKeysShardingValue.getLogicTableName(); //逻辑表名

        //getColumnNameAndRangeValuesMap方法:获取范围(between、>、<)的分片键。(相当于standard策略的范围分片类)
        Range<Long> orderIdRange = complexKeysShardingValue.getColumnNameAndRangeValuesMap().get("order_id");

        //如果我们的sql没有写between and或者其他范围相关的操作符,则orderIdRange就会空,调用下面的Endpoint方法就会NullPointerException。
        //所以一定要进行如下判断
        if(orderIdRange!=null&&orderIdRange.isEmpty()){
            Long lowerEndpoint = orderIdRange.lowerEndpoint(); //663033347873046528L

            Long upperEndpoint = orderIdRange.upperEndpoint(); //763033347873046528L
        }

        //getColumnNameAndShardingValuesMap方法:获取精准(=、in)的分片键。(相当于standard策略的精准分片类)
        Collection<Long> userIdCol = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("user_id");

        //如果我们的sql没有写=或者in,则userIdCol就会空,那么下面遍历这个集合就会NullPointerException
        //所以一定要进行如下判断.
        //如果userIdCol不为null,则遍历这个集合,实现order_$->{order_id % 2 + 1}算法
        if (userIdCol!=null&&!userIdCol.isEmpty()) {

            //最终查询的表名集合,因为=和in可能有多个值。
            List<String> tables = new ArrayList<>();

            //这里虽然是集合,但是我们很多情况下可以把它想成只有一个元素。
            for (Long value : userIdCol) {
                BigInteger bigInteger = BigInteger.valueOf(value);
                BigInteger tableNumber = bigInteger.mod(new BigInteger("2")).add(new BigInteger("1"));
                String tableName=logicTableName+"_"+tableNumber;
                if(collection.contains(tableName)){
                    tables.add(tableName); //添加到集合中
                }else {
                    throw new UnsupportedOperationException("MyComplexTableAlgorithm没有该表名");
                }
            }
            System.out.println(tables);
            return tables;
        }else { //如果userIdCol集合为null,那么就相当于范围查询,则查询全部表

           return collection; //collection里面有全部表名
        }

    }

}
MyComplexDataBaseAlgorithm类(自定义分数据库的complex策略的分片类)
java 复制代码
package com.boot.algorithm.complex;

import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class MyComplexDataBaseAlgorithm implements ComplexKeysShardingAlgorithm<Long> {

    //collection存储了全部数据源名称
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Long> complexKeysShardingValue) {

        //getColumnNameAndRangeValuesMap方法:获取范围(between、>、<)的分片键。(相当于standard策略的范围分片类)
        Range<Long> orderIdRange = complexKeysShardingValue.getColumnNameAndRangeValuesMap().get("order_id");

        //如果我们的sql没有写between and或者其他范围相关的操作符,则orderIdRange就会空,调用下面的Endpoint方法就会NullPointerException。
        //所以一定要进行如下判断
        if(orderIdRange!=null&&orderIdRange.isEmpty()){
            Long lowerEndpoint = orderIdRange.lowerEndpoint(); //663033347873046528L

            Long upperEndpoint = orderIdRange.upperEndpoint(); //763033347873046528L
        }

        //getColumnNameAndShardingValuesMap方法:获取精准(=、in)的分片键。(相当于standard策略的精准分片类)
        Collection<Long> userIdCol = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("user_id");


        //如果我们的sql没有写=或者in,则userIdCol就会空,那么下面遍历这个集合就会NullPointerException
        //所以一定要进行如下判断。

        //如果userIdCol不为null,则遍历这个集合,实现ds$->{order_id % 2 + 1}算法。
        if (userIdCol!=null&&!userIdCol.isEmpty()) {

            //最终查询的表名集合,因为=和in可能有多个值。
            List<String> datasouces = new ArrayList<>();

            //这里虽然是集合,但是我们很多情况下可以把它想成只有一个元素。
            for (Long value : userIdCol) {
                BigInteger bigInteger = BigInteger.valueOf(value);
                BigInteger dataSourceNumber = bigInteger.mod(new BigInteger("2")).add(new BigInteger("1"));
                String dataSourceName="ds"+dataSourceNumber;
                if(collection.contains(dataSourceName)){
                    datasouces.add(dataSourceName); //添加到集合中
                }else {
                    throw new UnsupportedOperationException("MyComplexDataBaseAlgorithm没有该数据源名");
                }
            }
            return datasouces;
        }else { //如果userIdCol集合为null,那么就相当于范围查询,则查询全部数据源


            return collection; //collection里面有全部数据源名称
        }

    }
}
application.properties
properties 复制代码
#配置ShardingSphere数据源,定义一个或多个数据源名称
spring.shardingsphere.datasource.names=ds1,ds2

#配置ds1的数据源(对应orderdb1数据库)
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/orderdb1?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=18420163207

#配置ds2的数据源(对应orderdb2数据库)
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/orderdb2?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=18420163207


# 真实数据节点配置,采用Groovy表达式(这里就代表我们可以操作的节点有ds1.order_1和ds1.order_2和ds2.order_1和ds2.order_2四张表)
spring.shardingsphere.sharding.tables.order.actual-data-nodes=ds$->{1..2}.order_$->{1..2}


# 主键id生成策略。
# 指定主键列,shardingsphere会为这个主键列自动生成id
spring.shardingsphere.sharding.tables.order.key-generator.column=order_id
# 生成主键的算法(推荐使用雪花算法)
spring.shardingsphere.sharding.tables.order.key-generator.type=snowflake



# 开始正式的使用complex策略进行分表-----分表。
# 指定一个或多个分片键(这里的分片键指定为order_id和user_id)
spring.shardingsphere.sharding.tables.order.table-strategy.complex.sharding-columns=order_id,user_id
# 指定complex分片的自定义类的全类名
spring.shardingsphere.sharding.tables.order.table-strategy.complex.algorithm-class-name=com.boot.algorithm.complex.MyComplexTableAlgorithm

# 开始正式的使用complex策略进行分库------分库。
# 指定一个或多个分片键(这里的分片键指定为order_id和user_id)
spring.shardingsphere.sharding.tables.order.database-strategy.complex.sharding-columns=order_id,user_id
# 指定complex分片的自定义类的全类名
spring.shardingsphere.sharding.tables.order.database-strategy.complex.algorithm-class-name=com.boot.algorithm.complex.MyComplexDataBaseAlgorithm


#开启ShardingSphere的SQL输出日志
spring.shardingsphere.props.sql.show=true
# 在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射 order_id ---> orderId
mybatis-plus.configuration.map-underscore-to-camel-case=true


# 这个配置一定要加(注意)
spring.main.allow-bean-definition-overriding= true
junit测试类
测试1:查询指定order_id和user_id的数据(并且order_id和user_id同时都是分片键)⭐
java 复制代码
	//查询指定order_id和user_id的数据(并且order_id和user_id同时都是分片键)
    @Test
    void selectOrderBystandardShardingTableAndDatabase_orderidAndUserid(){

        //select * from order where order_id between 663033347873046528L and 763033347873046528L and user_id=1003
        QueryWrapper<Order> objectQueryWrapper = new QueryWrapper<>();

        objectQueryWrapper.between("order_id",663033347873046528L,763033347873046528L);

        objectQueryWrapper.eq("user_id",1003L);

        List<Order> orders = orderMapper.selectList(objectQueryWrapper);

        orders.forEach(System.out::println);

    }
实战6:使用shardingsphere操作数据库(hint策略分表、hint策略分库)⭐
自定义分片策略类(Hint策略)
MyHintTableAlgorithm(自定义分表的Hint策略的分片类)
java 复制代码
package com.boot.algorithm.hint;

import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

//HintShardingAlgorithm的泛型就是addTableShardingValue方法的value的类型(我们通常使用Integer)
public class MyHintTableAlgorithm implements HintShardingAlgorithm<Integer> {


    @Override
    public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Integer> hintShardingValue) {

        List<String> tables = new ArrayList<>();
        String tableName="order_"+hintShardingValue.getValues().toArray()[0];
        if(collection.contains(tableName)){
            tables.add(tableName);
            return tables;
        }
        throw new UnsupportedOperationException("MyHintTableAlgorithm没有找到表名");
    }
}
MyHintDataBaseAlgorithm(自定义分数据库的Hint策略的分片类)
java 复制代码
package com.boot.algorithm.hint;

import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

//HintShardingAlgorithm的泛型就是addDatabaseShardingValue方法的value的类型(我们通常使用Integer)
public class MyHintDataBaseAlgorithm implements HintShardingAlgorithm<Long> {

    @Override
    public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Long> hintShardingValue) {

        List<String> datasources = new ArrayList<>();
        String dataSourceName="ds"+hintShardingValue.getValues().toArray()[0];
        if(collection.contains(dataSourceName)){
            datasources.add(dataSourceName);
            return datasources;
        }
        throw new UnsupportedOperationException("MyHintDataBaseAlgorithm没有找到数据源名");
    }
}
application.properties
properties 复制代码
#配置ShardingSphere数据源,定义一个或多个数据源名称
spring.shardingsphere.datasource.names=ds1,ds2

#配置ds1的数据源(对应orderdb1数据库)
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/orderdb1?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=18420163207

#配置ds2的数据源(对应orderdb2数据库)
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/orderdb2?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=18420163207


# 真实数据节点配置,采用Groovy表达式(这里就代表我们可以操作的节点有ds1.order_1和ds1.order_2和ds2.order_1和ds2.order_2四张表)
spring.shardingsphere.sharding.tables.order.actual-data-nodes=ds$->{1..2}.order_$->{1..2}


# 主键id生成策略。
# 指定主键列,shardingsphere会为这个主键列自动生成id
spring.shardingsphere.sharding.tables.order.key-generator.column=order_id
# 生成主键的算法(推荐使用雪花算法)
spring.shardingsphere.sharding.tables.order.key-generator.type=snowflake



# 开始正式的使用hint策略进行分表-----分表。
# 指定hint分片的自定义类的全类名
spring.shardingsphere.sharding.tables.order.table-strategy.hint.algorithm-class-name=com.boot.algorithm.hint.MyHintTableAlgorithm

# 开始正式的使用hint策略进行分库------分库。
# 指定hint分片的自定义类的全类名
spring.shardingsphere.sharding.tables.order.database-strategy.hint.algorithm-class-name=com.boot.algorithm.hint.MyHintDataBaseAlgorithm


#开启ShardingSphere的SQL输出日志
spring.shardingsphere.props.sql.show=true
# 在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射 order_id ---> orderId
mybatis-plus.configuration.map-underscore-to-camel-case=true


# 这个配置一定要加(注意)
spring.main.allow-bean-definition-overriding= true
junit测试类
测试1:根据hint策略进行强制分库分表插入数据到ds1的order_2表⭐
java 复制代码
	@Test
    void addOrderByHintShardingTableAndDataBase(){

        HintManager hintManager = HintManager.getInstance();

        //实现指定操作ds1的order_2表
        hintManager.addDatabaseShardingValue("order",1);//添加分库的值为1
        hintManager.addTableShardingValue("order",2); //添加分表的值为2

        for (int i = 0; i < 10; i++) {

            Order order = new Order();

            //orderid不需要自己手动插入了!
            order.setOrderInfo("订单信息"+(i+1))
                    .setUserId(Long.parseLong(""+(1001+i)));

            orderMapper.insert(order);
        }

        hintManager.close(); //用完之后要调用close方法。
    }
  • 查询数据库
测试2:利用Hint策略查询ds1的order_2表⭐
java 复制代码
	@Test
    void selectAllByHintShardingTableAndDataBase(){


        HintManager hintManager = HintManager.getInstance();

        //实现指定操作ds1的order_2表
        hintManager.addDatabaseShardingValue("order",1);//添加分库的值为1
        hintManager.addTableShardingValue("order",2); //添加分表的值为2


        List<Order> orders = orderMapper.selectList(null);

        orders.forEach(System.out::println);

        hintManager.close(); //用完之后要调用close方法。

    }
  • 输出日志:
相关推荐
夜光小兔纸20 分钟前
oracle查询出表中某几个字段值不唯一的数据
数据库·sql·oracle
职略1 小时前
负载均衡类型和算法解析
java·运维·分布式·算法·负载均衡
A22741 小时前
LeetCode 196, 73, 105
java·算法·leetcode
容若只如初见2 小时前
项目实战--Spring Boot + Minio文件切片上传下载
java·spring boot·后端
deadknight92 小时前
Oracle密码过期处理方式
数据库·oracle
Ljubim.te2 小时前
数据库第01讲章节测验(选项顺序可能不同)
数据库
吱吱喔喔2 小时前
数据分表和分库原理
数据库·分表·分库
快乐非自愿2 小时前
KES数据库实践指南:探索KES数据库的事务隔离级别
数据库·oracle
阿里巴巴P8资深技术专家2 小时前
Java常用算法&集合扩容机制分析
java·数据结构·算法
一只fish2 小时前
Oracle的RECYCLEBIN回收站:轻松恢复误删对象
数据库·oracle