使用数据库中间件ShardingSphere(JDBC&Proxy)分库分表

1,465 阅读19分钟

“这是我参与更文挑战的第7天,活动详情查看: 更文挑战

什么是ShardingSphere?

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

Apache ShardingSphere 定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 它通过关注不变,进而抓住事物本质。关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆。

ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目

官网:shardingsphere.apache.org/index_zh.ht…

Sharding-JDBC简介

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

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

Sharding-Proxy简介

定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前先提供MySQL/PostgreSQL版本,它可以使用任何兼容MySQL/PostgreSQL协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat等)操作数据,对DBA更加友好。

  • 向应用程序完全透明,可直接当做MySQL/PostgreSQL使用。
  • 适用于任何兼容MySQL/PostgreSQL协议的的客户端。

由此可见,使用了Sharding-Proxy之后,我们的操作都是去操作Sharding-Proxy不需要去操作MySQL,相当于我们发送SQL操作,让这个代理去操作数据库。

Sharding-Sidecar简介(开发中)

定位为Kubernetes的云原生数据库代理,以Sidecar的形式代理所有对数据库的访问。 通过无中心、零侵入的方案提供与数据库交互的的啮合层,即Database Mesh,又可称数据网格。

Database Mesh的关注重点在于如何将分布式的数据访问应用与数据库有机串联起来,它更加关注的是交互,是将杂乱无章的应用与数据库之间的交互有效的梳理。使用Database Mesh,访问数据库的应用和数据库终将形成一个巨大的网格体系,应用和数据库只需在网格体系中对号入座即可,它们都是被啮合层所治理的对象。

Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar的对比

Sharding-JDBCSharding-ProxySharding-Sidecar
数据库任意MySQLMySQL
连接消耗数
异构语言仅Java任意任意
性能损耗低损耗略高损耗低
无中心化
静态入口

混合搭配使用

Sharding-JDBC采用无中心化架构,适用于Java开发的高性能的轻量级OLTP应用;Sharding-Proxy提供静态入口以及异构语言的支持,适用于OLAP应用以及对分片数据库进行管理和运维的场景。

ShardingSphere是多接入端共同组成的生态圈。 通过混合使用Sharding-JDBC和Sharding-Proxy,并采用同一注册中心统一配置分片策略,能够灵活的搭建适用于各种场景的应用系统,架构师可以更加自由的调整适合于当前业务的最佳系统架构。

分库分表

数据库中的数据量不一定是可控的,在未进行分库分表的情况下,随着时间和业务的发展,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销 也会越来越大;另外由于无法进行分布式式部署,而一台服务器的资源(CPU、磁盘、内 存、IO 等)是有限的,最终数据库所能承载的数据量、数据处理能力都将遭遇瓶颈。

分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成,将数据大表拆分成若干数据表组成,使得单一数据库、单一数 据表的数据量变小,从而达到提升数据库性能的目的。

数据库的切分指的是通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)中,以达到分散单台设备负载的效果,即分库分表。数据的切分根据其切分规则的类型,可以分为 垂直切分 和水平切分。

垂直切分

垂直切分: 把单一的表拆分成多个表,并分散到不同的数据库(主机)上。

一个数据库有多个表构成,每个表对应不同的业务,垂直切分是只按照业务将表进行分类,

将其分布到不同的数据库上,这样就将数据分担到了不同的库上(专库专用)。

例如:

有如下几张表:

  • 用户信息表(User)
  • 课程信息表(Courses )
  • 订单信息表(Orders )
  • 针对以上案例,垂直切分就是根据每个表的不同业务进行切分。
  • 比如 User 表,Courses 表和 Orders 表,将每个表切分到不同的数据库上。

垂直切分的优点:

(1)拆分后业务清晰,系统之间进行整合或扩展很容易。

(2)按照成本、应用的等级、应用的类型等奖表放到不同的机器上,便于管理,数据维护简单。

垂直切分的缺点:

(1)部分业务表无法关联(Join), 只能通过接口方式解决,提高了系统的复杂度。

(2)受每种业务的不同限制,存在单库性能瓶颈,不易进行数据扩展和提升性能。

(3)事务处理变得复杂。

垂直分表:

例如:课程表中含有多个字段,我们可以通过垂直分表,把一些属性进行拆分。这样假如对“课程名称”进行修改,只会锁定“课程基本信息表”,同时对于一个表的访问变成了两个表的访问,业务复杂度也会相对上升。

垂直分库:

根据不同的业务,专门建立一个数据库来存储业务相关的表。例如在”商品表“专门建立一个”商品数据库“,”订单表“建立一个“订单数据库”,分库之后,同时也可以对商品表进行分表的。

水平切分

水平切分:根据表中数据的逻辑关系,将表中的数据按照某种条件拆分到多台数据库上。

与垂直切分对比,水平切分不是将表进行分类,而是将其按照某个字段的某种规则分散到多个库中,在每个表中包含一部分数据,所有表加起来就是全量的数据。

简单来说,我们可以将对数据的水平切分理解为按照数据行进行切分,就是将表中的某些行切分到一个数据库表中,而将其他行切分到其他数据库表中。这种切分方式根据单表的数据量的规模来切分,保证单表的容量不会太大,从而保证了单表的查询等处理能力

例如将用户的信息表拆分成 User1、User2 等,表结构是完全一样的。我们通常根据某些特定的规则来划分表,比如根据用户的 ID 来取模划分。

举例:

在博客类系统中,读取量一般都会很大。当同时有 100 万个用户在浏览时,如果是单表,则单表会进行 100 万次请求,如果是单库,数据库就会承受 100 万次的请求压力。 如果采取水平切分来减少每个单表的压力,将其分为 100 个表,并且分布在 10 个数据库 中,每个表进行 1 万次请求,则每个数据库会承受 10 万次的请求压力,虽然这不可能绝对平均,但是这样,压力就减少了很多,并且是成倍减少的。

水平切分的优点:

(1)单库单表的数据保持在一定的量级,有助于性能的提高。

(2)切分的表的结构相同,应用层改造较少,只需要增加路由规则即可。

(3)提高了系统的稳定性和负载能力。

水平切分的缺点:

(1)切分后,数据是分散的,很难利用数据库的Join 操作,跨库 Join 性能较差。

(2)分片事务的一致性难以解决,数据扩容的难度和维护量极大。

水平分表:

水平分库:

分库分表的应用和问题

应用:

  • 在数据设计时可以考虑垂直分库和垂直分表
  • 随着数据库数据量的增加,不要马上考虑水平切分,首先考虑使用如引入缓存处理、读写分离、使用数据库索引优化等方式,如果这些方式都不能根本解决问题,再考虑水平分库和水平分表。

问题:

  • 跨界点连接查询(join查询)问题
  • 多数据源管理问题

环境搭建

1、环境:SpringBoot2.2.2+MyBatis-Plus+Sharding-JDBC+Druid

创建父工程,Maven依赖:

<groupId>com.msr</groupId>
    <artifactId>ShardingSphere-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Sharding-jdbc-demo</module>
        <module>Sharding-Proxy-demo</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Sharding-jdbc-demo的maven依赖:

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/sharding-jdbc-core -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-namespace</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2、按照水平分表的方式,创建数据库和数据库表

(1)创建数据库course_db

(2)在数据库创建两张表 course_1 和 course_2

(3)约定规则:如果添加课程id 是偶数把数据添加course_1,如果奇数添加到course_2

CREATE DATABASE test;
USE test;
DROP TABLE IF EXISTS `course_1`;
CREATE TABLE `course_1`  (
  `course_id` bigint(20) NOT NULL,
  `course_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `course_status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`course_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

DROP TABLE IF EXISTS `course_2`;
CREATE TABLE `course_2`  (
  `course_id` bigint(20) NOT NULL,
  `course_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `course_status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`course_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

Sharding-JDBC实现水平分表

1.编写application.yml配置文件

server:
  port: 8080
spring:
  application:
    name: Sharding-Jdbc-demo
  shardingsphere:
    datasource:
      #配置数据源的名称
      names: ds0
      #配置每个数据源
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
        username: root
        password: XXX
    sharding:
      #表的分布情况
      tables:
        course: # course表
          actual-data-nodes: ds0.course_$->{1..2} #表达式,ds0中的course_1和course_2
          key-generator:
            column: course_id #主键生成的例
            type: SNOWFLAKE #使用雪花算法生成主键
          #数据表分片策略:course_id为偶数时添加进course1中,奇数添加进course2中
          table-strategy:
            inline:
              sharding-column: course_id
              algorithm-expression: course_$->{course_id % 2+1}
    props:
      sql:
        show: true

  main:
    allow-bean-definition-overriding: true
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:/mapper/*.xml

2.简单编写mapper、controller、ShardingJdbcApplication启动类。

@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}


@RestController
@RequestMapping("test")
public class TestController {

    @Autowired
    private CourseMapper courseMapper;


    @PostMapping("add")
    public String add(@RequestBody Course course) {
        int insert = courseMapper.insert(course);
        return insert == 0 ? "添加失败" : "添加成功";
    }
    
    @GetMapping("query/{id}")
    public Course queryById(@PathVariable Long id) {
        return courseMapper.selectById(id);
    }
}

@SpringBootApplication
@MapperScan(basePackages = "com.msr.shardingjdbc.mapper")
public class ShardingJdbcApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShardingJdbcApplication.class, args);
    }
}

3.测试

使用PostMan调用add方法,添加成功之后,查看控制台输出的日志,course_id为奇数。保存在course2表中

INFO 2700 --- [nio-8080-exec-1] ShardingSphere-SQL                       : Actual SQL: ds0 ::: INSERT INTO course_2   (course_name, user_id, course_status, course_id) VALUES (?, ?, ?, ?) ::: [docker入门到实践, 100, 1, 483626785309720577]

使用“483626785309720577”再去调用queryById方法,查看日志输出,确实是去course2表中查询

INFO 10220 --- [nio-8080-exec-1] ShardingSphere-SQL                       : Actual SQL: ds0 ::: SELECT course_id,course_name,user_id,course_status FROM course_2 WHERE course_id=? ::: [483626785309720577]

Sharding-JDBC实现水平分库

1.修改配置文件

server:
  port: 8080
spring:
  application:
    name: Sharding-Jdbc-demo
  shardingsphere:
    datasource:
      #配置数据源的名称
      names: ds0,ds1
      #配置每个数据源
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
        username: root
        password: 123456
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8
        username: root
        password: 123456
    sharding:
      #表的分布情况
      tables:
        course: # course表
          actual-data-nodes: ds$->{1..2}.course_$->{1..2} #表达式,ds0,ds1中的course_1和course_2
          key-generator:
            column: course_id #主键生成的例
            type: SNOWFLAKE #使用雪花算法生成主键
          #数据表分片策略:course_id为偶数时添加进course1中,奇数添加进course2中
          table-strategy:
            inline:
              sharding-column: course_id
              algorithm-expression: course_$->{course_id % 2+1}
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: ds$->{user_id % 2+1}
    props:
      sql:
        show: true

  main:
    allow-bean-definition-overriding: true
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:/mapper/*.xml

2.水平分库的策略

根据user_id去分库,当user_id为偶数时,数据添加进数据源ds1中,奇数则是路由到数据源ds2

3.测试

启动的时候,通过日志可以看出,两个数据源都已经完成初始化。

INFO 14796 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
INFO 14796 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited

使用PostMan调用add方法,添加成功之后,查看控制台输出的日志。course_id=1276767761787297794,user_id=10。课程id为偶数,user_id也为偶数。所以数据会被保存在数据源ds1中的course1表中。对比日志,说明水平分库分表的策略是成功的。

INFO 8684 --- [nio-8080-exec-1] ShardingSphere-SQL                       : Actual SQL: ds1 ::: INSERT INTO course_1   (course_id, course_name, user_id, course_status) VALUES (?, ?, ?, ?) ::: [1276767761787297794, docker入门到实践, 10, 1]

然后试一下查询,使用user_id和course_id作用查询条件。controller新增一个方法

@GetMapping("query/{uid}/{cid}")
public Course query(@PathVariable("uid") Long uid, @PathVariable("cid") Long cid) {
	QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda()
    			.eq(uid != null, Course::getUserId, uid)
                .eq(cid != null, Course::getCourseId, cid);java
    return courseMapper.selectOne(queryWrapper);
}

使用PostMan调用(uid=10,cid=1276767761787297794)查看结果:sql查询确实是会路由到ds1下的course1表

[nio-8080-exec-2] ShardingSphere-SQL                       : Actual SQL: ds1 ::: SELECT  course_id,course_name,user_id,course_status  FROM course_1 WHERE  user_id = ? AND course_id = ? ::: [10, 1276767761787297794]

Sharding-JDBC实现垂直切分

通过Sharding-JDBC进行垂直分库,实现专库专表。

1.新建两个数据库和两张表

CREATE DATABASE production_db;
USE production_db;
DROP TABLE IF EXISTS `t_production`;
CREATE TABLE `t_production`  (
  `pid` bigint(50) NOT NULL,
  `pname` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `price` int(20) NOT NULL,
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

CREATE DATABASE order_db;
USE order_db;
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`  (
  `order_id` bigint(50) NOT NULL,
  `order_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `create_time` TIMESTAMP NOT NULL,
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

2.编写相关的实体类,Mapper、测试的Controller

@Data
@TableName("t_order")
public class Order {

    @TableId
    private Long orderId;
    private String orderName;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}

@Data
@TableName("t_production")
public class Production {

    @TableId
    private Long pid;
    private String pname;
    private Integer price;
}

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

@Mapper
public interface ProductionMapper extends BaseMapper<Production> {
}


@RestController
@RequestMapping("test")
public class TestController {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ProductionMapper productionMapper;


    @PostMapping("add2")
    public String add2(@RequestBody Order order) {
        order.setCreateTime(new Date());
        int insert = orderMapper.insert(order);
        return insert == 0 ? "添加失败" : "添加成功";
    }

    @PostMapping("add3")
    public String add3(@RequestBody Production production) {
        int insert = productionMapper.insert(production);
        return insert == 0 ? "添加失败" : "添加成功";
    }
}

3.yml配置

server:
  port: 8080
spring:
  application:
    name: Sharding-Jdbc-demo
  shardingsphere:
    datasource:
      #配置数据源的名称
      names: ds1,ds2,ds3,ds4
      #配置每个数据源
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
        username: root
        password: 123456
      ds2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8
        username: root
        password: m123456
	#垂直分库的数据源
      ds3:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/production_db?serverTimezone=GMT%2B8
        username: root
        password: 123456
      ds4:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/order_db?serverTimezone=GMT%2B8
        username: root
        password: 123456

    sharding:
      #表的分布情况
      tables:
        course: # course表
          actual-data-nodes: ds$->{1..2}.course_$->{1..2} #表达式,ds0,ds1中的course_1和course_2
          key-generator:
            column: course_id #主键生成的例
            type: SNOWFLAKE #使用雪花算法生成主键
          #数据表分片策略:course_id为偶数时添加进course1中,奇数添加进course2中
          table-strategy:
            inline:
              sharding-column: course_id
              algorithm-expression: course_$->{course_id % 2+1}
          #数据库分片策略
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: ds$->{user_id % 2+1}
		#专库
        production:
          actual-data-nodes: ds$->{3}.t_production
          key-generator:
            column: pid #主键生成的例
            type: SNOWFLAKE #使用雪花算法生成主键
          table-strategy:
            inline:
              sharding-column: pid
              algorithm-expression: t_production
        order:
          actual-data-nodes: ds$->{4}.t_order
          key-generator:
            column: order_id #主键生成的例
            type: SNOWFLAKE #使用雪花算法生成主键
          table-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: t_order

    props:
      sql:
        show: true

  main:
    allow-bean-definition-overriding: true
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:/mapper/*.xml

4.测试

启动,四个数据源都已经初始化了

INFO 1240 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
INFO 1240 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
INFO 1240 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-3} inited
INFO 1240 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-4} inited

通过PostMan调用controller方法进行测试,sql日志如下所示

INFO 13384 --- [nio-8080-exec-2] ShardingSphere-SQL                       : Actual SQL: ds1 ::: INSERT INTO t_production   (pid, pname, price) VALUES (?, ?, ?) ::: [1277152899687895042, 小米10pro, 2999]

INFO 13384 --- [nio-8080-exec-5] ShardingSphere-SQL                       : Actual SQL: ds2 ::: INSERT INTO t_order   (order_id, order_name, create_time) VALUES (?, ?, ?) ::: [1277153290127265794, 小米10pro-24期免息, 2020-06-28 16:14:07.384]

Sharding-JDBC实现读写分离

配置MySQL主从复制(一主一从)

在TestController新增一个查询方法

@GetMapping("order/{id}")
public Order getOrder(@PathVariable("id")Long id){
	return orderMapper.selectById(id);
}

yml配置文件:

server:
  port: 8080
spring:
  application:
    name: Sharding-Jdbc-demo
  shardingsphere:
    datasource:
      names: m0,s0
# 主服务器数据源
    m0:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.74.129:3306/test?serverTimezone=GMT%2B8
      username: root
      password: 123456
#从服务器数据源
    s0:
	  type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.74.130:3306/test?serverTimezone=GMT%2B8
      username: root
      password: 123456
	masterslave:
	  name: ms
#主库和从库逻辑数据源定义:ds0
    sharding:
      master-slave-rules: 
        ds0: 
          master-data-source-name: m0
      master-slave-rules:
        ds0:
          slave-data-source-names: s0

# t_user 分表策略,固定分配至ds0 的t_user 真实表
    tables:
       t_user:
          actual-data-nodes: ds0.t_order


    props:
      sql: 
        show: true

  main:
    allow-bean-definition-overriding: true

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

启动工程,可以看到m0和s0两个数据源初始化完毕

INFO 2616 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
INFO 2616 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited

使用PostMan调用方法add2,执行写操作是在m0即主库中操作的

INFO 2616 --- [nio-8080-exec-1] ShardingSphere-SQL                       : Actual SQL: m0 ::: INSERT INTO t_order   (order_id, order_name, create_time) VALUES (?, ?, ?) ::: [1277540210028154881, 红米K30-24期免息, 2020-06-29 17:51:36.081]

调用getOrder方法时,执行读操作是在s0即从库中操作

INFO 5188 --- [nio-8080-exec-4] ShardingSphere-SQL                       : Actual SQL: s0 ::: SELECT order_id,order_name,create_time FROM t_order WHERE order_id=? ::: [1277540210028154881]

因为MySQL配置是一主一从,在配置了一主多从的时候,只需要把数据源都配置之后,然后加入一下配置

spring.shardingsphere.masterslave.name=ms
#负载均衡策略
spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0,slave1

Sharding-Proxy的使用

下载安装:官网下载地址:4.x版本,下载压缩包解压缩使用就行。

使用Docker安装,直接使用官方的镜像即可

docker pull apache/sharding-proxy

运行镜像生成容器,必须挂载配置路径到/opt/sharding-proxy/conf,在/opt/software/sharding/lib存放一个MySQL的连接驱动,Sharding-Proxy连接MySQL时用到。

docker run --name sharding-proxy -di -v /opt/software/sharding/conf:/opt/sharding-proxy/conf -v /opt/software/sharding/lib:/opt/sharding-proxy/ext-lib -e PORT=3308 -p13308:3308 apache/sharding-proxy:latest

修改conf里的配置文件

server.yml

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

######################################################################################################
# 
# If you want to configure orchestration, authorization and proxy properties, please refer to this file.
# 
######################################################################################################
#
# orchestration:
#  orchestration_ds:
#    orchestrationType: registry_center,config_center,distributed_lock_manager
#    instanceType: zookeeper
#    serverLists: localhost:2181
#    namespace: orchestration
#    props:
#      overwrite: false
#      retryIntervalMilliseconds: 500
#      timeToLiveSeconds: 60
#      maxRetries: 3
#      operationTimeoutMilliseconds: 500

authentication:
 users:
   root:
     password: root
   sharding:
     password: sharding 
     authorizedSchemas: sharding_db
yml
props:
 max.connections.size.per.query: 1
 acceptor.size: 16  # The default value is available processors count * 2.
 executor.size: 16  # Infinite by default.
 proxy.frontend.flush.threshold: 128  # The default value is 128.
   # LOCAL: Proxy will run with LOCAL transaction.
   # XA: Proxy will run with XA transaction.
   # BASE: Proxy will run with B.A.S.E transaction.
 proxy.transaction.type: LOCAL
 proxy.opentracing.enabled: false
 proxy.hint.enabled: false
 query.with.cipher.column: true
 sql.show: true
 allow.range.query.with.inline.sharding: false

config-sharding.yml:配置数据分片策略。在配置文件中有两个数据库的配置:postgresql和MySQL,使用MySQL需要添加mysql的驱动包到lib,在Docker部署Sharding-Proxy容器的时候就已经添加了该jar包。配置和之前Sharding-JDBC的一些配置相似

######################################################################################################
#
# If you want to connect to MySQL, you should manually copy MySQL driver to lib directory.
#
######################################################################################################

schemaName: sharding_db

dataSources:
 ds_0:
   url: jdbc:mysql://127.0.0.1:3306/demo_ds_0?serverTimezone=UTC&useSSL=false
   username: root
   password:
   connectionTimeoutMilliseconds: 30000
   idleTimeoutMilliseconds: 60000
   maxLifetimeMilliseconds: 1800000
   maxPoolSize: 50
 ds_1:
   url: jdbc:mysql://127.0.0.1:3306/demo_ds_1?serverTimezone=UTC&useSSL=false
   username: root
   password:
   connectionTimeoutMilliseconds: 30000
   idleTimeoutMilliseconds: 60000
   maxLifetimeMilliseconds: 1800000
   maxPoolSize: 50

shardingRule:
 tables:
   t_order:
     actualDataNodes: ds_${0..1}.t_order_${0..1}
     tableStrategy:
       inline:
         shardingColumn: order_id
         algorithmExpression: t_order_${order_id % 2}
     keyGenerator:
       type: SNOWFLAKE
       column: order_id
   t_order_item:
     actualDataNodes: ds_${0..1}.t_order_item_${0..1}
     tableStrategy:
       inline:
         shardingColumn: order_id
         algorithmExpression: t_order_item_${order_id % 2}
     keyGenerator:
       type: SNOWFLAKE
       column: order_item_id
 bindingTables:
   - t_order,t_order_item
 defaultDatabaseStrategy:
   inline:
     shardingColumn: user_id
     algorithmExpression: ds_${user_id % 2}
 defaultTableStrategy:
   none: