Sharding-JDBC分库分表-自定义分片算法-4

news2025/1/18 18:53:34

默认分片算法

Sharding JDBC通过org.apache.shardingsphere.sharding.spi.ShardingAlgorithm接口定义了数据分片算法,5.2.1版本默认提供了如下的分片算法

配置标识自动分片算法详细说明类名
MODY基于取模的分片算法ModShardingAlgorithm
HASH_MODY基于哈希取模的分片算法HashModShardingAlgorithm
BOUNDARY_RANGEY基于分片边界的范围分片算法BoundaryBasedRangeShardingAlgorithm
VOLUME_RANGEY基于分片容量的范围分片算法VolumeBasedRangeShardingAlgorithm
AUTO_INTERVALY基于可变时间范围的分片算法AutoIntervalShardingAlgorithm
INTERVALN基于固定时间范围的分片算法IntervalShardingAlgorithm
CLASS_BASEDN基于自定义类的分片算法ClassBasedShardingAlgorithm
INLINEN基于行表达式的分片算法InlineShardingAlgorithm
COMPLEX_INLINEN基于行表达式的复合分片算法ComplexInlineShardingAlgorithm
HINT_INLINEN基于行表达式的 Hint 分片算法HintInlineShardingAlgorithm

默认算法的继承关系如下

在这里插入图片描述

分片算法参考官方用户文档:默认分片算法介绍

自定义分片算法

自定义分片算法时通过配置分片策略类型和算法类名,实现自定义扩展。 CLASS_BASED 允许向算法类内传入额外的自定义属性,传入的属性可以通过属性名为 propsjava.util.Properties 类实例取出。

自定义分片算法有三种类型

  • 标准分片算法
  • 复杂分片算法
  • hint分片算法

对应需要实现的接口分别为:

算法分类需要实现接口说明
标准StandardShardingAlgorithm支持单个分片键,需要实现精确和范围分片接口
复杂ComplexShardingAlgorithm支持多个分片键,但是分片键数据类型需要一样
hintHintShardingAlgorithm没有分片键,分片值通过hint注入而不是SQL

分片算法开发

以标准算法为例,对下面的order_t进行分表

CREATE TABLE `order_t` (
  `order_id` bigint(20) NOT NULL COMMENT 'order_id主键',
  `order_no` varchar(32) DEFAULT NULL COMMENT '订单编号',
  `user_id` bigint(10) NOT NULL COMMENT '用户ID',
  `order_date` date NOT NULL COMMENT '下单时间',
  `order_amount` decimal(16,2) NOT NULL COMMENT '订单金额',
  `delivery_amount` decimal(16,2) DEFAULT '0.00' COMMENT '运费',
  `total_amount` decimal(16,2) NOT NULL COMMENT '汇总金额',
  `receiver_id` bigint(10) NOT NULL COMMENT '收货地址ID',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态,1:已提交,2:已付款,3:待发货,4:已发货,5:已收货,6:已完成',
  `deleted` tinyint(4) DEFAULT '0' COMMENT '删除标志,0:未删除,1:已删除',
  `create_by` bigint(10) DEFAULT NULL COMMENT '创建人',
  `creation_date` datetime DEFAULT NULL COMMENT '创建时间',
  `last_update_by` bigint(10) DEFAULT NULL COMMENT '修改人',
  `last_update_date` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`order_id`),
  KEY `idx_useId` (`user_id`),
  KEY `idx_orderNo` (`order_no`),
  KEY `idx_orderDate` (`order_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表';

分表规则为

  • 按照下单时间order_date分表
  • 一个季度分一个表
  • 只分2023年

目标表有:order_t1,order_t2,order_t3,order_t4

配置如下:

spring:
  shardingsphere:
    rules:
      sharding:
        tables: # 需要分库表的规则配置
          order_t:
            actual-data-nodes: ds0.order_t$->{1..4}  # 待选数据节点:ds0.order_t1、ds0.order_t2、ds0.order_t3
            key-generate-strategy:  # 分布式ID列,一般是主键
              column: order_id
              key-generator-name: beautySnowflake # 使用自定义分布式ID算法是
            table-strategy: # 分库策略配置
              standard: # 标准算法
                sharding-column: order_date # 分片列
                sharding-algorithm-name: quarter_std # 分片算法
        key-generators: # 分布式ID生成算法
          beautySnowflake:
            type: BEAUTY_SNOWFLAKE
        sharding-algorithms: # 分片算法,配置后可以在分片表的分片策略中被引用
          quarter_std:
            type: CLASS_BASED
            props:
              strategy: standard # 标准算法
              algorithmClassName: com.xlt.sharding.startegy.DateStdShardingAlgorithm # 自定义算法类路径
              lowerLimit: "2023-01-01 00:00:00" # 分片时间下限
              upperLimit: "2023-12-31 24:00:00" # 分片时间上限
              interval: 3  # 分片间隔月数

自定义标准分片算法DateStdShardingAlgorithm需要实现StandardShardingAlgorithm接口,实现其精确分片和范围分片doSharding方法,除此之外还要实现init、getProps、getType等方法。

@Slf4j
public class DateStdShardingAlgorithm implements StandardShardingAlgorithm<Date> {

    private Date lowerDate;

    private Date upperDate;

    private Integer interval;

    private Properties properties;

    private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";

    @Override
    public void init(Properties properties) {
        String lowerLimit = (String) properties.get("lowerLimit");
        String upperLimit = (String) properties.get("upperLimit");
        String interval = (String) properties.get("interval");
        AssertUtil.isNull(lowerLimit, "lowerLimit can't be empty");
        AssertUtil.isNull(upperLimit, "upperLimit can't be empty");
        AssertUtil.isNull(interval, "interval can't be empty");
        this.lowerDate = parseDate(lowerLimit);
        this.upperDate = parseDate(upperLimit);
        this.interval = Integer.parseInt(interval);
        this.properties = properties;
    }

    private Date parseDate(String lowerLimit) {
        Date date = null;
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);
        try {
            date = sdf.parse(lowerLimit);
        } catch (ParseException e) {
            log.error("String date parse error:", e);
            throw new CommonException(e.getMessage());
        }
        return date;
    }

    private String formatDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);
        return sdf.format(date);
    }

    private int getYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.YEAR);
    }

    private int getMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * Get properties.
     *
     * @return properties
     */
    @Override
    public Properties getProps() {
        return properties;
    }

    /**
     * Get type.
     *
     * @return type
     */
    @Override
    public String getType() {
        return "QUARTER_DATE";
    }

    /**
     * Sharding.
     *
     * @param tableNames    available data sources or table names
     * @param shardingValue sharding value
     * @return sharding result for data source or table name
     */
    @Override
    public String doSharding(Collection<String> tableNames, PreciseShardingValue<Date> shardingValue) {
        log.info("tableNames={},shardingValue={},properties={}", JSON.toJSONString(tableNames), JSON.toJSONString(shardingValue), JSON.toJSONString(properties));
        Date date = shardingValue.getValue();
        AssertUtil.isTrue(date.getTime() < lowerDate.getTime(), formatDate(date) + " is before lowerLimit: " + formatDate(lowerDate));
        AssertUtil.isTrue(date.getTime() > upperDate.getTime(), formatDate(date) + " is after upperLimit: " + formatDate(upperDate));
        int idx = calTableIdx(date);
        log.info("idx={}", idx);
        String targetTable = "";
        for (String tableName : tableNames) {
            String tblIdxStr = tableName.substring(tableName.indexOf("t") + 1);
            int tblIdx = Integer.parseInt(tblIdxStr);
            if (tblIdx == idx) {
                targetTable = tableName;
                break;
            }
        }
        log.info("targetTable={}", targetTable);
        return targetTable;
    }

    private int calTableIdx(Date date) {
        int months = (getYear(date) - getYear(lowerDate)) * 12 + getMonth(date);
        int flag = months % interval == 0 ? 0 : 1;
        return months / interval + flag;
    }

    /**
     * Sharding.
     *
     * @param tableNames    available data sources or table names
     * @param shardingValue sharding value
     * @return sharding results for data sources or table names
     */
    @Override
    public Collection<String> doSharding(Collection<String> tableNames, RangeShardingValue<Date> shardingValue) {
        log.info("tableNames={},shardingValue={},properties={}", JSON.toJSONString(tableNames), JSON.toJSONString(shardingValue), JSON.toJSONString(properties));
        Date lowDate = shardingValue.getValueRange().lowerEndpoint();
        Date upDate = shardingValue.getValueRange().upperEndpoint();
        AssertUtil.isTrue(lowDate.getTime() > upDate.getTime(), formatDate(lowDate) + " is after upperEndpoint: " + formatDate(upDate));
        AssertUtil.isTrue(lowDate.getTime() < lowerDate.getTime(), formatDate(lowDate) + " is before lowerLimit: " + formatDate(lowerDate));
        AssertUtil.isTrue(upDate.getTime() > upperDate.getTime(), formatDate(upDate) + " is after upperLimit: " + formatDate(upperDate));
        int lowIdx = calTableIdx(lowDate);
        int upIdx =  calTableIdx(upDate);
        log.info("lowIdx={},upIdx={}", lowIdx, upIdx);
        List<String> targetTbls = new ArrayList<>();
        for (String tableName : tableNames) {
            String dsIdxStr = tableName.substring(tableName.indexOf("t") + 1);
            int dsIdx = Integer.parseInt(dsIdxStr);
            if (dsIdx >= lowIdx && dsIdx <= upIdx) {
                targetTbls.add(tableName);
            }
        }
        log.info("target table names={}", targetTbls);
        return targetTbls;
    }
}

测试用例设计

针对以上的分片算法设计测试用例对其进行验证

1、新增订单数据

使用JMockData和Faker生成100条随机数据进行插入测试

@SpringBootTest
@Slf4j
public class ShardingJdbcTest {

    @Autowired
    private IOrderMapper mapper;

    /**
     * 新增订单
     */
    @Test
    public void addOrder() {
        for (int i = 0; i < 100; i++) {
            OrderPo orderPo = JMockData.mock(OrderPo.class);
            orderPo.setOrderId((long) i);
            orderPo.setStatus(1);
            orderPo.setDeleted(0);
            Faker faker = new Faker();
            Calendar fromDate = Calendar.getInstance();
            fromDate.set(2023, Calendar.JANUARY, 1);
            Calendar toDate = Calendar.getInstance();
            toDate.set(2023, Calendar.DECEMBER, 31);
            orderPo.setOrderDate(faker.date().between(fromDate.getTime(), toDate.getTime()));
            log.info("add new order:{}", orderPo);
            mapper.insert(orderPo);
        }
    }
}

生成数据情况,order_t1中order_date为1-3月:

在这里插入图片描述

order_t2中order_date为4-6月:

在这里插入图片描述

2、查询订单数据-等值查询

使用分片键查询单一时间的数据

    /**
     * 单个查询
     */
    @Test
    public void queryOrder_1() {
        QueryWrapper<OrderPo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_date", DateUtil.parseDate("2023-03-12", DateUtil.DATE_PATTERN_2));
        List<OrderPo> orderPos = mapper.selectList(queryWrapper);
        log.info("orderPos={}", JSON.toJSONString(orderPos));
    }

从日志中看,找到目标表之后,查到了对应的数据

在这里插入图片描述

3、范围查询订单数据

使用分片键查询一定时间范围内的数据

/**
 * 范围查询
 */
@Test
public void queryOrder_2() {
    QueryWrapper<OrderPo> queryWrapper = new QueryWrapper<>();
    queryWrapper.between("order_date", DateUtil.parseDate("2023-05-01", DateUtil.DATE_PATTERN_2), DateUtil.parseDate("2023-08-01", DateUtil.DATE_PATTERN_2));
    List<OrderPo> orderPos = mapper.selectList(queryWrapper);
    log.info("orderPos={}", JSON.toJSONString(orderPos));
}

可以看到查询过程中,根据order_date的范围,经过分片算法的计算,路由到了order_t_2和order_t_3表,然后查回相应的数据

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaFX下载

下载地址&#xff1a; https://gluonhq.com/products/javafx/

postman和node.js的使用

一 nodejs下载 下载链接&#xff1a; nodejs官网&#xff1a; https://nodejs.org/zh-cn/download 我使用的windows .msi安装方式&#xff0c;双击一直下一步就行 当前安装完成后的版本&#xff1a;1.下载 2.安装步骤 下载完成后&#xff0c;双击安装包&#xff0c;开始安装&…

在MAC电脑上将NTFS格式移动硬盘转换为ExFAT格式

注意&#xff1a;转化之前先将移动硬盘中的内容进行备份 1、点击桌面上的【前往】&#xff0c;选择【实用工具】 2、在列表中选择【磁盘工具】 3、在左侧选中你的磁盘&#xff0c;点击右侧上方的【抹掉】,注意&#xff1a;将永久抹掉储存在上面的所有数据&#xff0c;因此需要…

计算机毕业设计 JSPM校园闲置物品交易平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

分类预测 | Matlab实现基于LFDA-SVM局部费歇尔判别数据降维结合支持向量机的多输入分类预测

分类预测 | Matlab实现基于LFDA-SVM局部费歇尔判别数据降维结合支持向量机的多输入分类预测 目录 分类预测 | Matlab实现基于LFDA-SVM局部费歇尔判别数据降维结合支持向量机的多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于局部费歇尔判别数据降维的L…

Ubuntu下高效Vim的搭建(离线版)

软件界面 可以看到界面下方有一些常用提示信息&#xff1a;文件路径、format、文件类型、光标所在的坐标(x,y)、进度条(百分比)、日期时间 会提示已定义的变量名词(快速补全) 搭建方法 下载资源文件 把Vim 和 .vimrc 拷贝到家目录下&#xff0c;并执行tar -xvf Vim 即可。 …

汇川PLC学习Day3:轴控代码编写、用户程序结构说明与任务配置示例、用户变量空间与编址

汇川PLC学习Day3&#xff1a;轴控代码编写、用户程序结构说明与任务配置示例、用户变量空间与编址 一、新建轴与轴控代码编写 1. 新建轴 (1)新建一个轴 &#xff08;2&#xff09;将轴名字更新为实际名字 可以后面实例化后再更改&#xff0c;汇川可以在更新名字时同步更新…

使用DeepSpeed加速大型模型训练(二)

使用DeepSpeed加速大型模型训练 在这篇文章中&#xff0c;我们将了解如何利用Accelerate库来训练大型模型&#xff0c;从而使用户能够利用DeeSpeed的 ZeRO 功能。 简介 尝试训练大型模型时是否厌倦了内存不足 (OOM) 错误&#xff1f;我们已经为您提供了保障。大型模型性能非…

倒⽴摆闭环控制的设计与开发

倒立摆是一种典型的多变量、高阶次、非线性、强耦合、自然不稳定系统&#xff0c;常被用来检验新的控制理论和算法的正确性及其在实际应用中的有效性。 load func_ip_comp% Locations of Poles and Zeroes of Open-Loop Compensated Transfer Function in Complex Plane figur…

查看当前所有的数据库

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 先看下服务有没有启动,看下我这个是什么意思 exit 退出MySQL环境回车后是这样的 重新进入MySQL环境 查看当前所有的数据库 show datebases; mysql&…

nrf523832 串口点LED

/* P0.06&#xff1a;串口发送TXD P0.08&#xff1a;串口接收RXD P0.05&#xff1a;串口RTS&#xff1a;发送请求&#xff0c;硬件流控开启时有效 P0.07&#xff1a;串口CTS&#xff1a;发送允许&#xff0c;硬件流控开启时有效 */ #define RX_PIN_NUMBER 8 #define TX_PIN_N…

学习Bootstrap 5的第十天

目录 卡片 基础的卡片 实例 页眉和页脚 实例 多种颜色卡片 实例 标题、文本和链接 实例 图片卡片 实例 卡片图像叠加 实例 下拉菜单 基础的下拉列表 实例 下拉列表分隔线 实例 下拉列表标题 实例 禁用的和活动的项目 实例 下拉列表位置 实例 下拉菜单…

SpringMVC之文件的上传下载(教你如何使用有关SpringMVC知识实现文件上传下载的超详细博客)

目录 前言 一、文件上传 1. 配置多功能视图解析器&#xff08;spring-mvc.xml&#xff09; 2. 添加文件上传页面&#xff08;upload.jsp&#xff09; upload.jsp 3.做硬盘网络路径映射 4. 编写一个处理页面跳转的类 PageController.java ClazzController.java 5. 初步模…

【数据分享】STRM 90米分辨率DEM地形数据(无需转发/全国/分省/分市)

地形数据是我们在各种设计、研究中都经常使用的基础数据&#xff01;之前我们分享过12.5米精度的DEM地形数据、30米精度的DEM地形数据(均可查看之前的文章获悉详情&#xff09;。 本次给大家带来的是90米分辨率的DEM地形数据——STRM 90m高程数据&#xff01;该数据是由美国太…

学成在线-网站搭建

文章目录 代码素材来自b站pink老师 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>学成在线首…

《PWA实战:如何为你的网站增加离线功能和推送通知》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Hbase解决ERROR: KeeperErrorCode = ConnectionLoss for /hbase/master报错

1、在单机模式中&#xff0c;要先修改一个文件&#xff1a;/usr/local/hbase/conf/hbase-site.xml hbase-site.xml内容&#xff1a; <configuration><property><name>hbase.rootdir</name><value>file:///usr/local/hbase/hbase-tmp</value…

dji uav建图导航系列()move_base

文章目录 1、导航框架2、move_base功能包3、amcl功能包4、代价地图的配置4.1、通用配置文件4.2、全局规划配置文件4.3、局部规划配置文件5、局部规划器配置6、launch文件1、导航框架 导航的关键是机器人定位和路径规划两大部分 move_base:实现机器人导航中的最优路径规划 am…

ES8生产实践——pod日志采集(Elastic Agent方案)

pod日志采集方案 方案选型 DaemonSetElastic Agent方案&#xff1a;使用DaemonSet控制器在每个kubernetes集群节点上运行elastic agent服务&#xff0c;业务容器日志目录统一挂载到节点指定目录下。在fleet中配置集成Custom Logs集成策略&#xff0c;指定日志采集目录和inges…

【C语言 C++ 源码】课程设计 学生成绩管理系统

文章目录 演示 对学生的信息、学科成绩进行管理&#xff0c;并进行统计。 对信息进行读写文件操作。自动保存数据到文件。 演示 此课设分为C语言和C两个版本。对于学生数据&#xff0c;会自动保存数据到本地&#xff0c;下次运行自动读取数据。 部分源码&#xff1a; printf…