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)