SpringBoot 集成 Sharding-JDBC 实现读写分离、分库分表

文章目录

    • 一、Sharding-JDBC的应用场景
    • 二、SpringBoot 集成 Sharding-JDBC
      • 2.1、前期准备
      • 2.2、导入`pom.xml`依赖包
      • 2.3、结构代码实现
        • 2.3.1、MybatisPlusConfig(分页插件)
        • 2.3.2、TOrder(订单对象)
        • 2.3.3、TOrderMapper(订单mapper接口)
        • 2.3.4、TOrderServiceImpl(订单接口实现)
        • 2.3.5、ShardingApplication(启动类)
        • 2.3.6、application.yml(这个配置文件中只配置公用信息,具体分库分表信息通过active指定)
        • 2.3.7、SpringBoot测试类(用于测试所有应用场景,内容后面补充)
    • 三、Sharding-JDBC常用场景
      • 3.1、读写分离
        • 3.1.1、准备读写分离配置文件 application-duxiefenli.yml
        • 3.1.2、测试代码
        • 3.1.3、测试读写分离效果
      • 3.2、读写分离强制路由主库
        • 3.2.1、准备读写分离配置文件 application-duxiefenli.yml
        • 3.2.2、测试代码
        • 3.1.3、测试未开启强制路由主库同一个事务中 查询->更新->查询
        • 3.1.4、测试开启强制路由主库同一个事务中 查询->更新->查询
      • 3.3、分表(根据user_id取模分表)
        • 3.3.1、准备分片表
        • 3.3.2、准备分表配置文件 application-fenbiao-qumo.yml
        • 3.3.3、测试代码
        • 3.3.4、测试分表效果
      • 3.4、分表 & 配置读写分离
        • 3.4.1、准备分表 & 配置读写分离配置文件 application-duxiefenli-fenbiao-qumo.yml
      • 3.5、分库分表
        • 3.5.1、准备两个库在每个库中怎么两张表
        • 3.5.2、准备分库分表配置文件 application-fenkufenbiao-qumo.yml
        • 3.5.3、测试代码
        • 3.5.4、测试分库分表效果
      • 3.6、自定义分表策略(通过时间分表)
        • 3.6.1、准备分片表
        • 3.6.2、自定义分表策略代码实现
          • 3.6.2.1、精确分片策略实现
          • 3.6.2.2、范围区间分片策略实现
        • 3.6.3、准备自定义分表配置文件 application-zidingyi-fenbiao-date.yml
        • 3.6.4、测试代码
        • 3.6.4、测试自定义分表策略效果
          • 3.6.4.1、插入数据查看分片表选择
          • 3.6.4.2、根据时间精确查询查看分片表选择
          • 3.6.4.2、根据时间范围查询查看分片表选择
    • 四、总结

一、Sharding-JDBC的应用场景

      Sharding-JDBC是针对分库分表后的操作简化,相当于增强版的JDBC驱动,常被用于实现应用层读写分离以及分库分表,具体概括可以查看官网这里不做过多说明。
      官方文档

二、SpringBoot 集成 Sharding-JDBC

2.1、前期准备

      要想比较好的体验Sharding-JDBC能力最好使用一主多从数据库模式,我这里会使用MySQL一主两从,如果嫌比较麻烦也可以直接使用一主也行,区别不是很大,再执行日志中都能看出区别。
      想要部署MySQL一主两从可以参考:Linux从零部署MySQL8.0 主从复制(一主两从)

  • 创建一个测试库sharding-jdbc-test然后新增一张测试表后续测试都会使用
CREATE TABLE `t_order` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
  • 代码结构(这里会使用到Mybatis-plus方便进行测试)
    在这里插入图片描述

2.2、导入pom.xml依赖包

我这里使用SpringBoot2.x sharding-jdbc4.x 不同大版本可能会有冲突,如果测试时发现有冲突先检测一下代码是否有问题,如果代码没有问题可以调整一下包的版本。

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version></parent><properties><hutool.version>5.2.5</hutool.version><fastjson.version>1.2.74</fastjson.version><lombok.version>1.18.12</lombok.version><common.version>1.0-SNAPSHOT</common.version><commons-lang3.version>3.10</commons-lang3.version><druid.version>1.1.10</druid.version><mybatis-plus.version>3.5.1</mybatis-plus.version><sharding-jdbc.version>4.0.0</sharding-jdbc.version></properties><dependencies><!--工具包--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><optional>true</optional></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${commons-lang3.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--Mysql依赖包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency><!--druid数据源驱动 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>${sharding-jdbc.version}</version></dependency></dependencies>

2.3、结构代码实现

2.3.1、MybatisPlusConfig(分页插件)
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor());interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
2.3.2、TOrder(订单对象)
@Data
public class TOrder implements Serializable {private static final long serialVersionUID = 1L;/** 订单id */@TableId(value = "id",type = IdType.AUTO)private Long id;/** 订单id (通过雪花算法生成)*/private Long orderId;/** 订单编号 */private String orderNo;/** 用户id */private Integer userId;/** 商品信息 */private String goodsInfo;/** 收件地址 */private String toAddress;/** 创建时间 */private Date createTime;
}
2.3.3、TOrderMapper(订单mapper接口)
public interface TOrderMapper extends BaseMapper<TOrder> {@Select("SELECT t1.*,t2.`name` FROM t_order t1 LEFT JOIN t_user t2 ON t1.user_id = t2.id;")List<Map<String,Object>> queryOrderList();
}
  • ITOrderService(订单接口)
public interface ITOrderService extends IService<TOrder> {void updateOrder(Long id, String toAddress);
}
2.3.4、TOrderServiceImpl(订单接口实现)
@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements ITOrderService {@Override@Transactional(rollbackFor = Exception.class)public void updateOrder(Long id, String toAddress){// 强制路由主库// HintManager.getInstance().setMasterRouteOnly();TOrder order = this.getById(id);if(order == null){System.out.println("订单不存在");return;}this.lambdaUpdate().eq(TOrder::getId, id).set(TOrder::getToAddress, toAddress).update();order = this.getById(id);}
}
2.3.5、ShardingApplication(启动类)
@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})
@MapperScan("com.kerwin.service.mapper")
public class ShardingApplication {public static void main(String[] args) {SpringApplication.run(ShardingApplication.class);}
}
2.3.6、application.yml(这个配置文件中只配置公用信息,具体分库分表信息通过active指定)
server:port: 9090spring:profiles:active: duxiefenli
#    active: fenbiao-qumo
#    active: duxiefenli-fenbiao-qumo
#    active: fenkufenbiao-qumo
#    active: zidingyi-fenbiao-datemain:allow-bean-definition-overriding: truedatasource:druid:validation-query: SELECT 1  #验证数据库服务可用性的查询SQL,一般设置为SELECT 1initial-size: 10 #初始连接 默认0max-active: 20 #最大连接数 默认8min-idle: 10 # 最小连接数 默认0max-wait: 5000 # 获取连接最大等待时间,单位毫秒 默认-1一直等待logging:level:com.baomidou: debugcom.kerwin: debugmybatis-plus:configuration:#开启详细日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# Mybatis 一级缓存,默认为 SESSION 开启一级缓存,STATEMENT 关闭一级缓存# SESSION session 级别缓存,同一个 session 相同查询语句不会再次查询数据库localCacheScope: STATEMENT# Mybatis 二级缓存,默认为 truecacheEnabled: false
2.3.7、SpringBoot测试类(用于测试所有应用场景,内容后面补充)
@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingJDBCTest {@Autowiredprivate ITOrderService orderService;@Autowiredprivate TOrderMapper orderMapper;
}

三、Sharding-JDBC常用场景

      Sharding-JDBC有多种使用场景,可以通过配置文件灵活配置读写分离、分库分表等,这里会对一下常用方式做配置说明。

3.1、读写分离

3.1.1、准备读写分离配置文件 application-duxiefenli.yml
# 分表配置
spring:shardingsphere:datasource:# 全部数据源名称 多个用逗号隔开names:master1,slave1,slave2# 主数据源master1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.181:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456# 从数据源slave1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.191:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456slave2:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.192:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456masterslave:# 读写分离配置 用于配置从库负载均衡算法类型,可选值:ROUND_ROBIN(轮询),RANDOM(随机)load-balance-algorithm-type: round_robin# 最终的数据源名称name: dataSource# 主库数据源名称master-data-source-name: master1# 从库数据源名称列表,多个逗号分隔slave-data-source-names: slave1,slave2props:# 开启SQL显示,默认falsesql:show: true
3.1.2、测试代码
    //----------------------------- 读写分离测试 start -----------------------------@Testpublic void duxiefenliTest(){// 1、测试自动主库写操作(在执行写操作时会自动选择主库)for (int i = 0; i < 10; i++) {TOrder tOrder = new TOrder();tOrder.setOrderNo("NO"+RandomUtil.randomNumbers(10));tOrder.setUserId(RandomUtil.randomInt(10));tOrder.setGoodsInfo("商品"+RandomUtil.randomString(5));tOrder.setToAddress("地址"+RandomUtil.randomString(5));tOrder.setCreateTime(new Date());orderService.save(tOrder);}// 2、测试从库自动轮询读操作TOrder tOrder1 = orderService.getById(1);TOrder tOrder2 = orderService.getById(2);}//----------------------------- 读写分离测试 end -----------------------------
3.1.3、测试读写分离效果

      这里直接运行测试类中的读写分离测试方法,运行之后拉到最下面可以看到插入数据时使用的数据源是DataSources: master1,而查询时使用的数据源是DataSources: slave1 DataSources: slave2,多次查看可以看到会对两个从数据源进行轮询。

在这里插入图片描述

3.2、读写分离强制路由主库

3.2.1、准备读写分离配置文件 application-duxiefenli.yml

      配置文件和读写分离配置文件一致

3.2.2、测试代码
    //----------------------------- 读写分离强制路由主库测试 start -----------------------------@Testpublic void duxiefenliMasterRouteTest(){// 测试一个事务中有写也有读操作数据源路由情况orderService.updateOrder(1L,"罗马");}//----------------------------- 读写分离强制路由主库测试 end -----------------------------
3.1.3、测试未开启强制路由主库同一个事务中 查询->更新->查询

在这里插入图片描述
这里可以看到第一次查询路由到了从库,第二次查询路由到了主库,在同一个事务中如果进行了修改操作那么后面都会路由主库。
在这里插入图片描述

3.1.4、测试开启强制路由主库同一个事务中 查询->更新->查询

打开强制路由主库HintManager.getInstance().setMasterRouteOnly();运行后可以看到第一次查询也是使用的主库

在这里插入图片描述

3.3、分表(根据user_id取模分表)

3.3.1、准备分片表

       这里准备两个分片表,t_order_0 t_order_1

CREATE TABLE `t_order_0` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
CREATE TABLE `t_order_1` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
3.3.2、准备分表配置文件 application-fenbiao-qumo.yml
# 取模分表配置
spring:main:allow-bean-definition-overriding: trueshardingsphere:datasource:# 全部数据源名称 多个用逗号隔开names:master1# 主数据源master1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.181:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456props:# 开启SQL显示,默认falsesql:show: true# 分表配置sharding:tables:t_order:# 由数据源名.表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式# ds0代表数据源名称,t_order表名称 ,$->{0..1} inline表达式代表取值0 1actual-data-nodes: master1.t_order_$->{0..1}table-strategy:# 分表策略为inlineinline:# 分表列名sharding-column: user_id# 分表算法 根据t_order表中的user_id取模2进行数据分片存储algorithm-expression: t_order_$->{user_id % 2}
3.3.3、测试代码
    //----------------------------- 分表测试 start -----------------------------@Testpublic void fenbiaoTest(){// 1、测试自动主库写操作(在执行写操作时会自动选择主库)for (int i = 0; i < 10; i++) {TOrder tOrder = new TOrder();tOrder.setOrderNo("NO"+RandomUtil.randomNumbers(10));tOrder.setUserId(RandomUtil.randomInt(10));tOrder.setGoodsInfo("商品"+RandomUtil.randomString(5));tOrder.setToAddress("地址"+RandomUtil.randomString(5));tOrder.setCreateTime(new Date());orderService.save(tOrder);}// 2、测试根据userId分表查询List<TOrder> list = orderService.lambdaQuery().eq(TOrder::getUserId, 7).list();}//----------------------------- 分表测试 end -----------------------------
3.3.4、测试分表效果

       先将application.yml里的active配置成fenbiao-qumo,这里可以观察到新增和查询都能根据user_id自动取模匹配对应表
在这里插入图片描述

3.4、分表 & 配置读写分离

       这里只有配置文件不同,其它逻辑和分表一致,只用将application.yml里的active配置成duxiefenli-fenbiao-qumo

3.4.1、准备分表 & 配置读写分离配置文件 application-duxiefenli-fenbiao-qumo.yml
# 读写分离+取模分表
spring:main:allow-bean-definition-overriding: truedatasource:druid:access-to-underlying-connection-allowed: trueshardingsphere:datasource:# 全部数据源名称 多个用逗号隔开names:master1,slave1,slave2# 主数据源master1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.181:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456# 从数据源slave1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.191:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456slave2:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.192:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456props:# 开启SQL显示,默认falsesql:show: truesharding:# 读写分离配置master-slave-rules:ds0:master-data-source-name: master1slave-data-source-names: slave1, slave2# 负载均衡算法  ROUND_ROBIN:轮询  RANDOM:随机load-balance-algorithm-type: ROUND_ROBIN# 分表配置tables:t_order:# 由数据源名.表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式# ds0代表数据源名称,t_order表名称 ,$->{0..1} inline表达式代表取值0 1actual-data-nodes: ds0.t_order_$->{0..1}table-strategy:# 分表策略为inlineinline:# 分表列名sharding-column: user_id# 分表算法 根据t_order表中的user_id取模2进行数据分片存储algorithm-expression: t_order_$->{user_id % 2}

3.5、分库分表

3.5.1、准备两个库在每个库中怎么两张表

       我这里安装了两MySQL,在两个MySQL中都创建一个叫sharding-jdbc-test的库,然后在库中创建两个分片表,t_order_0 t_order_1,也可以在一个MySQL中创建两个数据库测试效果是一样的。

CREATE TABLE `t_order_0` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
CREATE TABLE `t_order_1` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
3.5.2、准备分库分表配置文件 application-fenkufenbiao-qumo.yml
spring:main:allow-bean-definition-overriding: trueshardingsphere:datasource:# 全部数据源名称 多个用逗号隔开names:master0,master1# 主数据源 数据源名称不能有下划线可以用 -master0:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.181:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456master1:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.182:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456props:# 开启SQL显示,默认falsesql:show: truesharding:# 分库配置default-database-strategy:inline:# 分库依据字段sharding-column: user_id# 分库规则algorithm-expression: master$->{user_id % 2}# 分表配置tables:t_order:# 由数据源名.表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式# master代表数据源名称, $->{0..1} inline表达式代表取值0 1,user_info表名称 ,$->{0..1} inline表达式代表取值0 1actual-data-nodes: master$->{0..1}.t_order_$->{0..1}table-strategy:# 分表策略为inlineinline:# 分表列名sharding-column: order_id# 分表算法 根据t_order表中的order_id取模2进行数据分片存储algorithm-expression: t_order_$->{order_id % 2}# 自定义t_order中order_id生成,使用雪花算法key-generator:column: order_idtype: SNOWFLAKEprops:worker:# 雪花算法的workId  机器为标识 0-1024id: 996
3.5.3、测试代码
    //----------------------------- 分库分表测试 start -----------------------------@Testpublic void fenkuFenbiaoTest(){// 1、测试自动主库写操作(在执行写操作时会自动选择主库)for (int i = 0; i < 10; i++) {TOrder tOrder = new TOrder();tOrder.setOrderNo("NO"+RandomUtil.randomNumbers(10));tOrder.setUserId(RandomUtil.randomInt(10));tOrder.setGoodsInfo("商品"+RandomUtil.randomString(5));tOrder.setToAddress("地址"+RandomUtil.randomString(5));tOrder.setCreateTime(new Date());orderService.save(tOrder);}// 2、测试根据userId分表查询
//        List<TOrder> list = orderService.lambdaQuery().eq(TOrder::getUserId, 7).list();}//----------------------------- 分表测试 end -----------------------------
3.5.4、测试分库分表效果

       先将application.yml里的active配置成fenkufenbiao-qumo,执行后可以看到根据我们配置的规则进行了分库分表。

在这里插入图片描述

3.6、自定义分表策略(通过时间分表)

       Sharding-JDBC提供了表达式配置分表,同时也提供了自定义分表策略方式,不同业务的分表方案不同,常见的有取模、根据时间分表,这里会进行自定义时间分表策略演示,每个月分一张表。

3.6.1、准备分片表

       这里准备两张表t_order_2024_7 t_order_2024_8,用来演示自定义分表策略。

CREATE TABLE `t_order_2024_7` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
CREATE TABLE `t_order_2024_8` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',`order_id` bigint DEFAULT NULL COMMENT '订单id(通过雪花算法生成)',`order_no` varchar(100) NOT NULL COMMENT '订单编号',`user_id` bigint NOT NULL COMMENT '用户id',`goods_info` varchar(100) DEFAULT NULL COMMENT '商品信息',`to_address` varchar(100) DEFAULT NULL COMMENT '收件地址',`create_time` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE
);
3.6.2、自定义分表策略代码实现

       分表策略有两个,精确分片PreciseShardingAlgorithm,范围区间分片RangeShardingAlgorithm,这里会对订单创建时间进行两种分片策略实现。

3.6.2.1、精确分片策略实现
@Slf4j
public class OrderCreateTimePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Date> {@Overridepublic String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {System.out.println("table PreciseShardingAlgorithm ");// 真实节点availableTargetNames.stream().forEach((item) -> {log.info("actual node table:{}", item);});log.info("logic table name:{},rout column:{}", shardingValue.getLogicTableName(), shardingValue.getColumnName());//精确分片log.info("column value:{}", shardingValue.getValue());String tb_name = shardingValue.getLogicTableName() + "_";// 根据当前日期 来 分库分表Date date = shardingValue.getValue();String year = String.format("%tY", date);String mon =String.valueOf(Integer.parseInt(String.format("%tm", date))); // 去掉前缀0// 选择表tb_name = tb_name + year + "_" + mon;System.out.println("tb_name:" + tb_name);for (String each : availableTargetNames) {System.out.println("t_order_:" + each);if (each.equals(tb_name)) {return each;}}throw new IllegalArgumentException();}
}
3.6.2.2、范围区间分片策略实现
@Slf4j
public class OrderCreateTimeRangeShardingAlgorithm implements RangeShardingAlgorithm<Date> {@Overridepublic Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) {ArrayList<String> result = new ArrayList<>();//获取表名称String tb_name = rangeShardingValue.getLogicTableName() + "_";//获取查询时间Range<Date> valueRange = rangeShardingValue.getValueRange();Integer queryYearMonthBegin = null; // 查询开始年月if(valueRange.hasLowerBound()){Date beginDate = valueRange.lowerEndpoint();queryYearMonthBegin = Integer.valueOf(String.format("%tY", beginDate)+String.format("%tm", beginDate));}Integer queryYearMonthEnd = null; // 查询结束年月if(valueRange.hasUpperBound()){Date endDate = valueRange.upperEndpoint();queryYearMonthEnd = Integer.valueOf(String.format("%tY", endDate)+String.format("%tm", endDate));}//筛选需要查询的表for (String each : collection) {// 将表中的年月取出来用于判断String yearMonth = each.replace(tb_name, "");String[] yearMonthSplit = yearMonth.split("_");String year = yearMonthSplit[0];String mon = yearMonthSplit[1];mon = mon.length()==1?"0"+mon:mon;Integer yearMonthInt = Integer.parseInt(year+mon); // 数据表对应年月// 当范围查询时为封闭区间 且表的年月在查询区间内if(valueRange.hasLowerBound() && valueRange.hasUpperBound()){if(yearMonthInt>=queryYearMonthBegin && yearMonthInt<=queryYearMonthEnd){result.add(each);}}// 当范围查询时为封闭区间if(valueRange.hasLowerBound() && !valueRange.hasUpperBound()){if(yearMonthInt>=queryYearMonthBegin){result.add(each);}}// 当范围查询时为封闭区间if(!valueRange.hasLowerBound() && valueRange.hasUpperBound()){if(yearMonthInt<=queryYearMonthEnd){result.add(each);}}}if(result.size()==0){log.error("查询表不存在");throw new IllegalArgumentException();}return result;}
}
3.6.3、准备自定义分表配置文件 application-zidingyi-fenbiao-date.yml
# 自定义时间分表配置
# 自定义时间分表配置
spring:main:allow-bean-definition-overriding: trueshardingsphere:datasource:# 全部数据源名称 多个用逗号隔开names:master0# 主数据源master0:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://172.16.8.181:3306/sharding-jdbc-test?useUnicode=true&characterEncoding=UTF-8username: rootpassword: 123456props:# 开启SQL显示,默认falsesql:show: true#工作线程数量,默认值: CPU核数executor:size: 6# 分表配置sharding:tables:t_order:# 由数据源名.表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式# master代表数据源名称,t_order表名称 ,$->{7..8} inline表达式代表取值7 8actual-data-nodes: master0.t_order_$->{2024..2024}_$->{7..8}table-strategy:# 分表策略为 自定义策略standard:# 分表列名sharding-column: create_time# 自定义精确分表算法类路径precise-algorithm-class-name: com.kerwin.config.OrderCreateTimePreciseShardingAlgorithm# 自定义范围分片算法类名称,用于BETWEEN,可选。该类需实现RangeShardingAlgorithm接口并提供无参数的构造器range-algorithm-class-name: com.kerwin.config.OrderCreateTimeRangeShardingAlgorithm# 自定义t_order中order_id生成,使用雪花算法key-generator:column: order_idtype: SNOWFLAKEprops:worker:# 雪花算法的workId  机器为标识 0-1024id: 996
3.6.4、测试代码
    //----------------------------- 自定义分表策略测试 start -----------------------------@Testpublic void zidinyiFenbiaoTest(){// 1、插入数据for (int i = 0; i < 10; i++) {TOrder tOrder = new TOrder();tOrder.setOrderNo("NO"+RandomUtil.randomNumbers(10));tOrder.setUserId(RandomUtil.randomInt(10));tOrder.setGoodsInfo("商品"+RandomUtil.randomString(5));tOrder.setToAddress("地址"+RandomUtil.randomString(5));tOrder.setCreateTime(new Date());orderService.save(tOrder);}
//        // 2、根据时间精确查询
//        List<TOrder> list = orderService.lambdaQuery()
//                .eq(TOrder::getCreateTime, DateUtil.parseDate("2024-08-07 00:00:00"))
//                .list();
//
//        // 3、根据时间范围查询
//        List<TOrder> list1 = orderService.lambdaQuery()
//                .ge(TOrder::getCreateTime, DateUtil.parseDate("2024-07-01 00:00:00"))
//                .le(TOrder::getCreateTime, DateUtil.parseDate("2024-08-30 23:59:59"))
//                .list();}//----------------------------- 自定义分表策略测试 end -----------------------------
3.6.4、测试自定义分表策略效果
3.6.4.1、插入数据查看分片表选择

       执行插入数据可以观察到选择到了t_order_2024_8表。

在这里插入图片描述

3.6.4.2、根据时间精确查询查看分片表选择

       根据时间精确查询可以观察到选择到了t_order_2024_8表。

在这里插入图片描述

3.6.4.2、根据时间范围查询查看分片表选择

       根据时间范围查询可以观察到选择到了t_order_2024_7 t_order_2024_8表。

在这里插入图片描述

四、总结

       这里只演示了部分情况,还有像多表关联查询、分页查询等都是可以适配的,在分页查询中有个问题,如果在分库或分表的情况下因为数据在不同表中获取结果集是有问题的,因为普通的分页查询没法确定多张表的顺序问题,比如一个表有两个分片,查询第3页每页3条数据,如果没有做分表那么在数据库一共会扫描9条数据取出第7-9条,而在分表的情况下则不能只取出每个分片中的第7-9条数据,必须将1-9条数据都取出来进行应用层归并处理,Sharding-JDBC已经将这个功能实现并且进行了优化,虽然做了优化但是不可避免分页查询还是存在的性能问题,要想解决这个问题也有很多方法这里不做展开。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一样都是虚拟化技术,堆叠和M-LAG差异在哪?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 早上好&#xff0c;我的网工朋友。 随着信息技术的快速发展&#xff0c;网络架构也在不断地演进以满足日益增长的需求。 其中&#xff0c;虚拟化技…

没有mac电脑ios上架截屏截图的最新方法

很多人使用uniapp或其他跨平台框架开发ios的app&#xff0c;上架的时候都会遇到一个问题&#xff0c;上架的时候需要各种尺寸的设备来做ios截屏&#xff0c;比如目前最新的要求是&#xff0c;需要对6.7寸、6.5寸和5.5寸的iphone进行截屏&#xff0c;假如支持ipad则还需要对ipad…

java之拼图小游戏(开源)

public class LoginJFrame extends JFrame {//表示登录界面&#xff0c;以后所有跟登录相关的都写在这里public LoginJFrame() {//设置界面的长和宽this.setSize(603,680);//设置界面的标题this.setTitle("拼图登陆界面");//设置界面置顶this.setAlwaysOnTop(true);/…

兼容性测试详解

目录 前言1. 兼容性测试的定义和重要性1.1 兼容性测试的定义1.2 兼容性测试的重要性 2. 兼容性测试的类型2.1 跨浏览器测试2.1.1 跨浏览器测试的挑战2.1.2 跨浏览器测试的方法 2.2 跨平台测试2.2.1 跨平台测试的挑战2.2.2 跨平台测试的方法 3. 兼容性测试的步骤和策略3.1 测试计…

必了解的 20 个 AI 术语解析(下)

AI 领域的基础概念和相关技术有很多&#xff0c;这篇文章里&#xff0c;作者就深入浅出地介绍了相应的内容&#xff0c;感兴趣的同学们&#xff0c;不妨来看一下。 必了解的 20 个 AI 术语解析&#xff08;下&#xff09;© 由 ZAKER科技 提供 本文专为非技术背景的 AI 爱…

【源码+文档+调试讲解】活力健身馆管理系统

摘 要 活力健身馆管理系统的目的是让使用者可以更方便的将人、设备和场景更立体的连接在一起。能让用户以更科幻的方式使用产品&#xff0c;体验高科技时代带给人们的方便&#xff0c;同时也能让用户体会到与以往常规产品不同的体验风格。 与安卓&#xff0c;iOS相比较起来&am…

html+css 实现hover选择按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目…

【Android】跨程序共享数据——内容提供器初识

跨程序共享数据——探究内容提供器 内容提供器的简介 主要用于在不同的应用程序之间实现数据共享的功能&#xff0c;它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供器是Android实现跨程序共享数据的标准方式。…

opencv-图像透视变换

透射变换是视角变化的结果&#xff0c;是指利用透视中心&#xff0c;像点&#xff0c;目标点共线的条件&#xff0c;按透视旋转定律使承影面(透视面)绕迹线(透视轴旋转某一角度&#xff0c;破坏原有的投影光束&#xff0c;仍能保持承影面上投影几何图形不变的变化) 它的本质将图…

【nginx】解决k8s中部署nginx转发不会自动更新域名解析启动失败的问题

文章目录 1. 问题2.解决办法3.扩展说明3.1 DNS解析阶段划分3.2 问题说明3.2.1 先看/etc/resolv.conf说明3.2.2 针对第一个问题3.2.3 针对第二个问题 【后端】NginxluaOpenResty高性能实践 参考&#xff1a; https://blog.csdn.net/u010837612/article/details/123275026 1. 问…

【二分查找】3143. 正方形中的最多点数

本文涉及的基础知识点 C二分查找 LeetCode3143. 正方形中的最多点数 给你一个二维数组 points 和一个字符串 s &#xff0c;其中 points[i] 表示第 i 个点的坐标&#xff0c;s[i] 表示第 i 个点的 标签 。 如果一个正方形的中心在 (0, 0) &#xff0c;所有边都平行于坐标轴&…

【数据结构入门 】栈

目录 前言 一、栈的概念及结构 二、栈的实现 1. 栈的声明 2.初始化栈 3. 栈的销毁 4.判断是否为空栈 5.入栈&#xff08;只能插入栈顶元素&#xff09; 6. 出栈&#xff08;只能从栈顶删除&#xff09; 7.栈的大小 8.获取栈顶元素 总结 前言 在计算机科学中&#xf…

【MySQL 01】在 Ubuntu 22.04 环境下安装 MySQL

文章目录 &#x1f308; 1. 说明&#x1f308; 2. 卸载不必要的环境&#x1f308; 3. 安装 MySQL&#x1f308; 4. 启动和关闭 MySQL 服务&#x1f308; 5. 临时登录 MySQL&#x1f308; 6. 设置 MySQL 密码&#x1f308; 7. 配置 MySQL &#x1f308; 1. 说明 在安装与卸载中…

Python面试宝典第29题:袋鼠过河

题目 一只袋鼠要从河这边跳到河对岸&#xff0c;河很宽&#xff0c;但是河中间打了很多桩子。每隔一米就有一个桩子&#xff0c;每个桩子上都有一个弹簧&#xff0c;袋鼠跳到弹簧上就可以跳得更远。每个弹簧力量不同&#xff0c;用一个数字代表它的力量&#xff0c;如果弹簧力量…

Maven实战(五)- Nexus 私服安装与使用

Maven实战&#xff08;五&#xff09;- Nexus 私服安装与使用 文章目录 Maven实战&#xff08;五&#xff09;- Nexus 私服安装与使用1.安装Nexus1.1.下载安装包1.2.Nexus启动命令1.3.登陆Nexus 2.仓库与仓库组2.1.内置仓库2.2.仓库分类2.3.创建宿主仓库2.4.创建代理仓库2.5.创…

CSS基础知识day4

目录 1. 浮动 1.1 传统网页布局的三种方式 1.2 标准流&#xff08;普通流/文档流&#xff09; 1.3 为什么需要浮动&#xff1f; 1.4 什么是浮动&#xff1f; 1.5 浮动特性&#xff08;重难点&#xff09; 1.6 浮动元素经常和标准流父级搭配使用 2.常见网页布局 2.1 常…

WEB应用(十四)---文件上传

什么是文件上传漏洞 文件上传是Web应用的常见功能&#xff0c;允许用户上传图片、视频及其他文件类型文件。如果用户上传的是木马文件&#xff0c;则服务器就会收到攻击。 对于这个漏洞的练习有一个专门的靶场&#xff0c;即upload-labs&#xff0c;这个的安装可以在windows中使…

顺序表的实现【数据结构】

文章目录 1.线性表2.顺序表2.1 概念及结构 3.模拟实现3.1 准备工作3.2 顺序表的初始化与销毁3.3 顺序表的尾插3.4 顺序表的尾删3.5顺序表的打印3.6 顺序表的头插3.7 顺序表的头删3.8 顺序表查找3.9 顺序表在pos位置插入x3.10 顺序表删除pos位置的值 4.代码整合 1.线性表 线性表…

【Linux学习】深入理解软硬链接

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 &#x1f388;软硬链接&#x1f427;软链接&#x1f42c;硬链接 &#x1f438;总结软硬链接的原理&#x1f40d;软硬链接的应用场景&…

观成科技:海莲花活跃木马KSRAT加密通信分析

概述 自2023年8月至今&#xff0c;海莲花组织多次利用KSRAT远控木马对我国发起攻击。KSRAT通过HTTP协议与C&C服务器进行通信&#xff0c;每个样本都使用了不同的URL。其心跳包采用XOR算法进行加密&#xff0c;而控制指令包和数据回传包则使用了XOR以及“XORAES-128-CBC”组…