shardingsphere实现按月分表

背景

一个老的springboot2的项目,每日订单主表超6万条,从表超10万条,考虑按月分表。调研了shardingsphere和mycat,还顺眼看了下阿里的两个组件。最后决定使用shardingsphere。怪就怪自己太相信大模型,在deepseek和kimi中搜索后,按它们的说法就开干了起来,结果这两个傻货,一顿瞎指挥,浪费了一天时间,每次跟他说不对吧,或者你这样写太不优雅,它总是态度很好的说理解你的需求,其实还是在胡扯。中间无数次想放弃,使用mycat,但又觉得研究到现在,放弃就太可惜了。就放弃了跟大模型的对话,直接到apache官网上找到对应版本的文档写好了。

技术栈

  • springboot
  • mysql8.0
  • shardingsphere 5.3.2
  • jpa 2.2.2

 

需求描述

主表:tb_erporder,主订单,分片:tb_erporder_yyyyMM

从表:tb_erporder_items,订单明细,分片:tb_erporder_items_yyyyMM

 配置变更

1. shardingsphere独立出一个配置文件,方便维护,跟application一样,分成dev和prd两个

2. application配置文件的datasource要改成从shardingsphere配置文件获取

datasource:
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    url: jdbc:shardingsphere:classpath:sharding-dev.yml

3.shardingsphere配置文件

为了配置这个文件,费了老鼻子的劲,一开始按大模型教的,配置键用短横线分隔命名法,实际官方5.x已全部使用驼峰了。其次在配置中如果出现错误,提示非常不友好,他会提示第一行的dataSources无法读取,我以为这行配置问题,改了好多次,大模型一会告诉用datasource,一会是dataSource,一会又是datasources,跟两傻子了样,一个敢教,一个敢学。下面这个错误,其实是因为rules写的不对,我没有注意有一小行:Cannot create property=rules,只看到下面的dataSources

HHH000342: Could not obtain connection to query metadata : Failed to initialize pool: Cannot create property=rules for JavaBean=org.apache.shardingsphere.infra.yaml.config.pojo.YamlRootConfiguration@7f22687e 

in 'reader', line 1, column 1: dataSources:

正确配置如下:

# 数据源集合
dataSources:
  # 数据库1
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/orderdb?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: xxxxxx
rules:
  - !SHARDING
    tables:
      tb_erporder:
        # 真实数据节点,比如数据库源以及数据库在数据库中真实存在的
        actualDataNodes: ds_0.tb_erporder_$->{202501..205012}
        # 分表策略
        tableStrategy:
          # 用于单分片键的标准分片场景
          standard:
            # 分片键
            shardingColumn: send_date
            # 分片算法,对应 rules[0].shardingAlgorithms
            shardingAlgorithmName: tb_erporder-inline
      tb_erporder_items:
        actualDataNodes: ds_0.tb_erporder_items_$->{202501..205012}
        tableStrategy:
          standard:
            shardingColumn: send_date
            shardingAlgorithmName: tb_erporder_items-inline
    # 分片算法
    shardingAlgorithms:
      # 数据表分片算法
      tb_erporder-inline:
        # 根据分片键 分片
        type: INLINE
        # 分片算法
        props:
          algorithm-expression: tb_erporder_$->{new java.text.SimpleDateFormat("yyyyMM").format(send_date)}
      tb_erporder_items-inline:
        type: INLINE
        props:
          algorithm-expression: tb_erporder_items_$->{new java.text.SimpleDateFormat("yyyyMM").format(send_date)}
# 展现逻辑 SQL & 真实 SQL
props:
  sql-show: true

 

# 20250405更新

分片是成功了,今天做范围查询时,shardingsphere居然把配置表里的202501-205012表全查了一次,我传入的查询日期只是当天数据。。。

于是问大模型有没有办法解决,这次大模型靠谱了点,给了解决方案:自定义切片算法。

先写一个自定义的算法配置类:

/**
 * @author: zhouyu
 * @date: 2025/4/5
 * @Description:
 */
public class MonthRangeShardingAlgorithm implements StandardShardingAlgorithm {
    @Override
    public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
        // 精确分片逻辑(单月查询)
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
        String tableSuffix = sdf.format(shardingValue.getValue());
        String tableName = shardingValue.getLogicTableName() + "_" + tableSuffix;

        // 检查表是否存在
        if (availableTargetNames.contains(tableName)) {
            return tableName;
        }
        throw new IllegalArgumentException("无效的表名: " + tableName);
    }

    @Override
    public Collection<String> doSharding(Collection availableTargetNames, RangeShardingValue shardingValue) {
        // 范围查询分片逻辑
        Set<String> result = new LinkedHashSet<>();

        // 1. 获取查询范围
        Range<Date> range = shardingValue.getValueRange();
        Date lower = range.hasLowerBound() ? range.lowerEndpoint() : null;
        Date upper = range.hasUpperBound() ? range.upperEndpoint() : null;

        // 2. 如果没有范围(全表查询),返回所有可用表(或根据业务限制)
        if (lower == null && upper == null) {
            return availableTargetNames;
        }

        // 3. 计算涉及的月份范围
        Calendar cal = Calendar.getInstance();
        if (lower != null) {
            cal.setTime(lower);
        } else if (upper != null) {
            cal.setTime(upper);
        }

        // 4. 如果是单月查询(不跨月)
        if (isSameMonth(lower, upper)) {
            String tableName = shardingValue.getLogicTableName() + "_" +
                    new SimpleDateFormat("yyyyMM").format(lower != null ? lower : upper);
            if (availableTargetNames.contains(tableName)) {
                result.add(tableName);
            }
            return result;
        }

        // 5. 处理跨月查询
        Calendar current = (Calendar) cal.clone();
        while (upper == null || !current.getTime().after(upper)) {
            String tableName = shardingValue.getLogicTableName() + "_" +
                    new SimpleDateFormat("yyyyMM").format(current.getTime());
            if (availableTargetNames.contains(tableName)) {
                result.add(tableName);
            }

            // 移动到下个月
            current.add(Calendar.MONTH, 1);
            if (upper != null && current.getTime().after(upper)) {
                break;
            }
        }

        return result;
    }
    private boolean isSameMonth(Date d1, Date d2) {
        if (d1 == null || d2 == null) return false;
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(d1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(d2);
        return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
                cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
    }

再将配置文件的INLINE,修改为BASIC_CLASSED

 # 分片算法
    shardingAlgorithms:
      # 数据表分片算法
      tb_erporder-inline:
        # 根据分片键 Hash 分片
        type: CLASS_BASED
        # 分片数量
        props:
          strategy: standard
          algorithmClassName: com.nantangju.backorder.config.MonthRangeShardingAlgorithm
      tb_erporder_items-inline:
        type: CLASS_BASED
        props:
          strategy: standard
          algorithmClassName: com.nantangju.backorder.config.MonthRangeShardingAlgorithm

 

写在最后

大模型写代码,现在还没有成熟到可以代替程序员的地步,但相信总有一天会成熟起来,现在的通义灵感,已经可以解决一部分基础问题了,感觉已经可以代替前端工程师了。但是如果没有一定基础,全靠大模型现在还不是时候。

其次是又一次深深的体会到,作为一个技术人员,要对技术充满一定的执着,才能体会到最后成功的喜悦!

5.3.2官网地址:概览 :: ShardingSphere (apache.org)

 

 

 

 

posted on 2025-04-04 23:17  zhouyu  阅读(751)  评论(0)    收藏  举报

导航