8.sjdbc源码之执行

路由完成后就决定了SQL需要在哪些数据源的哪些实际表中执行,接下来以执行SQL:为例,分析sharding-jdbc是如何执行重写后的SQL语句。

生成PreparedStatementUnit

根据前面的路由分析知,示例SQL的路由结果如下:

因为user_id是分库键且值为10,所以路由到dataSource_jdbc_0这个数据源;order_id是分表键但是where条件没有这个字段,所以路由的实际表为[t_order_0, t_order_1];

得到路由结果后,在执行前,首先需要根据重写后的实际SQL生成PreparedStatementUnit:

执行SQL

核心源码在ShardingPreparedStatement.java中,示例SQL是查询类型,所以核心源码如下(更新类型SQL可以查看executeUpdate(),原理一样):

PreparedStatementExecutor.executeQuery()会调用ExecutorEngine.executePreparedStatement(),所以执行的核心代码在ExecutorEngine.java中,核心源码如下:

EventBus原理以及失败后重试后面会单独一篇文章进行分析。这段代码里通过EventBus发送的执行事件是整个执行批次的事件,即用户执行的逻辑SQL事件,不论逻辑SQL路由成多少条实际SQL,都是一个批次。执行前发送BEFORE_EXECUTE事件;执行后,如果失败发送EXECUTE_FAILURE事件,如果成功发送EXECUTE_SUCCESS事件。至于执行实际SQL的事件,在后面分析的executeInternal()方法中发送。

OverallExecutionEvent 有几个重要属性:

sqlType:即sql类型,例如DQL,还有DML,DDL;

statementUnitSize:即有多少条需要执行的SQL;

id:代表此次需要执行的批次ID,生成规则就是UUD;

eventExecutionType:执行时间类型,申明为BEFORE_EXECUTE类型;

第一个任务同步执行其他任务异步执行的原因

考虑到分库分表后只需路由到一个数据源中的一个表的SQL占大多数(如果不是,说明分库分表的sharding列选取有问题),例如用户表根据user_id分库分表后,根据user_id查询用户信息。这种SQL全部同步执行能节省线程开销。后面从sharding-jdbc的作者张亮大神那里得到了证实。

异步执行

异步执行线程池

除第一个任务外的其他任务通过线程池异步执行,线程池构造法方式如下:

异步执行核心

除第一个任务外的其他任务异步执行核心代码如下:

同步执行核心代码

执行核心

由同步执行核心代码和异步执行核心代码可知,最终都是调用,核心源码如下:

分析这段源码可知:最终就是在目标数据库的目标表上执行的方法;且在执行前会利用google-guava的EventBus发布BEFORE_EXECUTE的事件(执行完成后,如果执行成功还会发布EXECUTE_SUCCESS事件,如果执行失败发布EXECUTE_FAILURE事件)。

接下来需要对并行执行后得到的结果集进行merge,示例SQL路由到两个表上执行,所以有2个执行结果,接下来的一篇文章会分析sharding-jdbc如何结果结果归并的;

执行总结

通过上面对执行过程的源码分析可知,sharding-jdbc整个执行过程还是比较简单的,主要有以下几步:

任务分离,第1个任务同步执行,其他任务异步执行;

整个逻辑SQL任务会发送执行前(BEFORE_EXECUTE)事件,和执行结果(EXECUTE_SUCCESS或者EXECUTE_FAILURE)的事件。

每个实际SQL执行结果也会发送执行前事件和执行结果事件;

监听到EXECUTE_FAILURE即执行失败的时间,会对SQL进行重试;

将同步执行结果和异步执行结果合并到一个List中(结果合并就是合并这个List中每个表的执行结果);

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180830G0978900?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券