12. ShardingSphere-JDBC 分库分表

Spring Cloud 微服务系列文章,点击上方合集↑

1. 简介

ShardingSphere 是国产的、开源的、配置简单的分布式数据库解决方案。可以通过简单的配置实现分库分表读写分离

ShardingSphere 提供了两种分布式数据库解决方案:ShardingSphere-JDBCShardingSphere-Proxy

ShardingSphere-JDBC是一个基于JDBC协议的中间件,JDBC API是Java访问关系型数据库的标准API,因此ShardingSphere-JDBC适用于JDBC API连接的各种关系型数据库。它通过JDBC驱动程序来拦截应用程序发出的数据库访问请求,并根据事先配置的分片规则将请求路由到正确的数据库分片中。

ShardingSphere-Proxy则是一个基于数据库协议的中间件,主要用于对数据库协议的拦截和处理。它是一个代理数据库,对于开发人员完全透明无感知。

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

本文先讲解ShardingSphere-JDBC。

2. 执行sql脚本

创建sharding_0sharding_1两个数据库。两个数据库完全一样,包含如下数据表:

  1. company 企业表,根据id分库

  2. product 商品表,根据企业idcompany_id分库

  3. permission 权限表,广播表不分库

CREATE DATABASE sharding_0 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

CREATE DATABASE sharding_1 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

CREATE TABLE `company`  (
  `id` bigint(20) NOT NULL COMMENT '主键id',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

CREATE TABLE `permission`  (
  `id` bigint(20) NOT NULL COMMENT '主键id',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

CREATE TABLE `product`  (
  `id` bigint(20) NOT NULL COMMENT '主键id',
  `company_id` bigint(20) NULL DEFAULT NULL COMMENT '公司id',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

3. pom.xml

添加shardingsphere-jdbc-core包依赖,版本是5.4.0

<dependency>
    <groupId>org.apache.shardingspheregroupId>
    <artifactId>shardingsphere-jdbc-coreartifactId>
    <version>5.4.0version>
dependency>
  • 5.3之前的版本和5.3之后的版本引入包的方式不一样,要注意一下。

如果出现如下问题在properties节点加上1.33

java.lang.NoSuchMethodError: org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.setCodePointLimit(I)V

4. application.properties

配置数据库驱动为org.apache.shardingsphere.driver.ShardingSphereDriver

数据库路径指向sharding.yaml配置文件。

spring.datasource.driver-class-name=org.apache.shardingsphere.driver.ShardingSphereDriver
spring.datasource.url=jdbc:shardingsphere:classpath:sharding.yaml

5. sharding.yaml

resources目录下创建sharding.yaml配置文件,主要配置如下内容:

  • 逻辑数据源 ds_0 指向 jdbc:mysql://127.0.0.1:3306/sharding_0
  • 逻辑数据源 ds_1 指向 jdbc:mysql://127.0.0.1:3306/sharding_1
  • company表的分片规则是id_inline,根据id取模。
  • product表没有配置分片规则,用默认分配规则,根据company_id取模。
  • permission表是广播表,插入(更新)数据的时候每张表都会插入(更新),读取的时候随机一张表读取。
  • 取模算法ds_$->{id % 2} 偶数在ds_0,奇数在ds_1
dataSources:
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/sharding_0?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
  ds_1:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/sharding_1?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456

rules:
- !SHARDING
  tables:
    company:
      actualDataNodes: ds_$->{0..1}.company
      databaseStrategy:
        standard:
          shardingColumn: id
          shardingAlgorithmName: id_inline
    product:
      actualDataNodes: ds_$->{0..1}.product
  defaultDatabaseStrategy:
    standard:
      shardingColumn: company_id
      shardingAlgorithmName: database_inline
  shardingAlgorithms:
    database_inline:
      type: INLINE
      props:
        algorithm-expression: ds_$->{company_id % 2}
    id_inline:
      type: INLINE
      props:
        algorithm-expression: ds_$->{id % 2}

- !BROADCAST
  tables: # 广播表规则列表
    - permission
props:
  sql-show: true

6. Controller

6.1 CompanyController

新增企业的接口,通过OpenFeign调用id-service服务递增生成企业id,企业名称通过UUID随机生成。

@RestController
@RequestMapping("company")
public class CompanyController {

    @Resource
    private CompanyService companyService;

    @Resource
    private IdService idService;

    @GetMapping("add")
    public Boolean add() {
        Company company = new Company();
        company.setId(idService.generateId("company"));
        company.setName("企业名称;" + UUID.randomUUID());
        company.setCreateTime(LocalDateTime.now());
        company.setUpdateTime(LocalDateTime.now());
        return companyService.save(company);
    }
}

6.2 PermissionController

新增权限的接口,通过OpenFeign调用id-service服务递增生成权限id,权限名称通过UUID随机生成。

@RestController
@RequestMapping("permission")
public class PermissionController {

    @Resource
    private PermissionService permissionService;

    @Resource
    private IdService idService;

    @GetMapping("add")
    public Boolean add() {
        Permission permission = new Permission();
        permission.setId(idService.generateId("permission"));
        permission.setName("权限名称:" + UUID.randomUUID());
        permission.setCreateTime(LocalDateTime.now());
        permission.setUpdateTime(LocalDateTime.now());
        return permissionService.save(permission);
    }
}

6.3 ProductController

新增商品的接口,传入参数companyId表明是新增那个企业的商品,通过OpenFeign调用id-service服务递增生成商品id,
商品名称通过UUID随机生成。

@RestController
@RequestMapping("product")
public class ProductController {

    @Resource
    private ProductService productService;

    @Resource
    private IdService idService;

    @GetMapping("add")
    public Boolean add(@RequestParam Long companyId) {
        Product product = new Product();
        product.setId(idService.generateId("product"));
        product.setCompanyId(companyId);
        product.setName("商品名称:" + UUID.randomUUID());
        product.setCreateTime(LocalDateTime.now());
        product.setUpdateTime(LocalDateTime.now());
        return productService.save(product);
    }
}

7. IdService

这里需要启动id-service。(具体可以看之前分布式ID那一篇文章)。

leaf_alloc数据表中插入三条记录,

INSERT INTO `leaf_alloc` VALUES ('company', 1, 2000, '企业表', '2023-08-30 11:26:23');
INSERT INTO `leaf_alloc` VALUES ('permission', 1, 2000, '权限表', '2023-08-30 11:26:27');
INSERT INTO `leaf_alloc` VALUES ('product', 1, 2000, '商品表', '2023-08-30 11:26:33');
  • 对于不同的业务表,在leaf_alloc数据表使用不同的biz_tag,比如商品表就在leaf_alloc表中插入biz_tagproduct的一条记录,获取id传入参数就是key=product

通过OpenFeign调用id-service服务获取id。

@FeignClient(name = "id-service")
public interface IdService {

    @GetMapping("/id/generate")
    Long generateId(@RequestParam String key);
}

8. 分库分表结果

8.1 新增企业

浏览器访问新增企业接口地址:http://localhost:8091/company/add

多次请求后发现:id为偶数的企业记录在sharding_0数据库,id为奇数的企业记录在sharding_1数据库。

12. ShardingSphere-JDBC 分库分表_第1张图片

12. ShardingSphere-JDBC 分库分表_第2张图片

8.2 新增商品

浏览器访问新增商品接口地址:

  • id为1的企业新增商品:http://localhost:8091/product/add?companyId=1

  • id为2的企业新增商品:http://localhost:8091/product/add?companyId=2

company_id为偶数的商品记录在sharding_0数据库,company_id为奇数的商品记录在sharding_1数据库。

注意跟company表的区别,company表是通过id取模,product表是通过company_id取模。

12. ShardingSphere-JDBC 分库分表_第3张图片

12. ShardingSphere-JDBC 分库分表_第4张图片

8.3 新增权限

浏览器访问新增权限接口地址:

http://localhost:8091/permission/add

发现sharding_0sharding_1两个数据库都有数据记录。

12. ShardingSphere-JDBC 分库分表_第5张图片

9. 结语

本文讲解了如何通过ShardingSphere-JDBC进行分库分表,通过ShardingSphere-JDBC分库分表需要在代码中引入相关依赖包并且在配置文件中进行相关配置。

ShardingSphere-JDBC的不方便之处就是需要开发人员在每个应用服务都去配置,对于开发人员是不透明的。但是由于是应用直连数据库,它避免了代理数据库崩溃的风险。

下文将讲解如何通过ShardingSphere-Proxy代理数据库来实现分布式数据库。


12. ShardingSphere-JDBC 分库分表_第6张图片

Spring Cloud 微服务系列 完整的代码在仓库的sourcecode/spring-cloud-demo目录下。

gitee(推荐):https://gitee.com/cunzaizhe/xiaohuge-blog

github:https://github.com/tigerleeli/xiaohuge-blog

关注微信公众号:“小虎哥的技术博客”,让我们一起成为更优秀的程序员❤️!

你可能感兴趣的:(Spring,Cloud,微服务系列,spring,cloud,微服务,分库分表)