正确使用shardingsphere-jdbc5.5.0

3,210 阅读4分钟

我们先了解一下什么是ShardingSphere:
  Apache ShardingSphere 是一款分布式的数据库生态系统,可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。
  Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。 它站在数据库的上层视角,关注它们之间的协作多于数据库自身。
  ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。

image.png

那为什么写这篇文章呢?在决定使用shardingsphere-jdbc进行分表的时候,网上都是使用的 shardingsphere-jdbc-core-spring-boot-starter来进行集成的,但是maven仓库最新版本的shardingsphere-jdbc-core-spring-boot-starter只到5.2.1且最后发布时间是2022年。

image.png   所以才会有这篇文章的诞生。由于本人第一次写,各位大佬请担待。

  集成正式开始,各位小伙伴请坐好。
  本次集成采用的springboot版本为3.3.2。
  pom文件需要引入 shardingsphere-jdbc 及orm框架 orm框架自己选择即可 jpa 与 mybatis都可以:

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc</artifactId>
            <version>5.5.0</version>
            <!--找不到这个包排除掉 不影响使用-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shardingsphere</groupId>
                    <artifactId>shardingsphere-test-util</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

接下来我们配置sharding-db.yml

mode:
  type: Standalone
  repository:
    type: JDBC
dataSources: # 数据源配置 YamlJDBCConfiguration
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: jdbc:mysql://localhost:3306/sharding_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: root

rules:  # YamlShardingRuleConfiguration
  - !SHARDING
    tables: # 数据分片规则配置
      t_order:
        actualDataNodes: ds_0.t_order_${0..3}
        tableStrategy: # 分表策略,同分库策略 databaseStrategy  三种模式 standard  用于单分片键的标准分片场景, complex 用于多分片键的复合分片场景, hint 分片策略 onoe 不分片
          complex:
            shardingColumns: order_seq,buyer_id # 分片列名称 订单号,买家id
            shardingAlgorithmName: order-sharding # 分片算法名称
        keyGenerateStrategy: # 分布式序列策略 不配置也可以 下面配置了默认的
          column: id # 自增列名称,缺省表示不使用自增主键生成器
          keyGeneratorName: snowflake # 分布式序列算法名称
    # 分片算法配置
    shardingAlgorithms:
      order-sharding: # 分片算法名称 需要和上面的对应上
        type: CLASS_BASED # 分片算法类型  org.apache.shardingsphere.sharding.spi.ShardingAlgorithm  ClassBasedShardingAlgorithm
        props:
          algorithmClassName: com.example.shardingjdbcdemo.config.OrderComplexKeysShardingAlgorithm
          strategy: complex # CLASS_BASED 这个必须配置 ClassBasedShardingAlgorithm 会判断类型
    # 分布式序列算法配置
    keyGenerators:
      snowflake: # 分布式序列算法名称 和上面的keyGeneratorName对应
        type: SNOWFLAKE # 分布式序列算法类型
    defaultKeyGenerateStrategy:
      column: id
      keyGeneratorName: snowflake
    defaultDatabaseStrategy:
      none:
    # 分片审计算法配置
    auditors:
      sharding_key_required_auditor:
        type: DML_SHARDING_CONDITIONS
  # 不需要分表的配置
  - !SINGLE
    tables:  # YamlSingleRuleConfiguration
      - "ds_0.*"
    defaultDataSource: ds_0 # 只用于create table 生效
props:
  sql-show: true

application.yml配置

spring:
  application:
    name: sharding-jdbc-demo
  datasource:
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    url: jdbc:shardingsphere:classpath:sharding-db.yml

  data:
    jdbc:
      dialect: mysql
  main:
    allow-bean-definition-overriding: true

mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:mapper/*.xml

自定义分片算法:

package com.example.shardingjdbcdemo.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;


@Component
@Slf4j
public class OrderComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<String> {

    /**
     * 分片
     * @param collection 表名称 t_order_0 t_order_1 t_order_2 t_order_3
     * @param complexKeysShardingValue 原表名称t_order 分片列名称 order_seq buyer_id
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<String> complexKeysShardingValue) {
        log.info("collection:{}",collection);
        //返回使用的表名称
        Collection<String> result = new HashSet<>();
        String logicTableName = complexKeysShardingValue.getLogicTableName();
        // map存储的 order_seq buyer_id 的值 分片的字段
        Map<String, Collection<String>> columnNameAndShardingValuesMap = complexKeysShardingValue.getColumnNameAndShardingValuesMap();
        //存在买家id 按照买家分片
        Collection<String> columnValues = columnNameAndShardingValuesMap.get("buyer_id");
        if (!CollectionUtils.isEmpty(columnValues)) {
            for (String columnValue : columnValues) {
                String tableName = getTableName(logicTableName, columnValue);
                 result.add(tableName);
            }
            return result;
        }
        // 不存在的时候如何处理 其实就是订单号
        columnNameAndShardingValuesMap.forEach((k,otherColumnValues)->{
            for (String otherColumnValue : otherColumnValues) {
                String tableName = getTableName(logicTableName, otherColumnValue);
                result.add(tableName);
            }
        });
        return result;
    }

    /**
     * 获取表名称 分片数量也可以动态设置,这里写死了分4张表
     * @param logicTableName
     * @param columnValue
     * @return
     */
    private String getTableName(String logicTableName,String columnValue){
      return   logicTableName + "_" + (Math.abs(columnValue.hashCode()) % 4);
    }
}

本人对t_order、t_order_item 同时进行了分表操作 t_order采用jpa t_order_item采用mybatis

jpa 对应的配置如下: 实体类:

package com.example.shardingjdbcdemo.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;

import java.math.BigDecimal;


@Table(name = "t_order")
@Setter
@Getter
@Entity
public class Order {

    /**
     * 需要标识id 作为主键 需要使用 spring的注解id 不能使用jdk的
     */
    @Id
    private Long id;

    /**
     * 商品名称
     */
    private String goodsName;

    /**
     * 买家id
     */
    private String buyerId;

    /**
     * 价格
     */
    private BigDecimal price;

    /**
     * 订单号 订单号生成带有 表标识
     */
    private String orderSeq;


}

jpa repository

@Repository
public interface OrderRepository extends CrudRepository<Order,Long> {
}

如果使用mybatis 参考下面配置:

package com.example.shardingjdbcdemo.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;


@Data
@TableName(value = "t_order_item")
public class OrderItem {

    private Long id;

    private String orderSeq;

    private String goodsName;
}

@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {
    
}