sharding-JDBC源碼分析(三)SQL路由

SQL router

分庫分表中重要的一個模塊就是數據切分,將數據根據必定的規則分佈在多個DB中,那麼這個過程當中涉及到了路由,即根據SQL中分片鍵經過規則(分片算法)計算出某個DB節點,這個過程稱爲SQL路由。web

The source code

sharding-JDBC路由入口統一在shardingStatementShardingPreparedStatement ,分析shardingStatement 源碼算法

private void shard(final String sql) {
 ShardingRuntimeContext runtimeContext = connection.getRuntimeContext();  //路由引擎,改引擎中處理非預編譯SQL,全部的非預編譯SQL路由都在這處理  SimpleQueryShardingEngine shardingEngine = new SimpleQueryShardingEngine(runtimeContext.getRule(),  runtimeContext.getProps(), runtimeContext.getMetaData(), runtimeContext.getDatabaseType(), runtimeContext.getParseEngine());  //根據路由引擎執行路由  sqlRouteResult = shardingEngine.shard(sql, Collections.emptyList());  } 複製代碼

sqlRouteResult 是根據路由結果構建的數據結構,整個執行流程會將sqlRouteResult 做爲參數傳遞。sql

shard方法執行路由邏輯數據結構

public SQLRouteResult shard(final String sql, final List<Object> parameters) {
 //拷貝預編譯參數,防止在路由過程當中對原始值修改  List<Object> clonedParameters = cloneParameters(parameters);  //route  SQLRouteResult result = executeRoute(sql, clonedParameters);  //根據路由結果構建可執行單元,這塊涉及到了SQL改寫,分表須要改寫SQL中的tableName ,好比原始表名acct,改寫後acct_1,acct_2等  result.getRouteUnits().addAll(HintManager.isDatabaseShardingOnly() ? convert(sql, clonedParameters, result) : rewriteAndConvert(sql, clonedParameters, result));  //日誌的方式輸出路由結果,包含SQL和db節點信息,方便問題追蹤  if (shardingProperties.getValue(ShardingPropertiesConstant.SQL_SHOW)) {  boolean showSimple = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SIMPLE);  SQLLogger.logSQL(sql, showSimple, result.getOptimizedStatement(), result.getRouteUnits());  }  return result;  } 複製代碼

executeRoute方法編輯器

private SQLRouteResult executeRoute(final String sql, final List<Object> clonedParameters) {
 //routingHook注入的鉤子方法,獲取路由過程當中的信息,此處用到的是SPI擴展技術,用戶可經過SPI將具體的鉤子實現注入,實現本身的邏輯,好比獲取路由執行時間等  routingHook.start(sql);  try {  //路由  SQLRouteResult result = route(sql, clonedParameters);  //執行鉤子中finishSuccess方法  routingHook.finishSuccess(result, metaData.getTable());  return result;  // CHECKSTYLE:OFF  } catch (final Exception ex) {  // CHECKSTYLE:ON  //執行鉤子中finishFailure方法  routingHook.finishFailure(ex);  throw ex;  }  } 複製代碼

route方法ide

//抽象方法,實現類有兩個 SimpleQueryShardingEngine和PreparedQueryShardingEngine,一個處理預編譯路由,另一個處理非預編譯SQL路由
protected abstract SQLRouteResult route(String sql, List<Object> parameters);  複製代碼

咱們分析非預編譯SQL路由SimpleQueryShardingEngineurl

public SimpleQueryShardingEngine(final ShardingRule shardingRule, final ShardingProperties shardingProperties,  final ShardingMetaData metaData, final DatabaseType databaseType, final SQLParseEngine sqlParseEngine) {  super(shardingRule, shardingProperties, metaData);  //StatementRoutingEngine 路由引擎   routingEngine = new StatementRoutingEngine(shardingRule, metaData, databaseType, sqlParseEngine);  }   @Override  protected List<Object> cloneParameters(final List<Object> parameters) {  return Collections.emptyList();  }   @Override  protected SQLRouteResult route(final String sql, final List<Object> parameters) {  //執行路由  return routingEngine.route(sql);  } 複製代碼
public SQLRouteResult route(final String logicSQL) {
 //sql 解析  SQLStatement sqlStatement = shardingRouter.parse(logicSQL, false);  //shardingRouter.route 根據SQL解析結果路由  return masterSlaveRouter.route(shardingRouter.route(logicSQL, Collections.emptyList(), sqlStatement));  } 複製代碼
public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {
 OptimizedStatement optimizedStatement = ShardingOptimizeEngineFactory.newInstance(sqlStatement).optimize(shardingRule, shardingMetaData.getTable(), logicSQL, parameters, sqlStatement);  //是否須要將子查詢分片值與外部查詢分片值進行合併,關於sharding-JDB中子查詢的分析,在個人另一篇文章中已經分析,有興趣的能夠去看看  boolean needMergeShardingValues = isNeedMergeShardingValues(optimizedStatement);  if (optimizedStatement instanceof ShardingConditionOptimizedStatement && needMergeShardingValues) {  //對子查詢的分片鍵進行檢查,主要肯定與外部是否同一個分片鍵  checkSubqueryShardingValues(optimizedStatement, ((ShardingConditionOptimizedStatement) optimizedStatement).getShardingConditions());  mergeShardingConditions(((ShardingConditionOptimizedStatement) optimizedStatement).getShardingConditions());  }  //路由,工廠模式RoutingEngineFactory建立路由算法,而後執行路由,返回RoutingResult  RoutingResult routingResult = RoutingEngineFactory.newInstance(shardingRule, shardingMetaData.getDataSource(), optimizedStatement).route();  if (needMergeShardingValues) {  //不支持跨分片子查詢  Preconditions.checkState(1 == routingResult.getRoutingUnits().size(), "Must have one sharding with subquery.");  }  if (optimizedStatement instanceof ShardingInsertOptimizedStatement) {  setGeneratedValues((ShardingInsertOptimizedStatement) optimizedStatement);  }  SQLRouteResult result = new SQLRouteResult(optimizedStatement);  result.setRoutingResult(routingResult);  return result;  } 複製代碼

主要是根據表配置的路由策略構建具體的路由算法實現類spa

shardingRule:
 tables:  t_order:  actualDataNodes: ds_${0..1}.t_order  #表路由策略  tableStrategy:  #路由算法類型  inline:  #算法參數  shardingColumn: order_id  algorithmExpression: t_order_${order_id % 2}  databaseStrategy:  inline:  shardingColumn: order_id  algorithmExpression: ds_${order_id % 2} 複製代碼

路由算法類型可有多種類型選擇,好比complex,standard等,同理能夠本身實現自定義算法,在算法參數中加入自定義路由實現類便可.日誌

sharding-JDBC同時也支持多個字段路由(多維度路由)code

conclusion

sharding-JDBC SQL router相對還算簡單,總體流程已經介紹,與預編譯的SQL路由不一樣之處是,預編譯須要提取?參數,有興趣的能夠翻閱源碼,後續會對SQL結果集合並作分析,包括內存合併和流式合併,這塊相對複雜些.

相關文章
相關標籤/搜索