java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > ShardingJDBC数据分片

使用ShardingJDBC进行数据分片以及读写分离

作者:一只爱撸猫的程序猿

ShardingJDBC是一个轻量级的Java框架,提供了数据分片、读写分离、分布式主键生成等数据访问功能,本文将给大家介绍如何使用ShardingJDBC进行数据分片以及读写分离,需要的朋友可以参考下

简述

原理

ShardingJDBC 的核心组件和功能,包括一些相关代码片段以更好地理解其工作原理。

1. SQL 解析

SQL 解析是 ShardingJDBC 处理流程的起点。ShardingJDBC 使用 ANTLR(另一个语言识别工具)作为 SQL 解析工具。

SQLParseEngine 源码分析

public final class SQLParseEngine {
    private final DatabaseType databaseType;
    private final String sql;
    private final LexerEngine lexerEngine;
    private final SQLStatementParser statementParser;

    public SQLParseEngine(DatabaseType databaseType, String sql, Properties properties, List<SQLToken> sqlTokens) {
        this.databaseType = databaseType;
        this.sql = sql;
        Lexer lexer = LexerFactory.newInstance(databaseType, sql);
        this.lexerEngine = new LexerEngine(lexer);
        this.statementParser = SQLStatementParserFactory.newInstance(databaseType, lexerEngine);
    }

    public SQLStatement parse() {
        lexerEngine.nextToken();
        return statementParser.parse();
    }
}

在上面的代码中,SQLParseEngine 使用了数据库类型(DatabaseType)和 SQL 语句来初始化。它首先创建一个 Lexer(词法解析器),然后使用这个 Lexer 创建一个 LexerEngine,并且基于数据库类型创建相应的 SQL 语句解析器。parse 方法最终返回一个 SQL 语句对象(SQLStatement),这个对象代表了解析后的 SQL 语句。

2. 路由计算

路由是根据分片策略和解析出的 SQL 信息,确定 SQL 应该执行在哪些具体的数据库和表上。

ShardingRouter 源码分析

public final class ShardingRouter {
    private final ShardingRule shardingRule;

    public ShardingRouter(ShardingRule shardingRule) {
        this.shardingRule = shardingRule;
    }

    public RoutingResult route(final SQLStatement sqlStatement) {
        // 省略具体路由逻辑
    }
}

在这里,ShardingRouter 通过 ShardingRule(包含分片策略和规则)来进行初始化。route 方法接受一个 SQL 语句对象,并根据分片规则返回路由结果。

3. SQL 改写

根据路由结果,ShardingJDBC 会改写原始 SQL,使其适用于目标的物理数据库和表。

SQLRewriteEngine 源码分析

public final class SQLRewriteEngine {
    private final SQLStatement sqlStatement;
    private final List<SQLToken> sqlTokens;

    public SQLRewriteEngine(SQLStatement sqlStatement) {
        this.sqlStatement = sqlStatement;
        this.sqlTokens = new LinkedList<>();
    }

    public String rewrite() {
        // 省略具体改写逻辑
    }
}

这里的 SQLRewriteEngine 接收一个 SQL 语句对象,并根据路由结果和 SQL 语句中的令牌(SQLToken)列表来改写 SQL。

4. 执行计划生成与执行

生成 SQL 的执行计划并在相应的数据库实例上执行。

public class ShardingExecuteEngine implements AutoCloseable {
    private final ExecutorService executorService;

    public ShardingExecuteEngine(int executorSize) {
        this.executorService = Executors.newFixedThreadPool(executorSize);
    }

    // 省略执行方法
}

ShardingExecuteEngine 使用一个线程池来执行 SQL。这个类负责管理 SQL 的执行过程,包括可能的并行查询和结果集合并。

5. 结果集合并

对于查询操作,ShardingJDBC 需要合并来自不同物理表或数据库的结果集。

public final class MergeEngine {
    public MergedResult merge(List<QueryResult> queryResults, SQLStatement sqlStatement) {
        // 省略合并逻辑
    }
}

MergeEngine 负责将来自不同物理表或数据库的查询结果合并成一个统一的结果集。它根据 SQL 语句的类型(如聚合查询、排序查询)来应用不同的合并策略。

6. 分布式事务处理

处理分布式环境下的事务一致性。

ShardingTransactionManager 接口和 XAShardingTransactionManager 实现

public interface ShardingTransactionManager {
    void begin();
    void commit();
    void rollback();
    // 省略其他方法
}

public class XAShardingTransactionManager implements ShardingTransactionManager {
    // 实现分布式事务管理逻辑
}

ShardingTransactionManager 接口定义了事务管理的基本行为,如开始(begin)、提交(commit)和回滚(rollback)操作。XAShardingTransactionManager 是这个接口的一个实现,用于处理 XA 类型的分布式事务。

我们将假设有两个业务表:orderuser,并且这两个表需要根据不同的策略进行分片。同时,我们将设置四个数据源(两个主库和两个从库)来实现读写分离。

实际案例

场景设定

配置和代码实现

添加依赖:

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

配置文件 application.yml:

spring:
  shardingsphere:
    datasource:
      names: ds0,ds0_slave,ds1,ds1_slave
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: YOUR_DRIVER_CLASS
        jdbc-url: JDBC_URL_FOR_DS0
        username: YOUR_USERNAME
        password: YOUR_PASSWORD
      ds0_slave:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: YOUR_DRIVER_CLASS
        jdbc-url: JDBC_URL_FOR_DS0_SLAVE
        username: YOUR_USERNAME
        password: YOUR_PASSWORD
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: YOUR_DRIVER_CLASS
        jdbc-url: JDBC_URL_FOR_DS1
        username: YOUR_USERNAME
        password: YOUR_PASSWORD
      ds1_slave:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: YOUR_DRIVER_CLASS
        jdbc-url: JDBC_URL_FOR_DS1_SLAVE
        username: YOUR_USERNAME
        password: YOUR_PASSWORD
    sharding:
      tables:
        order:
          actual-data-nodes: ds${0..1}.order_${0..1}
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: order_${id % 2}
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: ds${user_id % 2}
          key-generator:
            type: SNOWFLAKE
            column: id
        user:
          actual-data-nodes: ds${0..1}.user_${0..1}
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: user_${id > 5000 ? 1 : 0}
          database-strategy:
            inline:
              sharding-column: id
              algorithm-expression: ds${id % 2}
          key-generator:
            type: SNOWFLAKE
            column: id
      master-slave-rules:
        ds0:
          master-data-source-name: ds0
          slave-data-source-names: ds0_slave
        ds1:
          master-data-source-name: ds1
          slave-data-source-names: ds1_slave

这段配置是用于设置 Apache ShardingSphere(ShardingJDBC 的一个部分)的 YAML 格式的配置文件,专门用于 Spring Boot 项目。它定义了数据源(包括主从数据源),表的分片策略,以及主从复制规则。让我们逐个部分进行详细解释:

数据源配置

datasource:
  names: ds0,ds0_slave,ds1,ds1_slave
  ds0:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: YOUR_DRIVER_CLASS
    jdbc-url: JDBC_URL_FOR_DS0
    username: YOUR_USERNAME
    password: YOUR_PASSWORD
  ...

分片配置

sharding:
  tables:
    order:
      actual-data-nodes: ds${0..1}.order_${0..1}
      table-strategy:
        inline:
          sharding-column: id
          algorithm-expression: order_${id % 2}
      database-strategy:
        inline:
          sharding-column: user_id
          algorithm-expression: ds${user_id % 2}
      key-generator:
        type: SNOWFLAKE
        column: id
    user:
      ...

主从配置

master-slave-rules:
  ds0:
    master-data-source-name: ds0
    slave-data-source-names: ds0_slave
  ds1:
    master-data-source-name: ds1
    slave-data-source-names: ds1_slave

实体类和数据访问层:

定义 Order 和 User 实体类,以及对应的 JPA 仓库或 MyBatis 映射。

Order 实体类

@Entity
@Table(name = "order")
public class Order {
    @Id
    private Long id;

    @Column(name = "user_id")
 private Long userId;

    @Column(name = "order_amount")
    private BigDecimal orderAmount;

 // 标准的构造函数、getter 和 setter
    public Order() {
    }

    // ... 省略其他构造函数、getter 和 setter 方法

    // ... 可以添加业务逻辑方法
}

User 实体类

@Entity
@Table(name = "user")
public class User {
    @Id
    private Long id;

    @Column(name = "username")
    private String username;

    @Column(name = "email")
    private String email;

    // 标准的构造函数、getter 和 setter
    public User() {
    }

    // ... 省略其他构造函数、getter 和 setter 方法

    // ... 可以添加业务逻辑方法
}

OrderRepository 接口

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserId(Long userId);

    // ... 可以根据需要添加其他查询方法
}

UserRepository 接口

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);

    // ... 可以根据需要添加其他查询方法
}

补充一个mybatis的写法:

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);

    @Insert("INSERT INTO user (username, email) VALUES (#{username}, #{email})")
    void insert(User user);

    // 更多的 MyBatis SQL 映射可以根据需要添加
}

总结

ShardingJDBC 的源码实现体现了其作为一个数据库中间件框架的复杂性和灵活性。它将 SQL 解析、路由、改写、执行和结果集合并等多个步骤封装成一系列高度解耦的组件和接口。这种设计使得 ShardingJDBC 能够灵活地适应各种数据库和 SQL 方言,同时提供丰富的分片策略和读写分离功能。

以上就是使用ShardingJDBC进行数据分片以及读写分离的详细内容,更多关于ShardingJDBC数据分片的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文