Bootstrap

Mybatis-Plus 配合Sharding-JDBC 实现分库分表

在现代数据库设计中,随着数据量的不断增长,单一数据库往往无法满足高并发、高性能的业务需求。因此,分库分表策略成为数据库架构优化的重要手段。本文将介绍分库分表的基本概念,并重点探讨垂直拆分与水平拆分的区别,以及如何在 MyBatis-Plus 中结合 Sharding-JDBC 实现数据库的分库分表。

数据库分库分表概念

分库分表是数据库优化的一种常见方式,其核心目的是为了减少单库单表的数据压力,提高查询效率,同时提升数据库的可扩展性。

  • 分库(Database Sharding):将数据拆分存储到多个独立的数据库中。
  • 分表(Table Sharding):将一张大表拆分成多张小表,提高查询效率。

分库分表可以缓解单库承载的压力,并能提高数据库的可用性,但同时也会带来数据路由、事务一致性等挑战。

垂直拆分与水平拆分的区别

垂直拆分(Vertical Partitioning)

垂直拆分主要是按照业务功能或者数据表的字段进行拆分,通常有两种方式:

  1. 按业务拆分:不同的业务数据存储在不同的数据库中,例如用户数据存储在 user_db,订单数据存储在 order_db。
  2. 按字段拆分:将大表按照字段拆分成不同的表,例如 user 表可能包含大量不常用的扩展字段,可以拆分成 user_base(基础信息表)和 user_detail(扩展信息表)。

优点

  • 业务逻辑清晰,适用于不同业务模块的数据隔离。
  • 单表字段减少,提高查询效率。

缺点

  • 需要修改业务代码,跨库查询变得复杂。
  • 事务处理难度增加。

水平拆分(Horizontal Partitioning)

水平拆分是按照数据的范围进行拆分,将相同结构的数据分散存储到不同的数据库或表中,常见的拆分策略有:

  1. 按范围拆分(Range Sharding):例如按照用户 ID 进行范围分片,id 在 11000000 存储在 db_1,10000012000000 存储在 db_2。
  2. 按哈希拆分(Hash Sharding):通过哈希函数计算数据存储位置,例如 hash(user_id) % 4 来决定数据存储在哪个分库。

优点

  • 解决了单表数据量过大的问题,提高查询性能。
  • 单表数据量减少,提高索引查询效率。

缺点

  • 需要数据库代理或者中间件进行数据路由。
  • 分片后的数据管理变得复杂。

在 MyBatis-Plus 中实现分库分表

MyBatis-Plus 是基于 MyBatis 的增强框架,提供了更加方便的 CRUD 操作,而 Sharding-JDBC 则是用于分库分表的中间件。二者结合可以实现灵活的分库分表方案。

1. 引入依赖

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.1</version>
</dependency>

2. 配置数据源

spring:
  shardingsphere:
    datasource:
      names: db1,db2
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1
        username: root
        password: password
      db2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db2
        username: root
        password: password
    sharding:
      tables:
        user:
          actual-data-nodes: db$->{1..2}.user_$->{0..1}
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: user_$->{id % 2}
          key-generator:
            column: id
            type: SNOWFLAKE

3. 配置 MyBatis-Plus

@Configuration
@MapperScan("com.example.mapper")
public class MyBatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

4. 编写 Mapper 接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

5. 测试分库分表

public void testInsert()ublic void testInsert() {
        User user = new User();
        user.setId(1L);
        user.setName("Alice");
        userMapper.insert(user);
    }
}

范围查询的实现

在实际应用中,范围查询(如按时间或 ID 范围查询)是常见需求,Sharding-JDBC 提供了基于 SQL 解析的查询路由。

public void testRangeQuery() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.between("id", 1000, 2000);
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

底层原理

  1. SQL 解析:Sharding-JDBC 解析 BETWEEN 语句,识别分片键。
  2. 数据路由:计算查询的范围涉及哪些数据库或表。
  3. SQL 重写:将单一查询拆分为多个子查询,在多个分片表上执行。
  4. 数据合并:Sharding-JDBC 负责合并多个分片的查询结果,并返回统一的结果集。

总结

本文介绍了数据库分库分表的概念,并对比了垂直拆分与水平拆分的优缺点。在 MyBatis-Plus 中,可以通过 Sharding-JDBC 轻松实现分库分表,提高数据库的可扩展性和查询性能。此外,我们深入分析了 MyBatis-Plus 与 Sharding-JDBC 结合的底层执行逻辑,展示了 SQL 路由、改写及执行的完整过程,并补充了范围查询的实现方式及底层机制。在实际应用中,选择合适的拆分策略需要结合业务需求,确保数据分布均匀,并考虑事务一致性和查询优化等问题。

;