乐趣区

关于springboot:SpringBoot-2-种方式快速实现分库分表轻松拿捏

大家好,我是小富~

本文是《分库分表 ShardingSphere5.x 原理与实战》系列的第三篇文章,本文将为您介绍 ShardingSphere 的一些根底个性和架构组成,以及在 Springboot 环境下通过 JAVA 编码Yml 配置 两种形式疾速实现分库分表。

本文案例 demo 地址

一、什么是 ShardingSphere?

shardingsphere 是一款开源的分布式关系型数据库中间件,为 Apache 的顶级我的项目。其前身是 sharding-jdbcsharding-proxy 的两个独立我的项目,起初在 2018 年合并成了一个我的项目,并正式更名为 ShardingSphere。

其中 sharding-jdbc 为整个生态中最为经典和成熟的框架,最早接触分库分表的人应该都晓得它,是学习分库分表的最佳入门工具。

现在的 ShardingSphere 曾经不再是单纯代指某个框架,而是一个残缺的技术生态圈,由三款开源的分布式数据库中间件 sharding-jdbc、sharding-proxy 和 sharding-sidecar 所形成。前两者问世较早,性能较为成熟,是目前广泛应用的两个分布式数据库中间件,因而在后续的文章中,咱们将重点介绍它们的特点和应用办法。

二、为什么选 ShardingSphere?

为了答复这个问题,我整顿了市面上常见的分库分表工具,包含 ShardingSphereCobarMycatTDDLMySQL Fabric 等,并从多个角度对它们进行了简略的比拟。

Cobar

Cobar 是阿里巴巴开源的一款基于 MySQL 的分布式数据库中间件,提供了分库分表、读写拆散和事务管理等性能。它采纳轮询算法和哈希算法来进行数据分片,反对分布式分表,然而不反对单库分多表。

它以 Proxy 形式提供服务,在阿里外部被宽泛应用已开源,配置比拟容易,无需依赖其余货色,只须要有 Java 环境即可。兼容市面上简直所有的 ORM 框架,仅反对 MySQL 数据库,且事务反对方面比拟麻烦。

MyCAT

Mycat 是社区爱好者在阿里 Cobar 根底上进行二次开发的,也是一款比拟经典的分库分表工具。它以 Proxy 形式提供服务,反对分库分表、读写拆散、SQL 路由、数据分片等性能。

兼容市面上简直所有的 ORM 框架,包含 Hibernate、MyBatis 和 JPA 等都兼容,不过,美中不足的是它仅反对 MySQL 数据库,目前社区的活跃度绝对较低。

TDDL

TDDL 是阿里巴巴团体开源的一款分库分表解决方案,能够主动将 SQL 路由到相应的库表上。它采纳了垂直切分和程度切分两种形式来进行分表分库,并且反对多数据源和读写拆散性能。

TDDL 是基于 Java 开发的,反对 MySQL、Oracle 和 SQL Server 数据库,并且能够与市面上 Hibernate、MyBatis 等 ORM 框架集成。

不过,TDDL 仅反对一些阿里巴巴外部的工具和框架的集成,对于内部公司来说可能绝对有些局限性。同时,其文档和社区活跃度相比 ShardingSphere 来说稍显有余。

Mysql Fabric

MySQL Fabric是 MySQL 官网提供的一款分库分表解决方案,同时也反对 MySQL 其余性能,如高可用、负载平衡等。它采纳了治理节点和代理节点的架构,其中治理节点负责实时治理分片信息,代理节点则负责接管并解决客户端的读写申请。

它仅反对 MySQL 数据库,并且能够与市面上 Hibernate、MyBatis 等 ORM 框架集成。MySQL Fabric 的文档相对来说比拟简略,而且因为是官网提供的解决方案,其社区活跃度也绝对较低。

ShardingSphere

ShardingSphere 成员中的 sharding-jdbc 以 JAR 包的模式下提供分库分表、读写拆散、分布式事务等性能,但仅反对 Java 利用,在利用扩大上存在局限性。

因而,ShardingSphere 推出了独立的中间件 sharding-proxy,它基于 MySQL 协定实现了通明的分片和多数据源性能,反对各种语言和框架的应用程序应用,对接的应用程序简直无需更改代码,分库分表配置可在代理服务中进行治理。

除了反对 MySQL,ShardingSphere 还能够反对 PostgreSQL、SQLServer、Oracle 等多种支流数据库,并且能够很好地与 Hibernate、MyBatis、JPA 等 ORM 框架集成。重要的是,ShardingSphere 的开源社区十分沉闷。

如果在应用中呈现问题,用户能够在 GitHub 上提交 PR 并失去疾速响应和解决,这为用户提供了足够的安全感。

产品比拟

通过对上述的 5 个分库分表工具进行比拟,咱们不难发现,就整体性能、功能丰富度以及社区反对等方面来看,ShardingSphere 在泛滥产品中劣势还是比较突出的。下边用各个产品的次要指标整顿了一个表格,看着更加直观一点。

三、ShardingSphere 成员

ShardingSphere 的次要组成成员为sharding-jdbcsharding-proxy,它们是实现分库分表的两种不同模式:

sharding-jdbc

它是一款轻量级 Java 框架,提供了基于 JDBC 的分库分表性能,为客户端直连模式。应用 sharding-jdbc,开发者能够通过简略的配置实现数据的分片,同时无需批改原有的 SQL 语句。反对多种分片策略和算法,并且能够与各种支流的 ORM 框架无缝集成。

sharding-proxy

它是基于 MySQL 协定的代理服务,提供了通明的分库分表性能。应用 sharding-proxy 开发者能够将分片逻辑从应用程序中解耦进去,无需批改利用代码就能实现分片性能,还反对多数据源和读写拆散等高级个性,并且能够作为独立的服务运行。

四、疾速实现

咱们先应用 sharding-jdbc 来疾速实现分库分表。相比于 sharding-proxy,sharding-jdbc 实用于简略的利用场景,不须要额定的环境搭建等。下边次要基于 SpringBoot 的两种形式来实现分库分表,一种是通过 YML 配置 形式,另一种则是通过纯 Java 编码 形式(不可并存 )。在后续章节中,咱们会独自具体介绍如何应用sharding-proxy 以及其它高级个性。

ShardingSphere 官网地址:https://shardingsphere.apache.org/

筹备工作

在开始实现之前,须要对数据库和表的拆分规定进行明确。以对 t_order 表进行分库分表拆分为例,具体地,咱们将 t_order 表拆分到两个数据库中,别离为 db1db2,每个数据库又将该表拆分为三张表,别离为 t_order_1t_order_2t_order_3

db0
├── t_order_0
├── t_order_1
└── t_order_2
db1
├── t_order_0
├── t_order_1
└── t_order_2

JAR 包引入

引入必要的 JAR 包,其中最重要的是 shardingsphere-jdbc-core-spring-boot-startermysql-connector-java这两个。为了保障性能的全面性和兼容性,以及防止因低版本包导致的不必要谬误和调试工作,我抉择的包版本都较高。

shardingsphere-jdbc-core-spring-boot-starter 是 ShardingSphere 框架的外围组件,提供了对 JDBC 的分库分表反对;而 mysql-connector-java 则是 MySQL JDBC 驱动程序的实现,用于连贯 MySQL 数据库。除此之外,我应用了 JPA 作为长久化工具还引入了相应的依赖包。

<!-- jpa 长久化工具 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>2.7.6</version>
</dependency>
<!-- 必须引入的包 mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.31</version>
</dependency>
<!-- 必须引入的包 ShardingSphere -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.2.0</version>
</dependency>

YML 配置

我集体是比拟举荐应用 YML 配置形式来实现 sharding-jdbc 分库分表的,应用 YML 配置形式不仅能够让分库分表的实现更加简略、高效、可保护,也更合乎 SpringBoot 的开发标准。

在 src/main/resources/application.yml 门路文件下增加以下残缺的配置,即可实现对 t_order 表的分库分表,接下来拆解看看每个配置模块都做了些什么。


spring:
  shardingsphere:
    # 数据源配置
    datasource:
      # 数据源名称,多数据源以逗号分隔
      names: db0,db1
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/shardingsphere-db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 123456
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/shardingsphere-db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 123456
    # 分片规定配置
    rules:
      sharding:
        # 分片算法配置
        sharding-algorithms:
          database-inline:
            # 分片算法类型
            type: INLINE
            props:
              # 分片算法的行表达式(算法自行定义,此处为不便演示成果)algorithm-expression: db$->{order_id > 4?1:0}
          table-inline:
            # 分片算法类型
            type: INLINE
            props:
              # 分片算法的行表达式
              algorithm-expression: t_order_$->{order_id % 4}
        tables:
          # 逻辑表名称
          t_order:
            # 行表达式标识符能够应用 ${...} 或 $->{...},但前者与 Spring 自身的属性文件占位符抵触,因而在 Spring 环境中应用行表达式标识符倡议应用 $->{...}
            actual-data-nodes: db${0..1}.t_order_${0..3}
            # 分库策略
            database-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: database-inline
            # 分表策略
            table-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: table-inline
    # 属性配置
    props:
      # 展现批改当前的 sql 语句
      sql-show: true

以下是 shardingsphere 多数据源信息的配置,其中的 names 示意须要连贯的数据库别名列表,每增加一个数据库名就须要新增一份对应的数据库连贯配置。

spring:
  shardingsphere:
    # 数据源配置
    datasource:
      # 数据源名称,多数据源以逗号分隔
      names: db0,db1
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/shardingsphere-db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 123456
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/shardingsphere-db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 123456

rules节点下为分片规定的配置,sharding-algorithms 节点为自定义的分片算法模块,分片算法能够在后边配置表的分片规定时被援用,其中:

  • database-inline:自定义的分片算法名称;
  • type:该分片算法的类型,这里先以 inline 为例,后续会有具体章节介绍;
  • props:指定该分片算法的具体内容,其中 algorithm-expression 是该分片算法的表达式,即依据分片键值计算出要拜访的实在数据库名或表名,。

db$->{order_id % 2} 这种为 Groovy 语言表达式,示意对分片键 order_id 进行取模,依据取模后果计算出 db0、db1,分表的表达式同理。

spring:
  shardingsphere:
    # 规定配置
    rules:
      sharding:
        # 分片算法配置
        sharding-algorithms:
          database-inline:
            # 分片算法类型
            type: INLINE
            props:
              # 分片算法的行表达式(算法自行定义,此处为不便演示成果)algorithm-expression: db$->{order_id % 2}
          table-inline:
            # 分片算法类型
            type: INLINE
            props:
              # 分片算法的行表达式
              algorithm-expression: t_order_$->{order_id % 3}

tables节点定义了逻辑表名 t_order 的分库分表规定。actual-data-nodes 用于设置物理数据节点的数量。

db${0..1}.t_order_${0..3} 表达式意思此逻辑表在不同数据库实例中的散布状况,如果只想单纯的分库或者分表,能够调整表达式,分库db${0..1}、分表t_order_${0..3}

db0
├── t_order_0
├── t_order_1
└── t_order_2
db1
├── t_order_0
├── t_order_1
└── t_order_2
spring:
  shardingsphere:
    # 规定配置
    rules:
      sharding:
        tables:
          # 逻辑表名称
          t_order:
            # 行表达式标识符能够应用 ${...} 或 $->{...},但前者与 Spring 自身的属性文件占位符抵触,因而在 Spring 环境中应用行表达式标识符倡议应用 $->{...}
            actual-data-nodes: db${0..1}.t_order_${0..3}
            # 分库策略
            database-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: database-inline
            # 分表策略
            table-strategy:
              standard:
                # 分片列名称
                sharding-column: order_id
                # 分片算法名称
                sharding-algorithm-name: table-inline

database-strategytable-strategy别离设置了分库和分表策略;

sharding-column示意依据表的哪个列(分片键)进行计算分片路由到哪个库、表中;

sharding-algorithm-name 示意应用哪种分片算法对分片键进行运算解决,这里能够援用方才自定义的分片算法名称应用。

props节点用于设置其余的属性配置,比方:sql-show示意是否在控制台输入解析革新后实在执行的 SQL 语句以便进行调试。

spring:
  shardingsphere:
    # 属性配置
    props:
      # 展现批改当前的 sql 语句
      sql-show: true

跑个单测在向数据库中插入 10 条数据时,发现数据曾经绝对平均地插入到了各个分片中。

JAVA 编码

如果您不想通过 yml 配置文件实现主动拆卸,也能够应用 ShardingSphere 的 API 实现雷同的性能。应用 API 实现分片规定和数据源的配置,劣势在于更加灵便、可定制性强的特点,不便进行二次开发和扩大。

下边是纯 JAVA 编码方式实现分库分表的残缺代码。

@Configuration
public class ShardingConfiguration {

    /**
     * 配置分片数据源
     * 公众号:程序员小富
     */
    @Bean
    public DataSource getShardingDataSource() throws SQLException {Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("db0", dataSource1());
        dataSourceMap.put("db1", dataSource2());

        // 分片 rules 规定配置
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.setShardingAlgorithms(getShardingAlgorithms());

        // 配置 t_order 表分片规定
        ShardingTableRuleConfiguration orderTableRuleConfig = new ShardingTableRuleConfiguration("t_order", "db${0..1}.t_order_${0..2}");
        orderTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "table-inline"));
        orderTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "database-inline"));
        shardingRuleConfig.getTables().add(orderTableRuleConfig);

        // 是否在控制台输入解析革新后实在执行的 SQL
        Properties properties = new Properties();
        properties.setProperty("sql-show", "true");
        // 创立 ShardingSphere 数据源
        return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig), properties);
    }

    /**
     * 配置数据源 1
     * 公众号:程序员小富
     */
    public DataSource dataSource1() {HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/shardingsphere-db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    /**
     * 配置数据源 2
     * 公众号:程序员小富
     */
    public DataSource dataSource2() {HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/shardingsphere-db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    /**
     * 配置分片算法
     * 公众号:程序员小富
     */
    private Map<String, AlgorithmConfiguration> getShardingAlgorithms() {Map<String, AlgorithmConfiguration> shardingAlgorithms = new LinkedHashMap<>();

        // 自定义分库算法
        Properties databaseAlgorithms = new Properties();
        databaseAlgorithms.setProperty("algorithm-expression", "db$->{order_id % 2}");
        shardingAlgorithms.put("database-inline", new AlgorithmConfiguration("INLINE", databaseAlgorithms));

        // 自定义分表算法
        Properties tableAlgorithms = new Properties();
        tableAlgorithms.setProperty("algorithm-expression", "t_order_$->{order_id % 3}");
        shardingAlgorithms.put("table-inline", new AlgorithmConfiguration("INLINE", tableAlgorithms));

        return shardingAlgorithms;
    }
}

ShardingSphere 的分片外围配置类 ShardingRuleConfiguration,它次要用来加载分片规定、分片算法、主键生成规定、绑定表、播送表等外围配置。咱们将相干的配置信息 set 到配置类,并通过 createDataSource 创立并笼罩 DataSource,最初注入 Bean。

应用 Java 编码方式只是将 ShardingSphere 预知的加载配置逻辑本人手动实现了一遍,两种实现形式比拟下来,还是举荐应用 YML 配置形式来实现 ShardingSphere的分库分表性能,相比于 Java 编码,YML 配置更加直观和易于了解,开发者能够更加专一于业务逻辑的实现,而不须要过多关注底层技术细节。

@Getter
@Setter
public final class ShardingRuleConfiguration implements DatabaseRuleConfiguration, DistributedRuleConfiguration {
    
    // 分表配置配置
    private Collection<ShardingTableRuleConfiguration> tables = new LinkedList<>();
    // 主动分片规定配置
    private Collection<ShardingAutoTableRuleConfiguration> autoTables = new LinkedList<>();
    // 绑定表配置
    private Collection<String> bindingTableGroups = new LinkedList<>();
    // 播送表配置
    private Collection<String> broadcastTables = new LinkedList<>();
    // 默认的分库策略配置
    private ShardingStrategyConfiguration defaultDatabaseShardingStrategy;
    // 默认的分表策略配置
    private ShardingStrategyConfiguration defaultTableShardingStrategy;
    // 主键生成策略配置
    private KeyGenerateStrategyConfiguration defaultKeyGenerateStrategy;
    
    private ShardingAuditStrategyConfiguration defaultAuditStrategy;
    // 默认的分片键
    private String defaultShardingColumn;
    // 自定义的分片算法
    private Map<String, AlgorithmConfiguration> shardingAlgorithms = new LinkedHashMap<>();
    // 主键生成算法
    private Map<String, AlgorithmConfiguration> keyGenerators = new LinkedHashMap<>();
    
    private Map<String, AlgorithmConfiguration> auditors = new LinkedHashMap<>();}

通过查看控制台打印的实在 SQL 日志,发现在应用 ShardingSphere 进行数据插入时,其外部实现会先依据分片键 order_id 查问记录是否存在。如果记录不存在,则执行插入操作;如果记录已存在,则进行更新操作。看似只会执行 10 条插入 SQL,但实际上须要执行 20 条 SQL 语句,多少会对数据库的性能产生肯定的影响。

性能挺简略的,但因为 不同版本的 ShardingSphere 的 API 变动较大,网上相似的材料太不靠谱,原本想着借助 GPT 快点实现这段代码,后果差点和它干起来,最初还是扒了扒看了源码实现的。

默认数据源

可能有些小伙伴会有疑难,对于曾经设置了分片规定的 t_order 表能够失常操作数据,如果咱们的 t_user 表没有配置分库分表规定,那么在执行插入操作时会产生什么呢?

认真看了下官网的技术文档,其实曾经答复了小伙伴这个问题,如果只有局部数据库分库分表,是否须要将不分库分表的表也配置在分片规定中?官网答复:不须要

咱们创立一张 t_user 表,并且不对其进行任何分片规定的配置。在我的印象中没有通过设置 default-data-source-name 默认的数据源,操作未分片的表应该会报错的!

咱们向 t_user 尝试插入一条数据,后果竟然胜利了?翻了翻库表发现数据只被插在了 db1 库里,阐明没有走播送路由。

shardingsphere-jdbc 5.x 版本 移除了本来的默认数据源配置,主动应用了默认数据源的规定,为验证我多减少了数据源,尝试性的调整了 db2db0db1 的程序,再次插入数据,这回记录被插在了 db2 库,重复试验初步得出结论。

未分片的表默认会应用第一个数据源作为默认数据源,也就是 datasource.names 第一个。

spring:
  shardingsphere:
    # 数据源配置
    datasource:
      # 数据源名称,多数据源以逗号分隔
      names: db2 , db1 , db0

总结

本期咱们对 shardingsphere 做了简略的介绍,并应用 yml 和 Java 编码的形式疾速实现了分库分表性能,接下来会依照文首的思维导图的性能逐个实现。

下期文章将是《分库分表 ShardingSphere5.x 原理与实战》系列的第四篇,《分库分表默认分片策略、播送表、绑定表一网打尽》

本文案例 demo 地址

我是小富,下期见~

退出移动版