Springboot整合ShardingSphere实现分库分表,读写分离

ShardingSphere简介

Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成,我们只关注 Sharding-JDBC 即可。Sharding-JDBC 定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架的使用。Sharding-JDBC 主要功能:数据分片读写分离 通过Sharding-JDBC ,应用可以透明的使用jdbc访问已经分库分表、读写分离的多个数据源,而不用关心数据源的数量以及数据如何分布。

SpringBoot整合ShardingSphere

本次记录项目版本为SpringBoot(2.4.0),ShardingSphere(4.0.0-RC1)

PS:由于ShardingSphere版本过高启动项目会出现报错,经测试4.0.0-RC1在当前SpringBoot版本启动正常,更换其他版本请自行调试

ShardingSphere读写分离

  • 拟定我们要为订单表实现读写分离,插入,修改在主库 ds0 中进行,查询在从库 ds1 中操作。
  1. 先创建订单表,这里是用本地运行2个MYSQL服务分别在库中创建,具体实现请参考本地同时运行多个MYSQL服务一文。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    DROP TABLE IF EXISTS `fu_order`;
    CREATE TABLE `fu_order` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单id',
    `user_id` int DEFAULT NULL COMMENT '用户id',
    `product_id` int DEFAULT NULL COMMENT '商品id',
    `order_no` bigint DEFAULT NULL COMMENT '订单号',
    `quantity` int DEFAULT NULL COMMENT '商品数量',
    `total_price` decimal(20,2) DEFAULT NULL COMMENT '商品总价,单位(元)保留2位小数点',
    `status` int DEFAULT NULL COMMENT '订单状态(0:已取消,1:待支付,2:已付款)',
    `end_time` datetime DEFAULT NULL COMMENT '交易完成时间',
    `closet_time` datetime DEFAULT NULL COMMENT '交易关闭时间',
    `create_time` datetime DEFAULT NULL COMMENT '创建时间',
    `update_time` datetime DEFAULT NULL COMMENT '更新时间',
    `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除',
    PRIMARY KEY (`id`),
    UNIQUE KEY `order_no_index` (`order_no`) USING BTREE,
    UNIQUE KEY `order_no_user_id_index` (`user_id`,`order_no`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT = 1 CHARSET=utf8mb4 COMMENT='订单表';
  2. 在项目pom.xml中引入相关依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- ShardingSphere -->
    <dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.0.0-RC1</version>
    </dependency>

    <dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-namespace</artifactId>
    <version>4.0.0-RC1</version>
    </dependency>
  3. 配置文件

    PS:需要配置下mybatis-plus中的命名为dataSource的bean和shardingjdbc中的bean名字冲突导致无法启动项目的问题。本项目数据库使用Jasypt进行加密,如需配置请参考Jasypt加密库一文。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    spring:
    jackson:
    time-zone: GMT+8
    # shardingsphere 读写分离配置
    main:
    allow-bean-definition-overriding: true # 启动项目报bean冲突配置(shardingsphere/mybatisplus)
    shardingsphere:
    enabled: true
    datasource:
    ds0:
    type: com.zaxxer.hikari.HikariDataSource
    jdbc-url: ENC(1Ec2OVfgDl1nKRf4f2wXI1f1uhLa2FCc3Aw3mxVnChU64rlqO/PemyFSfHK/G0n5DQySOKllf3A/LJKKaUNfIS+ZoYKiODV40FmaVac+62XDlzdhU3qigmwTdfWNyihK7Peng+8wmiMiuziXe5huVpNoUbKD+9pj)
    password: ENC(j/o7k6nZe+yAMoWVWRnjFg==)
    username: ENC(ytrxf1igA4bazqZ6TOOc1A==)
    ds1:
    type: com.zaxxer.hikari.HikariDataSource
    jdbc-url: ENC(rHDezuPA7GdjUHe+RP7cdZlOx/O4TjoTOfsncXmePbm8b27Viocz2YZJvORLoAl8KiV5Qs/f2OjzaJ+cBFHwG5p29u0Yuj37tGNVqiP64qWTU9U2AL8GgQY38VeAJl3O72vxPxOpwnGnsyFWNzLFqX0boEhQIaEX)
    password: ENC(j/o7k6nZe+yAMoWVWRnjFg==)
    username: ENC(ytrxf1igA4bazqZ6TOOc1A==)
    # 配置数据源
    names: ds0,ds1
    masterslave:
    # 配置slave节点的负载均衡均衡策略,采用轮询机制
    load-balance-algorithm-type: round_robin
    # 配置主库master,负责数据的写入
    master-data-source-name: ds0
    # 配置主从名称
    name: ms
    # 配置从库slave节点
    slave-data-source-names: ds1
    props:
    sql:
    show: true
    sharding:
    # 配置默认数据源ds1 默认数据源,主要用于写
    default-data-source-name: ds0
    mybatis-plus:
    configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    mapper-locations: com/fu99999/note/mapper/xml/*.xml
    type-aliases-package: com.ppdai.shardingjdbc.entity # shardingjdbc整合mybatis的配置
  4. 测试
  • 启动项目创建一个新订单,控制台输出如图,Insert语句是在ds0主库中的表执行。
  • 我们在查询刚插入的这条订单信息,可以看出select语句是在ds1从库执行,所以没有查到数据。

  • 需要配置数据库主从同步才能在从库中查到数据,请参考MQSQL主从数据同步配置一文。

ShardingSphere分库分表

  1. 分别在3306库和3307库创建3张订单表,根据id字段取模确定最终数据的位置,数据库环境配置如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    localhost:3306
    wind-of-grace
    fu_order_0
    fu_order_1
    fu_order_2
    localhost:3307
    wind-of-grace
    fu_order_0
    fu_order_1
    fu_order_2
  2. 三张表的逻辑表为fu_order_0

    PS:这里不能将主键的生成规则设置成自增长,需要使用shardingsphere中的SNOWFLAKE雪花算法来生成主键

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    DROP TABLE IF EXISTS `fu_order_0`;
    CREATE TABLE `fu_order_0` (
    `id` bigint NOT NULL COMMENT '订单id',
    `user_id` int DEFAULT NULL COMMENT '用户id',
    `product_id` int DEFAULT NULL COMMENT '商品id',
    `order_no` bigint DEFAULT NULL COMMENT '订单号',
    `quantity` int DEFAULT NULL COMMENT '商品数量',
    `total_price` decimal(20,2) DEFAULT NULL COMMENT '商品总价,单位(元)保留2位小数点',
    `status` int DEFAULT NULL COMMENT '订单状态(0:已取消,1:待支付,2:已付款)',
    `end_time` datetime DEFAULT NULL COMMENT '交易完成时间',
    `closet_time` datetime DEFAULT NULL COMMENT '交易关闭时间',
    `create_time` datetime DEFAULT NULL COMMENT '创建时间',
    `update_time` datetime DEFAULT NULL COMMENT '更新时间',
    `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除',
    PRIMARY KEY (`id`),
    UNIQUE KEY `order_no_index` (`order_no`) USING BTREE,
    UNIQUE KEY `order_no_user_id_index` (`user_id`,`order_no`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表_0';
  3. 配置文件
    定义分库分表规则:根据主键分片,偶数主键的记录放入ds0库(或表),奇数主键的记录放入ds1库(或表)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    spring:
    jackson:
    time-zone: GMT+8
    # shardingsphere 分库分表配置
    # 启动项目报bean冲突配置(shardingsphere/mybatisplus)
    main:
    allow-bean-definition-overriding: true
    shardingsphere:
    # 是否启用
    enabled: true
    datasource:
    ds0:
    type: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbc-url: ENC(1Ec2OVfgDl1nKRf4f2wXI1f1uhLa2FCc3Aw3mxVnChU64rlqO/PemyFSfHK/G0n5DQySOKllf3A/LJKKaUNfIS+ZoYKiODV40FmaVac+62XDlzdhU3qigmwTdfWNyihK7Peng+8wmiMiuziXe5huVpNoUbKD+9pj)
    password: ENC(j/o7k6nZe+yAMoWVWRnjFg==)
    username: ENC(ytrxf1igA4bazqZ6TOOc1A==)
    ds1:
    type: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbc-url: ENC(rHDezuPA7GdjUHe+RP7cdZlOx/O4TjoTOfsncXmePbm8b27Viocz2YZJvORLoAl8KiV5Qs/f2OjzaJ+cBFHwG5p29u0Yuj37tGNVqiP64qWTU9U2AL8GgQY38VeAJl3O72vxPxOpwnGnsyFWNzLFqX0boEhQIaEX)
    password: ENC(j/o7k6nZe+yAMoWVWRnjFg==)
    username: ENC(ytrxf1igA4bazqZ6TOOc1A==)
    # 配置ds0 和ds1两个数据源
    names: ds0,ds1
    # 打印SQL
    props:
    sql:
    show: true
    # 分库策略 根据id取模确定数据进哪个数据库 偶数存ds0库/表 奇数存ds1库/表
    sharding:
    default-database-strategy:
    inline:
    algorithm-expression: ds$->{id % 2}
    sharding-column: id
    # 具体分表策略
    tables:
    fu_order:
    actual-data-nodes: ds$->{0..1}.fu_order_$->{0..2}
    # ※千万不能将主键的生成规则设置成自增长,需要按照一定规则来生成主键,这里使用shardingsphere中的SNOWFLAKE俗称雪花算法来生成主键
    key-generator:
    column: id
    type: SNOWFLAKE # 使用SNOWFLAKE算法生成主键
    # 分表字段id
    # 分表策略 根据id取模,确定数据最终落在那个表中
    table-strategy:
    inline:
    algorithm-expression: fu_order_$->{id % 3}
    sharding-column: id
  4. 测试
  • 启动项目创建几个订单,观察控制台输出

  • 根据分库分表规则,订单被分别插入到ds1库中的fu_order_0表和ds0库中的fu_order_1表中