ShardingSphere-JDBC原理及分库分表实战

目录

简介

特点

架构实现原理

主要功能点

主要核心概念

实战

集成ShardingSphere-JDBC

自定义分片策略

分库不分表

分表不分库

分表分库

主从及读写分离

与Mycat对比

总结


简介

Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

ShardingSphere-JDBC属于ShardingSphere其中一核心组件。定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。采用无中心化架构,适用于 Java 开发的高性能的轻量级 OLTP 应用,它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

特点

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, JDBC。
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。

架构实现原理

image

 

以查询语句以例:

select * from order where id=100000;
  1. 分片之前要配置好分片的规则:主库ds0,从库ds1,以id为分片键,逻辑表为order, 真实表为order0、order1两张表,分库、分表算法为均为取余(%2)。
  2. SQL解析:解析sql语句提取分片键列与值进行分片,例如比较符 =、in 、between and,及查询的表等。
  3. SQL改写:根据解析结果,及采用的分片逻辑改写sql,上例经过sql改写后,真实语句为:
select * from order0 where id=100000;
  1. SQL路由:找到sql需要去哪个库、哪个表执行语句,上例sql根据采用的策略可以得到将在ds0库的order0表执行语句。
  2. sql执行:执行改写后的sql。
  3. 结果归并:当我们执行某些复杂语句时,sql可能会在多个库、多个表中执行,sql分别对应执行后会对结果集进行归并操作,得到最终的结果。

主要功能点

  • 数据分片。包括:读写分离,分库分表,分片策略定制化,无中心化分布式主键
  • 分布式事务。提供:标准化事务接口,XA强一致事务,柔性事务

主要核心概念

  • 逻辑表(LogicTable):水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为10张表,分别是order0到order9,他们的逻辑表名为order。
  • 真实表(ActualTable):在分片的数据库中真实存在的物理表。即上个示例中的order0到order9。
  • 数据节点(DataNode):数据分片的最小单元。由数据源名称和数据表组成,例:ds0.order0。
  • 绑定表(BindingTable):指分片规则一致的主表和子表。例如:order表和order_item表,均按照order_id分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。举例说明,如果SQL为:
SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

在不配置绑定表关系时,假设分片键order_id将数值10路由至第0片,将数值11路由至第1片,那么路由后的SQL应该为4条,它们呈现为笛卡尔积:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

在配置绑定表关系后,路由的SQL应该为2条:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

其中t_order在FROM的最左侧,ShardingSphere将会以它作为整个绑定表的主表。 所有路由计算将会只使用主表的策略,那么t_order_item表的分片计算将会使用t_order的条件。故绑定表之间的分区键要完全相同。

  • 分片键(ShardingColumn):用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL 中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,Apache ShardingSphere 也支持根据多个字段进行分片
  • 分片算法(ShardingAlgorithm):目前提供4种分片算法。由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。
1、精确分片算法
对应PreciseShardingAlgorithm,必选,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
2、范围分片算法
对应RangeShardingAlgorithm,可选,用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。需要配合StandardShardingStrategy使用。
3、复合分片算法
对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
4、Hint分片算法
对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。
  • 分片策略(ShardingStrategy):包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供5种分片策略。
1、标准分片策略
对应StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
2、复合分片策略
对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
3、行表达式分片策略
对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
4、Hint分片策略
对应HintShardingStrategy。通过Hint而非SQL解析的方式分片的策略。
5、不分片策略
对应NoneShardingStrategy。不分片的策略。

实战

源码请参考个人github:https://github.com/huhua1990/shardingsphere.git

集成ShardingSphere-JDBC

  • 配置pom
<properties>
        <java.version>1.8</java.version>
        <druid.version>1.2.3</druid.version>
        <mysql.version>8.0.22</mysql.version>
        <hutool.version>5.5.2</hutool.version>
        <lombok.version>1.18.16</lombok.version>
        <slf4j.version>1.7.30</slf4j.version>

        <mybatis.version>3.5.6</mybatis.version>
        <mybatis-spring.version>2.1.3</mybatis-spring.version>
        <mybatis-plus.version>3.4.1</mybatis-plus.version>
        <pagehelper.vserion>1.2.13</pagehelper.vserion>
        <sharding-jdbc.version>4.1.1</sharding-jdbc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--  mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!-- 使用ali的druid数据源 or  默认为springboot自带hikari-->
        <!-- 不能使用druid-spring-boot-starter,会导致:Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required -->
        <!--<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>


        <!-- 省略get/set -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <!--日志-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
            <scope>compile</scope>
        </dependency>

        <!--  工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>


        <!-- mybatis 与mybatis-plus 可只选择一个 or 使用spring boot jpa -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring.version}</version>
        </dependency>

        <!--<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>-->

        <!--分页 也可直接配置使用mybatis-plus中插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>${pagehelper.vserion}</version>
        </dependency>


        <!-- 分表分库 ShardingShpere -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>${sharding-jdbc.version}</version>
        </dependency>

        <!--<dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>${sharding-jdbc.version}</version>
        </dependency>-->


        <!-- end  -->
        <!--<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>

    </dependencies>
  • 启动
//排除默认数据源自动装配
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ShardingsphereApplication {

    public static void main(String[] args) {
        System.out.println("格式" + System.getProperty("file.encoding"));
        SpringApplication.run(ShardingsphereApplication.class, args);
    }

}

自定义分片策略

  • hash 取余(0-9)。优点:均匀;缺点:不好扩展
  • 时间或数据(0-1000,1000-2000...)优点:好扩展 ;缺点:有数据热点问题
  • 自定义路由表ROUTE 优点:灵活,可定制为一个表的策略 缺点:多一次查询
  • 在多表关联查询时可以配置绑定表关系:spring.shardingsphere.sharding.binding-tables[0] = t_order,t_order_item,来减少笛卡尔积
/**
 * @Title: OrderDataBasesPreciseShardingAlgorithm
 * @Description: 分表算法:精确分片算法,适应于=和IN为例
 * @author: huhua
 * @date: 2021/3/13 18:04
 */

public class OrderDataBasesPreciseShardingAlgorithm implements PreciseShardingAlgorithm<String> {

    /**
     *
     * @param collection  库名集合
     * @param preciseShardingValue 分片列
     * @return
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
        // 分片字段值
        String value = preciseShardingValue.getValue();
        // 自定义算法:
        // 取末位%2求余: 如果是0则order0,如果是1则order1。
        value = value.substring(value.length()-1,value.length());
        int result = Integer.valueOf(value) % 2;
        for (String s : collection) {
            if(s.endsWith(result+"")){
                return s;
            }
        }
        throw new UnsupportedOperationException();
    }

}

分库不分表

#配置分库不分表
spring:
  shardingsphere:
    props:
      sql.show: true # 开启SQL显示,默认值: false
      executor.size: 2 #工作线程数量,默认值: CPU核数
    #配置数据源
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3308/ds0
        username: root
        password: root
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3308/ds1
        username: root
        password: root

    sharding:
      tables:
        order:
          actual-data-nodes: ds$->{0..1}.order
          databse-strategy:
            #配置order表分库策略(inline-基于行表达式的分片算法)
            inline:
              sharding-column: order_id
              algorithm-expression: ds$->{order_id % 2}
            #配置order表分库策略(standard-基于的标准分片算法)
#            standard:
#              sharding-column: order_id
#              precise-algorithm-class-name: com.cp3.shardingsphere.utils.OrderDataBasesPreciseShardingAlgorithm


分表不分库

#配置分表不分库
spring:
  shardingsphere:
    props:
      sql.show: true # 开启SQL显示,默认值: false
      executor.size: 2 #工作线程数量,默认值: CPU核数
    #配置数据源
    datasource:
      names: ds0
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3308/ds0
        username: root
        password: root

    sharding:
      tables:
        order:
          actual-data-nodes: ds0.order$->{0..1}
          #指定主键生成策略为 SNOWFLAKE
#          key-generator:
#            column: order_id
#            type: SNOWFLAKE
#            props:
#              worker:
#                id: 123
          table-strategy:
            ##配置order表分库策略(inline-基于行表达式的分片算法)
#            inline:
#              sharding-column: order_id
#              algorithm-expression: order$->{order_id % 2}
            # #配置order表分库策略(standard-基于的标准分片算法)
            standard:
              sharding-column: order_id
              precise-algorithm-class-name: com.cp3.shardingsphere.utils.OrderTablesPreciseShardingAlgorithm

分表分库

#配置分库分表
spring:
  shardingsphere:
    props:
      sql.show: true # 开启SQL显示,默认值: false
      executor.size: 2 #工作线程数量,默认值: CPU核数
    #配置数据源
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3308/ds0
        username: root
        password: root
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3308/ds1
        username: root
        password: root

    sharding:
      tables:
        order:
          actual-data-nodes: ds$->{0..1}.order$->{0..1}
          databse-strategy:
            #配置order表分库策略(inline-基于行表达式的分片算法)
#            inline:
#              sharding-column: order_id
#              algorithm-expression: ds$->{order_id % 2}
            #配置order表分库策略(standard-基于的标准分片算法)
            standard:
              sharding-column: order_id
              precise-algorithm-class-name: com.cp3.shardingsphere.utils.OrderDataBasesPreciseShardingAlgorithm
          table-strategy:
#            #配置order表分库策略(inline-基于行表达式的分片算法)
#            inline:
#              sharding-column: order_no
#              algorithm-expression: order$->{order_no % 2}
            #配置order表分库策略(standard-基于的标准分片算法)
            standard:
              sharding-column: order_no
              precise-algorithm-class-name: com.cp3.shardingsphere.utils.OrderTablesPreciseShardingAlgorithm

主从及读写分离

主库m0,从库为s0,s1。库与库之间通过修改mysql.cnf文件配置主从关系。

#配置分库分表
spring:
  shardingsphere:
    props:
      sql.show: true # 开启SQL显示,默认值: false
      executor.size: 2 #工作线程数量,默认值: CPU核数
    #多从库配置负载均衡
    masterslave:
      load-balance-algorithm-type: ROUND_ROBIN
    #配置数据源
    datasource:
      #定义主从,通过修改mysql.cnf文件配置主从m0-s1关系
      names: m0,s0,s1
      m0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3309/m0
        username: root
        password: root
      s0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3310/s0
        username: root
        password: root
      s1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.45.50.241:3311/s1
        username: root
        password: root

    sharding:
      tables:
        order:
          #分表策略 如需多个主库时,可增加主表的分库逻辑
          actual-data-nodes: m0.order
      master-slave-rules:
        m0:
          master-data-source-name: m0
          slave-data-source-name: s0,s1

与Mycat对比

 

Sharding-JDBC

Mycat

性能

限制

SQL支持有限制,详情可见官方文档

SQL支持有限制,详情可见官方文档

工作层面

重写JDBC层逻辑,实现了JDBC协议

Proxy中间件层,可当做一个mysql数据库使用,

集成方式

工程中导入sharding-jdbc-spring-boot-starter的jar包,然后在配置文件中配置相应的数据源和自定义分片策略即可

需要单独部署Mycat服务,然后配置相关的数据源和分片策略

开发运维方式

Sharding-JDBC的运维成本低,与业务业务绑定,需要在业务逻辑层处理

Mycat运维成本高,得配置Mycat的一系列参数以及高可用负载均衡的配置,需要一定的运维实力

推荐使用

需要使用的分片策略和算法不是非常复杂,JAVA储备足

分片规则比较复杂,且具有一定的运维储备

总结

ShardingSphere可以学习和注意的知识点很多,这边就是举几个简单的例子。象SQL支持,事务支持,分布式治理、弹性伸缩等都是可以学习使用的。

 

参考:https://shardingsphere.apache.org/document/current/cn/overview/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值