大家好,我是小富~

(一)好好的系统,为什么要分库分表?

(二)分库分表的 21 条法则,hold 住!

本文是《分库分表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 官网地址: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地址

我是小富,下期见~

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。