mybatis plus + shardingsphere读写分离+分表

需求场景

在实际业务中,高并发的数据库中,单个数据库性能受限,尤其是在有读写夹杂在一起的时候。我们都知道,系统中大部分都是读操作,写操作是占用比较小的部分的,所有就有了读写分离的操作,读可以专门读的数据库,而写操作(CUD)就专门在主数据库中进行。
mybatis plus + shardingsphere读写分离+分表_第1张图片
而分表则是为了避免单个的表的数据量太大,根据ali开发规范来说,一个表的上限是2G,数据量上限为500w,但是通常来说虽然我们不一定完全遵循,但是2000w条数据,对于系统来说也是比较大了,而2000w数据来说是非常正常的,一些比如日志表啥的,动不动是10G+以上的数据,所以分表也是非常必要的。

方案

仅仅是主从分离的话,可以使用mybatis plus系列的dynamic-datasource,参考data-source来实现主从分离,非常方便,但是暂时没有分表功能。所以我们使用mybaits plus + shardingsphere方案

方案 特点
dynamic-datasource 简单方便,支持开发指定从库的数据查询 ,不支持分表
shardingshpere 功能强大,比较复杂,支持分表,暂时不支持指定库操作
什么是mybatis plus

mybatis plus是国内基于mybatis做出一个开源工具包,

是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

简单来说就是代替mybatis的一个orm访问工具,但是功能更强大,这里不多赘述,因为我们就是用它的orm功能,其他的查看文档。
官方文档

什么是shardingsphere

官方文档

Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。

Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。 它站在数据库的上层视角,关注它们之间的协作多于数据库自身。

shardingSphere的功能非常强大,但是我们暂时只需要使用到sharding-jdbc,sharding-jdbc时什么呢?

ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务

我的理解就是jdbc api的重写和封装。

系统结构

  1. spring boot
  2. mybatis plus
  3. shardingsphere
    maven依赖如下:
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            mysql
            mysql-connector-java
            
        
        
            com.baomidou
            mybatis-plus-boot-starter
        
        
        
            org.apache.shardingsphere
            sharding-jdbc-spring-boot-starter
            4.1.1
        
        
            org.apache.shardingsphere
            sharding-core-common
            4.1.1
        
        
            com.alibaba
            druid
            1.1.9
        

数据结构

在开始之前我们,我们需要创建sql,数据结构如下:
order (1) —order_id----> order_item(n),请将idx换成0~1

sql
CREATE TABLE `t_order_idx` (
  `order_id` bigint NOT NULL,
  `order_no` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `create_name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

CREATE TABLE `t_order_item_0` (
  `item_id` bigint NOT NULL,
  `order_no` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `item_name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `order_id` bigint DEFAULT NULL,
  PRIMARY KEY (`item_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

如何配置

如果上述步骤完成后,那么需要做的仅仅做些配置了,yaml注册文件如下:

spring:
  shardingsphere:
#    数据库名称
    datasource:
      names: master,slave-0,slave-1
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/asx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
        username: root
        password: root
      slave-0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3307/asx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
        username: root
        password: root
      slave-1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3308/asx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
        username: root
        password: root
#  配置分片规则
    sharding:
      tables:
        t_order:
          key-generator:
            column: order_id
            type: SNOWFLAKE
          actual-data-nodes: ms.t_order_$->{0..1}
          database-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: ms
          table-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: t_order_$->{order_id % 2}
        t_order_item:
          key-generator:
            column: item_id
            type: SNOWFLAKE
          actual-data-nodes: ms.t_order_item_$->{0..1}
          database-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: ms
          table-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: t_order_item_$->{order_id % 2}
      binding-tables: t_order,t_order_item
      broadcast-tables: t_config
#      default-data-source-name: master
      defaultTableStrategy:
        none:
#      配置主从规则
      masterSlaveRules:
        ms:
          masterDataSourceName: master
          slaveDataSourceNames:
            - slave-0
            - slave-1
          loadBalanceAlgorithmType: ROUND_ROBIN
    props:
      sql:
        show: true                                          # 打印SQL

结果校验

校验数据插入问题

编写insert case

 @Test
    public void testCreateOrder() {
        int start = 0;
        for (int i = start; i < start + 10; i++) {
            BtOrderModel order = new BtOrderModel();
            order.setOrderNo("A000" + i);
            order.setCreateName("订单 " + i);
            order.setPrice(new BigDecimal("" + i));
            btOrderMapper.insert(order);

            BtOrderItemModel orderItem = new BtOrderItemModel();
            orderItem.setOrderId(order.getOrderId());
            orderItem.setOrderNo("A000" + i);
            orderItem.setItemName("服务项目" + i);
            orderItem.setPrice(new BigDecimal("" + i));
            btOrderItemMapper.insert(orderItem);
        }
    }

查看日志

INFOLogic SQL: INSERT INTO t_order  ( order_id,
order_no,
create_name,
price )  VALUES  ( ?,
?,
?,
? )
INFOSQLStatement: InsertStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@3a5e2525, tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@6546371), tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@6546371, columnNames=[order_id, order_no, create_name, price], insertValueContexts=[InsertValueContext(parametersCount=4, valueExpressions=[ParameterMarkerExpressionSegment(startIndex=75, stopIndex=75, parameterMarkerIndex=0), ParameterMarkerExpressionSegment(startIndex=78, stopIndex=78, parameterMarkerIndex=1), ParameterMarkerExpressionSegment(startIndex=81, stopIndex=81, parameterMarkerIndex=2), ParameterMarkerExpressionSegment(startIndex=84, stopIndex=84, parameterMarkerIndex=3)], parameters=[1584134049671151617, A00011, 订单 11, 11])], generatedKeyContext=Optional[GeneratedKeyContext(columnName=order_id, generated=false, generatedValues=[1584134049671151617])])
INFOActual SQL: master ::: INSERT INTO t_order_1  ( order_id,
order_no,
create_name,
price )  VALUES  (?, ?, ?, ?) ::: [1584134049671151617, A00011, 订单 11, 11]

actual sql中采用的是master数据库

校验数据查询问题

编写一个url为:

GET http://localhost:9380/master/slave/sharding/order/{id} 

的接口,让后手动修改数据库中的值,调用两次,就会发现结果不同,当然你也可以查看日志
mybatis plus + shardingsphere读写分离+分表_第2张图片

遇到的问题

问题1: 插入insert的时候,主键不回写,如上面的orderId,orderId是根据雪花算法生成

解决方案: Model中增加主键注释

  @TableId(type = IdType.ASSIGN_ID)
  1. 分页查询
    分页查询如果没有shardingKey的话,会遍历每个表,然后做集合分页,这样的话效率比较低

参考文档

  1. Mysql支持分库请参考:windows配置mysql8.0主从数据库,主从数据同步。
    分库的话配置参考:
  2. shardingsphere配置手册
  3. ShardingSphere分库分表3-内核原理及核心源码解析

源码参考

spring-data-sharding

你可能感兴趣的:(数据库,java后端开发,mybatis,shardingsphere,读写分离,分表)