sharding-jdbc入门

概览

认识ShardingSphere

ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由ShardingJDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。
他们均提
供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容
器、云原生等各种多样化的应用场景。

官网地址: https://shardingsphere.apache.org/index_zh.html

ShardingSphere构成:
在这里插入图片描述

认识Sharding-JDBC

定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包
形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种
ORM框架。

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

在这里插入图片描述

功能列表

数据分片
分库 & 分表
读写分离
分布式主键
分布式事务(Doing)
XA强一致事务
柔性事务
数据库治理
配置动态化
熔断 & 禁用
调用链路追踪
弹性伸缩 (Planning)

数据分片工作原理

hardingSphere的3个产品的数据分片主要流程是完全一致的。 核心由 SQL解析 => 执行器优化
=> SQL路由 => SQL改写 => SQL执行 => 结果归并 的流程组成。
在这里插入图片描述

**SQL解析 **
分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语
法解析器对SQL进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、
分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。
**执行器优化 **
合并和优化分片条件,如OR等。

**SQL路由 **
根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。
**SQL改写 **
将SQL改写为在真实数据库中可以正确执行的语句。SQL改写分为正确性改写和优化改写。
**SQL执行 **
通过多线程执行器异步执行。
**结果归并 **
将多个执行结果集归并以便于通过统一的JDBC接口输出。结果归并包括流式归并、内存归并和
使用装饰者模式的追加归并这几种方式。

入门DEMO

多数据源入门

  1. pom配置
<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
<dependencies>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.0.0</version>
   </dependency>

   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>io.shardingsphere</groupId>
       <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
       <version>3.1.0</version>
   </dependency>
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.1.14</version>
   </dependency>
</dependencies>
  1. 多数据源配置
sharding:
  jdbc:
    datasource:
      names: ds0,ds1   配置多数据源 ds0,ds1 ds0 ds1是逻辑名称真正的数据库是url连接中的
      ds0: 
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
        maxPoolSize: 50
        minPoolSize: 1
      ds1: 
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
        maxPoolSize: 50
        minPoolSize: 1
    config:
      sharding:
       default-data-source-name: ds0 默认走ds0数据库
      props:
        sql.show: true
             
mybatis:
  configuration:
    map-underscore-to-camel-case: true
  1. dao代码,执行下面的addOrder()和get()默认会命中ds0-db1,因为配置了 default-data-source-name: ds0 默认走ds0数据库
@Mapper
public interface OrderDao {

   //@Insert("insert into t_order(order_time,customer_id) values(#{orderTime},#{customerId})")
   @Insert("insert into t_order(order_id,order_time,customer_id) values(#{orderId},now(),#{customerId})")
   void addOrder(Order o);
   
   @Select("select order_id,order_time,customer_id from t_order where order_id=#{id}")
   Order get(@Param("id")Long orderId);
}

读写分离

  1. pom文件和上面示例多数据源入门一致, 配置数据库的主从复制 可以参考MYSQL Master-Slaves主从复制.pdf或者其他帖子
  2. 主从复制数据源配置
sharding:
  jdbc:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://192.168.120.218:3306/orders?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: mike
        password: Mike666!
        maxPoolSize: 50
        minPoolSize: 1
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://192.168.120.219:3306/orders?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: mike
        password: Mike666!
        maxPoolSize: 50
        minPoolSize: 1
    config:
      sharding:
        default-data-source-name: ds0
      masterslave:  主从复制配置
        name: ms
        master-data-source-name: ds0  主数据源
        slave-data-source-names: ds1  从数据源可以多个
      props:
        sql.show: true

mybatis:
  configuration:
    map-underscore-to-camel-case: true
  1. dao代码 配置完读写分离,进行addOrder()会命中配置的master对应的dso,进行查询会命中配置的slave对应的ds1

@Mapper
public interface OrderDao {

//@Insert(“insert into t_order(order_time,customer_id) values(#{orderTime},#{customerId})”)
@Insert(“insert into t_order(order_id,order_time,customer_id) values(#{orderId},now(),#{customerId})”)
void addOrder(Order o);

@Select(“select order_id,order_time,customer_id from t_order where order_id=#{id}”)
Order get(@Param(“id”)Long orderId);
}

简单分库分表

  1. pom文件和上面示例多数据源入门一致
  2. 数据源分库分表配置
sharding:
  jdbc:
    datasource:
      names: ds0,ds1
      ds0: 
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
        maxPoolSize: 50
        minPoolSize: 1
      ds1: 
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
        maxPoolSize: 50
        minPoolSize: 1
    config:
      sharding:
        tables:
           t_order:
             actual-data-nodes: ds$->{0..1}.t_order$->{0..1}   库(ds0、ds1对应各自物理库)、表节点(t_order0、t_order1)
             database-strategy:
               inline:
                 sharding-column: customer_id
                 algorithm-expression: ds$->{customer_id % 2} 数据库选择根据customer_id字段去余,然后得到逻辑名称,根据名称映射对应库,如ds1对应db2
             table-strategy:
               inline:
                 sharding-column: order_id 在上面确定库的基础上,表选择根据order_id取余确定表
                 algorithm-expression: t_order$->{order_id % 2}
        default-data-source-name: ds0
      props:
        sql.show: true        
mybatis:
  configuration:
    map-underscore-to-camel-case: true
  1. dao层代码配置分库分表 请求传入参数orderId=10000&customerId=1003&orderTime=2020-09-19 ;addOrder()传入orderId=10000和customerId=1003 访问OrderDao类addOrder和get方法

@Mapper
public interface OrderDao {

1.根据分库分表路由配置,首先根据customerId%2=1,确定ds1,ds1对应db2,数据库确定之后根据表路由算法order_id % 2=0 所以最终命中

ds1.t_order0(db2.t_order0),sql打印如下

Actual SQL: ds1 ::: insert into t_order0(order_id,order_time,customer_id) values(?,?,?) ::: [[10000, 2020-09-19 00:00:00.0, 1003]]

@Insert(“insert into t_order(order_id,order_time,customer_id) values(#{orderId},now(),#{customerId})”)
void addOrder(Order o);

2.查询因为传入是orderId,库选择是根据customer_id路由的,没传入customer_id会去ds0和ds1都去查询,然后根据传入的orderId确定是t_order0查询,所以查询两次从ds0.t_order0查询一次、ds1.t_order0查询一次,sql打印如下

Actual SQL: ds0 ::: select order_id,order_time,customer_id from t_order0 where order_id=? ::: [[10000]]

Actual SQL: ds1 ::: select order_id,order_time,customer_id from t_order0 where order_id=? ::: [[10000]]

@Select(“select order_id,order_time,customer_id from t_order where order_id=#{id}”)
Order get(@Param(“id”)Long orderId);
}

standard 时间字段确定分片

  1. pom引入包和以上一致

  2. 数据源配置,根据order_time字段自定义,2019年前放入dbo,2019后放入db1

    sharding:
      jdbc:
        datasource:
          names: ds0,ds1
          ds0: 
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class: com.mysql.jdbc.Driver
            url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
            username: root
            password: root
            maxPoolSize: 50
            minPoolSize: 1
          ds1: 
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class: com.mysql.jdbc.Driver
            url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
            username: root
            password: root
            maxPoolSize: 50
            minPoolSize: 1
        config:
          sharding:
            tables:
               t_order:
                 actual-data-nodes: ds$->{0..1}.t_order$->{0..1} 
                 database-strategy:
                   standard:
                     sharding-column: order_time  分片库路由字段
                     preciseAlgorithmClassName: com.study.mike.sharding.jdbc.sharding.OrderTimePreciseShardingAlgorithm  重写PreciseShardingAlgorithm自定义路由策略,2019年前放入dbo,2019后放入db1
                 table-strategy:
                   inline:
                     sharding-column: order_id
                     algorithm-expression: t_order$->{order_id % 2}
                 #keyGeneratorClassName: io.shardingsphere.core.keygen.DefaultKeyGenerator
            default-data-source-name: ds0
          props:
            sql.show: true
            
            
    mybatis:
      configuration:
        map-underscore-to-camel-case: true
    

3.dao层代码 传入参数orderId=10001&customerId=1004&orderTime=2018-09-19

@Mapper
public interface OrderDao {

1.根据分库分表路由配置,首先根据orderTime大于2019年路由ds0,否则ds1,因为传入2018年所以命中ds0,数据库确定之后根据表路由算法order_id % 2=01所以最终命中

ds1.t_order1(db2.t_order1),sql打印如下

Actual SQL: ds0 ::: insert into t_order1(order_id,order_time,customer_id) values(?,?,?) ::: [[10001, 2018-09-19 00:00:00.0, 1004]]

@Insert(“insert into t_order(order_id,order_time,customer_id) values(#{orderId},now(),#{customerId})”)
void addOrder(Order o);

2.get 方法和简单分库分表一致 因为没有传入分库字段 所以会两个库都会查询,查询的表根据orderId%2=1确定为t_order1

@Select(“select order_id,order_time,customer_id from t_order where order_id=#{id}”)
Order get(@Param(“id”)Long orderId);
}

4.OrderTimePreciseShardingAlgorithm

public class OrderTimePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Date>{
   
   Date[] dateRanges = new Date[2];
   
   {
      Calendar cal = Calendar.getInstance();
      cal.set(2019, 1, 1, 0, 0, 0);
      dateRanges[0] = cal.getTime();
      cal.set(2020, 1, 1, 0, 0, 0);
      dateRanges[1] = cal.getTime();
   }

   @Override
   public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
      
      Date d = shardingValue.getValue();
      Iterator<String> ite = availableTargetNames.iterator();
      String target = null;
      for(Date s : dateRanges) {
         target = ite.next();
         if(d.before(s)) {
            break;
         }
      }
      return target;
   }

}

全局唯一主键

1.pom包引入和上面一致

2.数据源配置,配置keyGeneratorColumnName、 key-generator-column-name、keyGeneratorClassName三个字段,默认是雪花算法

sharding:
  jdbc:
    datasource:
      names: ds0,ds1
      ds0: 
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
        maxPoolSize: 50
        minPoolSize: 1
      ds1: 
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
        maxPoolSize: 50
        minPoolSize: 1
    config:
      sharding:
        tables:
           t_order:
             actual-data-nodes: ds$->{0..1}.t_order$->{0..1}
             database-strategy:
               standard:
                 sharding-column: order_time
                 preciseAlgorithmClassName: com.study.mike.sharding.jdbc.sharding.OrderTimePreciseShardingAlgorithm
             table-strategy:
               inline:
                 sharding-column: order_id
                 algorithm-expression: t_order$->{order_id % 2}
             keyGeneratorColumnName: order_id  order_id当做自增主键,默认是雪花算法
             key-generator-column-name: order_id
             keyGeneratorClassName: io.shardingsphere.core.keygen.DefaultKeyGenerator
        default-data-source-name: ds0
          #type: SNOWFLAKE
      props:
        sql.show: true
        
        
mybatis:
  configuration:
    map-underscore-to-camel-case: true

3.dao 插入数据库 orderid不用传入,由shardingjdbc生成

@Insert("insert into t_order(order_time,customer_id) values(#{orderTime},#{customerId})")
void addOrder(Order o);

绑定表

指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片(路由策略一致),则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。使用bindingTables进行绑定。

在这里插入图片描述

绑定示例:

在这里插入图片描述

广播表

指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,如字典表。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介 随着互联网的发展,软件的规模在逐渐变大,用关系型数据库如何存储和处理大规模的业务数据成为企业面临的挑战, 关系型数据库作为OLTP(联机事务处理过程)系统的首选毋庸置疑,但是关系型数据面对大规模数据的处理有其先天的不足,比如单表存储上千万数据时便会出现不同程度的处理速度缓慢问题,如何解决?分库分表技术就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。本课程将系统的讲解分库分表技术。 课程价值 分库分表技术是为解决关系型数据库存储和处理大规模数据的问题,主要应用于OLTP系统,它与应用于OLAP(联机分析处理)的大数据技术有不同的应用场景,本课程本着从解决生产实际问题出发,讲授分库分表技术的解决方案,包括:垂直分库、垂直分表、水平分库、水平分表、读写分离,涵盖了分库分表的各种方案,并且深入讲解Sharding-JDBC框架的原理及使用方法,通过学习本课程可以快速应用到生产实践中。 课程优势 本课程不仅讲解多种有效的分库分表的解决方案,还深入讲解了Sharding-JDBC框架的原理和使用方法,Sharding-JDBC是一套轻量级的对代码零侵入的框架,在生产中有广泛的使用。本课程从思想原理、技术框架、案例实操三个方面去学习,可以快速的将分库分表技术应用到生产实践中,解决大数据存储与处理的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值