简介

在前几篇文中,我们基于源码就ShardingSphere的核心功能给运行了一遍,本篇文章开始,我们开始探索源码,看看ShardingSphere是如何进行工作的

概览

开始之前,我们先思考这次探索的疑点:

  • 1.ShardingSphere是如何加载我们配置的多个数据库源的?
  • 2.ShardingSphere是如何将语句写到不同的数据源的?

带着问题去探索,虽然可能得不到答案,但起码有个方向

基于我们JDBC探索的文章,打上相关的断点,开始我们的探索之旅

  • ShardingSphere JDBC 分库分表 读写分离 数据加密

源码探索

1.寻找ShardingSphere JDBC 的入口

在下面代码中的init和process打上断点,看看能不能通过程序调用栈找到蛛丝马迹

public final class ExampleExecuteTemplate {public static void run(final ExampleService exampleService) throws SQLException {try {exampleService.initEnvironment();exampleService.processSuccess();} finally {exampleService.cleanEnvironment();}}
}

exampleService.initEnvironment() 进来以后,调用栈空空如也,啥也没有,看来我们第一个探索如果加载多个配置的数据源的目的告吹了

但还是继续Debug下去,竟然全是Ibatis相关的东西,没有Debug到ShardingSphere相关的代码,安慰自己,正常现象

继续探索:exampleService.processSuccess(),来到下面的相关语句的插入、查询等相关的操作,在insertData()打上断点

public class OrderServiceImpl implements ExampleService {@Override@Transactionalpublic void processSuccess() throws SQLException {System.out.println("-------------- Process Success Begin ---------------");List<Long> orderIds = insertData();printData();deleteData(orderIds);printData();System.out.println("-------------- Process Success Finish --------------");}
}

基于之前尝试没有收获,这次不得不小心翼翼,跟着函数调用不断的深入下去

MapperProxy.class,执行下面的方法,进行跟下去:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}if (this.isDefaultMethod(method)) {return this.invokeDefaultMethod(proxy, method, args);}} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}MapperMethod mapperMethod = this.cachedMapperMethod(method);// 从这里进入return mapperMethod.execute(this.sqlSession, args);}

来到下面的函数

    public Object execute(SqlSession sqlSession, Object[] args) {Object result;Object param;switch(this.command.getType()) {case INSERT:param = this.method.convertArgsToSqlCommandParam(args);// 从这里进入,跟踪insert方法result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));break;case UPDATE:......}

其中我们看到了熟悉的数据库相关的参数:SqlSession sqlSession,我们调用堆栈,查看其内容,如下图:

数据库名称竟然是 logic_db,想到秦老师讲课时说ShardingSphere UI中也能用JDBC,只是它的名称是 logic_db

此时有了一些猜测,但得继续跟踪下去,证实下

来到了:SqlSessionTemplate.class,其中重新获取了一次 sqlSession,但其实还是logic_db,继续跟

private class SqlSessionInterceptor implements InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);Object unwrapped;try {// 这里继续跟下去Object result = method.invoke(sqlSession, args);if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {sqlSession.commit(true);}unwrapped = result;} catch (Throwable var11) {......} finally {......}return unwrapped;}}

一路上遇到很多的 update 操作,我们进入跟下去即可,最后我们来到:PreparedStatementHandler.class

public class PreparedStatementHandler extends BaseStatementHandler {public int update(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement)statement;// 执行这里很关键了ps.execute();int rows = ps.getUpdateCount();Object parameterObject = this.boundSql.getParameterObject();KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();keyGenerator.processAfter(this.executor, this.mappedStatement, ps, parameterObject);return rows;}
}

在上面的:ps.execute(),我们跟下去的时候,惊喜就来了,砰的一下,就来到了ShardingSphere自己写的类中:ShardingSpherePreparedStatement.java

到这里,对于ShardingSphere JDBC有了一些想法:

ShardingSphere JDBC 感觉其实本质上还有一个中间件,内嵌式的,同样是介于程序与数据库之间,在本地探索中,它就以一个 logic_db ,最为 Mybatis的下游,将所有的数据库进行获取截断处理

初步有这么个体会,后面随着研究的深入可能会有更多的发现,到这我们就找到了ShardingSphere的入口了

2.初步探索ShardingSphere JDBC 处理路径

在上面我们来到了这次找到的入口:ShardingSpherePreparedStatement.java

    @Overridepublic boolean execute() throws SQLException {try {clearPrevious();// 这里有了真实的数据库源和语句executionContext = createExecutionContext();if (metaDataContexts.getMetaData(connection.getSchemaName()).getRuleMetaData().getRules().stream().anyMatch(each -> each instanceof RawExecutionRule)) {// TODO process getStatementCollection<ExecuteResult> executeResults = rawExecutor.execute(createRawExecutionGroupContext(), executionContext.getLogicSQL(), new RawSQLExecutorCallback());return executeResults.iterator().next() instanceof QueryResult;}if (executionContext.getRouteContext().isFederated()) {List<QueryResult> queryResults = executeFederatedQuery();return !queryResults.isEmpty();}// 继续跟踪ExecutionGroupContext<JDBCExecutionUnit> executionGroupContext = createExecutionGroupContext();cacheStatements(executionGroupContext.getInputGroups());return driverJDBCExecutor.execute(executionGroupContext,executionContext.getLogicSQL(), executionContext.getRouteContext().getRouteUnits(), createExecuteCallback());} finally {clearBatch();}}

executionContext = createExecutionContext() 有原始的语句,还是我们定时真实的数据源和SQL语句,如下图,我们继续跟下去:

来到:AbstractExecutionPrepareEngine.java,前面其实已经能得到展示的执行语句,目前还不太清楚这步的目的是啥

    public final ExecutionGroupContext<T> prepare(final RouteContext routeContext, final Collection<ExecutionUnit> executionUnits) throws SQLException {Collection<ExecutionGroup<T>> result = new LinkedList<>();for (Entry<String, List<SQLUnit>> entry : aggregateSQLUnitGroups(executionUnits).entrySet()) {String dataSourceName = entry.getKey();List<SQLUnit> sqlUnits = entry.getValue();List<List<SQLUnit>> sqlUnitGroups = group(sqlUnits);ConnectionMode connectionMode = maxConnectionsSizePerQuery < sqlUnits.size() ? ConnectionMode.CONNECTION_STRICTLY : ConnectionMode.MEMORY_STRICTLY;result.addAll(group(dataSourceName, sqlUnitGroups, connectionMode));}return decorate(routeContext, result);}

回到 ShardingSpherePreparedStatement.java,跟踪 driverJDBCExecutor.execute,来到:DriverJDBCExecutor.java

   public boolean execute(final ExecutionGroupContext<JDBCExecutionUnit> executionGroupContext, final LogicSQL logicSQL,final Collection<RouteUnit> routeUnits, final JDBCExecutorCallback<Boolean> callback) throws SQLException {try {ExecuteProcessEngine.initialize(logicSQL, executionGroupContext, metaDataContexts.getProps());List<Boolean> results = jdbcLockEngine.execute(executionGroupContext, logicSQL.getSqlStatementContext(), routeUnits, callback);boolean result = null != results && !results.isEmpty() && null != results.get(0) && results.get(0);ExecuteProcessEngine.finish(executionGroupContext.getExecutionID());return result;} finally {ExecuteProcessEngine.clean();}}

有趣的是 jdbcLockEngine 还是 logic_db,继续跟踪execute函数,一路跟下去来到: ExecutorEngine.java

    public <I, O> List<O> execute(final ExecutionGroupContext<I> executionGroupContext,final ExecutorCallback<I, O> firstCallback, final ExecutorCallback<I, O> callback, final boolean serial) throws SQLException {if (executionGroupContext.getInputGroups().isEmpty()) {return Collections.emptyList();}return serial ? serialExecute(executionGroupContext.getInputGroups().iterator(), firstCallback, callback): parallelExecute(executionGroupContext.getInputGroups().iterator(), firstCallback, callback);}private <I, O> List<O> serialExecute(final Iterator<ExecutionGroup<I>> executionGroups, final ExecutorCallback<I, O> firstCallback, final ExecutorCallback<I, O> callback) throws SQLException {ExecutionGroup<I> firstInputs = executionGroups.next();List<O> result = new LinkedList<>(syncExecute(firstInputs, null == firstCallback ? callback : firstCallback));while (executionGroups.hasNext()) {// 继续跟踪result.addAll(syncExecute(executionGroups.next(), callback));}return result;}

一路跟踪下去,来到: JDBCExecutorCallback.java

    public final Collection<T> execute(final Collection<JDBCExecutionUnit> executionUnits, final boolean isTrunkThread, final Map<String, Object> dataMap) throws SQLException {// TODO It is better to judge whether need sane result before execute, can avoid exception thrownCollection<T> result = new LinkedList<>();for (JDBCExecutionUnit each : executionUnits) {T executeResult = execute(each, isTrunkThread, dataMap);if (null != executeResult) {result.add(executeResult);}}return result;}private T execute(final JDBCExecutionUnit jdbcExecutionUnit, final boolean isTrunkThread, final Map<String, Object> dataMap) throws SQLException {SQLExecutorExceptionHandler.setExceptionThrown(isExceptionThrown);// 在这里得到了我们真实的数据库源DataSourceMetaData dataSourceMetaData = getDataSourceMetaData(jdbcExecutionUnit.getStorageResource().getConnection().getMetaData());SQLExecutionHook sqlExecutionHook = new SPISQLExecutionHook();try {SQLUnit sqlUnit = jdbcExecutionUnit.getExecutionUnit().getSqlUnit();sqlExecutionHook.start(jdbcExecutionUnit.getExecutionUnit().getDataSourceName(), sqlUnit.getSql(), sqlUnit.getParameters(), dataSourceMetaData, isTrunkThread, dataMap);// 继续跟踪执行语句T result = executeSQL(sqlUnit.getSql(), jdbcExecutionUnit.getStorageResource(), jdbcExecutionUnit.getConnectionMode());sqlExecutionHook.finishSuccess();finishReport(dataMap, jdbcExecutionUnit);return result;} catch (final SQLException ex) {......}}

在上面的代码中,找到直接获取真实数据库源相关的代码

我们继续跟踪执行的相关代码: ShardingSpherePreparedStatement.java , 来到

   private JDBCExecutorCallback<Boolean> createExecuteCallback() {boolean isExceptionThrown = SQLExecutorExceptionHandler.isExceptionThrown();return new JDBCExecutorCallback<Boolean>(metaDataContexts.getMetaData(connection.getSchemaName()).getResource().getDatabaseType(), sqlStatement, isExceptionThrown) {@Overrideprotected Boolean executeSQL(final String sql, final Statement statement, final ConnectionMode connectionMode) throws SQLException {return ((PreparedStatement) statement).execute();}@Overrideprotected Optional<Boolean> getSaneResult(final SQLStatement sqlStatement) {return Optional.empty();}};}

其中的: Statement statement ,如果用原生的 JDBC 自己写过操作数据库,那应该比较数据,那语句执行到这里就基本告一个段落了

总结

这次感觉收获还是挺大,虽然相关的数据库源的解析和语句如何打到对应的数据库的部分没有探索,但我们已经定位到了其大致的位置,可以后面继续研究

ShardingSphere JDBC 在本次探索中,感觉就是一个内嵌的 Proxy ,其数据库库名固定为 logic_db,截断了 Mybatis与真实数据库的链接,当前了中间商,大致如下图:

ShardingSphere JDBC 语句执行初探相关推荐

  1. mysql:通过JDBC接口执行创建触发器的SQL语句

    delimiter 以下是从mysql官方文档<23.3.1 Trigger Syntax and Examples>抄来的一段创建触发器的SQL脚本, delimiter // CREA ...

  2. ShardingSphere JDBC 分库分表 读写分离 数据加密

    简介 在上篇文章中,在本地搭建了运行环境,本地来体验下ShardingSphere JDBC的三个功能:分库分表.读写分离.数据加密 示例运行 首先把概念先捋一捋,参考下面的文档: 数据分片 读写分离 ...

  3. Oracle SQL语句执行步骤

    Oracle中SQL语句执行过程中,Oracle内部解析原理如下: 1.当一用户第一次提交一个SQL表达式时,Oracle会将这SQL进行Hard parse,这过程有点像程序编译,检查语法.表名.字 ...

  4. statement执行insert into语句_【图文并茂】源码解析MyBatis ShardingJdbc SQL语句执行流程详解...

    源码分析Mybatis系列目录: 1.源码分析Mybatis MapperProxy初始化[图文并茂] 2.源码分析Mybatis MappedStatement的创建流程 3.[图文并茂]Mybat ...

  5. jdbc 批量执行sql

    最近有个需求是需要在java 后端执行导入,数据量比较大, 需要对数据进行很多操作,最后要执行插入数据操作, 一开始先组织好插入数据的sql语句放在数组中,使用的是 jdbcTemplate.batc ...

  6. oracle中子查询的执行顺序是,Oracle sql语句执行顺序

    sql语法的分析是从右到左 一.sql语句的执行步骤: 1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义. 2)语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限. ...

  7. 详解MySQL的逻辑架构和SQL语句执行流程

    文章目录 1. 逻辑架构 1.1 连接层 1.2 服务层 1.3 引擎层 1.3.1 InnoDB 存储引擎 1.3.2 MyISAM 存储引擎 1.3.3 其他存储引擎 1.4 存储层 3. SQL ...

  8. Oracle sql语句执行顺序

    一.sql语句的执行步骤: 1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义. 2)语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限. 3)视图转换,将涉及视图的 ...

  9. 100% 展示 MySQL 语句执行的神器-Optimizer Trace

    在上一篇文章<用Explain 命令分析 MySQL 的 SQL 执行>中,我们讲解了 Explain 命令的详细使用.但是它只能展示 SQL 语句的执行计划,无法展示为什么一些其他的执行 ...

最新文章

  1. vmware响应时间过长_性能调优高并发下如何缩短响应时间
  2. 领导力,就是这5个问题
  3. 主流Java数据库连接池比较及前瞻
  4. 如何判断浏览器的请求头是不是结束
  5. 【存储知识学习】第八章-Fibre Channel协议《大话存储》阅读笔记
  6. python模块分类_Python-模块分类及导入
  7. Linux下启动、停止J2SE程序(脚本)
  8. 如何配置 strongSwan 客户端 -- 节选自 OpenSuSE 中文用户手册
  9. delphi操作word
  10. Spring学习(一)Spring简介、SpringIOC
  11. 【ECG实践篇(1)】MIT-BIH数据库数据解析的方法以及使用rdann获取人工标注注释的方法
  12. Windows下使用HDFView查看hdf5文件
  13. 人大金仓数据库Centos 7 部署
  14. 说说技术总监的三板斧(十年肺腑之言)
  15. wpa.b.qq.com/cgi/wpa.php,营销QQ在线客服代码生成的方法(支持手机和PC)
  16. go学习 --- go协程
  17. AHP层析分析法初步讲解
  18. SQL注入漏洞[OWASP TOP 1]
  19. FVCOM三维水动力、水交换、溢油物质扩散及输运数值模拟丨FVCOM模型流域、海洋水环境数值模拟方法
  20. JPEG2000压缩DICOM文件的解压(一)

热门文章

  1. SymPy库常用函数
  2. Html5-canvas
  3. WinForm里ListBox实现加入项目,并且排序。
  4. Duplicate interface definition for class解决方法
  5. windows下用pip安装软件超时解决方案
  6. 【方案分享】地产项目2022年新春1月系列暖场活动策划方案:新年置业,如虎添翼.pptx(附下载链接)...
  7. 【白皮书分享】技术重构社会供应链:未来科技趋势白皮书.pdf(附下载链接)...
  8. 如何嫁给改变世界的男人:程序员理想女友大调查
  9. 迭代最近点算法Iterative Closest Point(ICP)以及c++实现代码
  10. 第一周周冠军带你解析赛题,尝试广告算法新思路