分庫分表中重要的一個模塊就是數據切分,將數據根據必定的規則分佈在多個DB中,那麼這個過程當中涉及到了路由,即根據SQL中分片鍵經過規則(分片算法)計算出某個DB節點,這個過程稱爲SQL路由。web
sharding-JDBC路由入口統一在shardingStatement
和ShardingPreparedStatement
,分析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路由SimpleQueryShardingEngine
url
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
sharding-JDBC SQL router相對還算簡單,總體流程已經介紹,與預編譯的SQL路由不一樣之處是,預編譯須要提取?參數,有興趣的能夠翻閱源碼,後續會對SQL結果集合並作分析,包括內存合併和流式合併,這塊相對複雜些.