SpringBoot+JPA+ShardingSphere4.x单库按月份分表

1.走过的弯路

在研究分表的时候,我他喵的是真的体会到了"浩如烟海"是啥意思.要么就是资料太老,要么就很少是按照月份分表的. 中间我走了很多弯路,甚至一度怀疑jap不适合用来进行分表.

从我看过的资料来说,没有一个博客介绍使用Shardingsphere进行分表是思路是什么样的,这也是我走了很多弯路的原因.

先介绍背景: 单个数据库,按照月份进行分表,每个表有月份的后缀,类似于: table_202006.
我先说下我的误区, 一开始我以为分表查询只需要配置库级别的分表规则就行了.这种想法是错误的,Shardingsphere是按照表级别进行配置的, 也就是说你想要分表查询哪个就配置对应表的分表规则. 其他没有配置分表规则的表则还是按照基本的表名进行查询.

还有一个坑, 一般我们会打印sql, 引入Shardingsphere之后,原本的打印的sql并不会是最后路由的表.举个栗子, 我们原本的查询"select * from base". 分表之后我们的查询最后路由到了base_202006表上面, 但是sql显示的还是"select * from base". 需要我们开启Shardingsphere的日志才能看到最终的路由.

2.Shardingsphere
2.1引入依赖

具体的版本,根据时间自行更新

<dependency>
   <groupId>org.apache.shardingspheregroupId>
   <artifactId>sharding-jdbc-spring-boot-starterartifactId>
   <version>4.1.1version>
dependency>
2.2 application-dev.yml

看到这个名字,相信大家应该都知道了还有一个application.yml配置.分表的关键配置都在application-dev.yml, 所以直贴这个配置的内容了.

#配置数据源
spring:
  #配置 Jpa
  jpa:
    hibernate:
      # 生产环境设置成 none,避免程序运行时自动更新数据库结构
      ddl-auto: none

  shardingsphere:
    datasource:
      names: db0 # 数据源名称
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/database?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
        username: root
        password: password
    sharding:
      tables:
        base: # 表名
          actual-data-nodes: db0.base_$->{2019..2020}0$->{1..9}, db0.base_$->{2019..2020}$->{10..12}
          table-strategy:
            standard:
              sharding-column: date # 分片键
              precise-algorithm-class-name: com.bihu.kankan.config.TableShardingAlgorithm # 实现类的完全限定类名
              range-algorithm-class-name: com.bihu.kankan.config.TableRangeShardingAlgorithm
    props:
      sql:
        show: true

整个配置的重点都在shardingsphere这个级别下面,我来一个个的解释:

  • datasource: 数据库连接的配置. url这个配置如果是使用阿里云的连接可能需要改成jdbc-url
  • names: 自定义的数据源名称
  • db0: 就是刚刚配置的数据源名称
  • sharding.tables.base: base就是数据库中需要分表查询的表名
  • actual-data-nodes: 这个是重点,需要inline表达式进行配置. 上面的配置意思是:查询数据源db0下面2019年-2020年的base表. 这个规则可以查看官网的说明 行表达式
  • sharding-column: base表中的字段,在查询的时候需要传入这个字段,然后根据自己定义的分表规则判断应该路由到那张表上. 不仅仅是date字段, 也可以是snowflake算法生成的id字段(因为可以根据id字段反推数据入库时间). 还可以是自增id字段(模运算决定路由的表)等等.
  • precise-algorithm-class-name: 自定义的分表逻辑实现类, 这里我们指定的是精确查询,即sql中是=的时候会触发此规则.
  • range-algorithm-class-name: 复合分片策略, 提供对 SQL 语句中的 =, >, <, >=, <=, IN 和 BETWEEN AND 的分片操作支持
  • props.sql.show: 展示路由后的sql, 前面已经说了,这是个坑.
3.自定义的分表规则实现类

PreciseShardingAlgorithm中的泛型类型和上面指定的sharding-column类型有关

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collection;

/**
 * 根据当前的月份进行分表查询
 * @author LiYilin
 */
@Component
public class TableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {

    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
        // 基本的表名_年份月份  base_199001
        String targetTable = preciseShardingValue.getLogicTableName() + "_" + LocalDate.now().format(formatter);
        if (availableTargetNames.contains(targetTable)){
            return targetTable;
        }
        throw new UnsupportedOperationException("无效的表名称: " + targetTable);
    }
}

TableRangeShardingAlgorithm.java

import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.springframework.stereotype.Component;

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

@Component
public class TableRangeShardingAlgorithm implements RangeShardingAlgorithm<Integer> {

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Integer> rangeShardingValue) {
        Collection<String> collect = new ArrayList<>();
        Range<Integer> valueRange = rangeShardingValue.getValueRange();
        //TODO 这种写法只支持between, 但是效率很高
        String between = rangeShardingValue.getLogicTableName() + "_" + String.valueOf(valueRange.lowerEndpoint()).substring(0, 6);
        String and = rangeShardingValue.getLogicTableName() + "_" + String.valueOf(valueRange.upperEndpoint()).substring(0, 6);
        for (String each : collection) {
            if (between.equals(each) || and.equals(each)) {
                collect.add(each);
            }
        }
        return collect;
    }
}

至此,一个分表操作基本完成.

你可能感兴趣的:(Spring,Boot,SpringBoot,JPA,Shardingsphere,数据库分表,月份)