天天看点

Sharding-JDBC 工作流程

Sharding-JDBC 工作流程

一、概述

Sharding-JDBC 的原理总结起来很简单:

SQL 解析 => 执行器优化 => SQL 路由 => SQL 改写 => SQL 执行 => 结果归并。

Sharding-JDBC 工作流程

SQL 解析

SQL 解析主要是词法和语法的解析。Sharding-JDBC1.4.x 之前的版本使用 Druid 作为 SQL 解析器。从 1.5.x 版本开始,Sharding-JDBC 采用完全自研的 SQL 解析引擎。

SQL 路由

Sharding-JDBC 工作流程

SQL 路由是根据分片规则配置以及解析上下文中的分片条件,将 SQL 定位至真正的数据源。它又分为直接路由(使用 Hint )、简单路由和笛卡尔积路由

SQL 改写

将逻辑表名称改成真实表名称,优化分页查询。

SQL 执行

因为可能链接到多个真实数据源, Sharding -JDBC 将采用多线程并发执行 SQL。

结果归并

数据的组装、分页、排序等等。

二、Sharding-JDBC 实现原理

ShardingJDBC 在执行过程中,主要是三个环节:

  • 第一个是解析配置文件。
  • 第二个是对 SQL 进行解析、路由和改写。
  • 第三个把结果集汇总到一起返回给客户端。

之前讲过说 Sharding-JDBC 是一个增强版的 JDBC 驱动,JDBC 的四大核心对象:DataSource、Connection、Statement(PS)、ResulstSet。

Sharding-JDBC 实现了这四个核心接口,在类名前面加上了 Sharding。ShardingDataSource 、 ShardingConnection 、 ShardingStatement ( PS ) 、ShardingResulstSet。

如果要在 Java 代码操作数据库的过程里面,实现各种各样的逻辑,肯定是要从数据源就开始替换成自己的实现。当然,因为在配置文件里面配置了数据源,启动的时候就创建好了。下面用 核心就是ShardingDataSource 获 取 ShardingConnection 。

MyBatis 源码的查询方法最终会走到SimpleExecutor 的 doQuery()方法,doQuery() 方 法 里 面 调 用 了 prepareStatement() 创建连接 , 通 过ShardingDataSource 返回连接

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
           

它经过以下两个方法,返回了一个 ShardingConnection,

DataSourceUtil.fetchConnection()
Connection con = dataSource.getConnection();
           

基于这个 ShardingConnection,最终得到一个 ShardingPreparedStatement,

然后就是SQL执行

return handler.query(stmt, resultHandler);

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
           

最终调用的是 ShardingPreparedStatement 的 execute 方法。

public boolean execute() throws SQLException {
try {
clearPrevious();
prepare();
initPreparedStatementExecutor();
return preparedStatementExecutor.execute();
} finally {
refreshTableMetaData(connection.getShardingContext(), routeResult.getSqlStatement());
clearBatch();
}
}
           

prepare() 方法中,

prepareEngine.prepare

调用

执行路由

private RouteContext executeRoute(String sql, List<Object> clonedParameters) {
this.registerRouteDecorator();
return this.route(this.router, sql, clonedParameters);
}
           

最后到相应的路由执行引擎。