sharding-jdbc(3.0.0)源碼解析-insert執(zhí)行流程分析

1.基礎(chǔ)介紹

  • sharing-jdbc是一個(gè)在客戶(hù)端的數(shù)據(jù)源層面實(shí)現(xiàn)分庫(kù)分表的中間件,對(duì)應(yīng)分析源碼首先要找到代碼執(zhí)行的入口笤喳,對(duì)于一個(gè)數(shù)據(jù)庫(kù)操作入口當(dāng)然是Statement的相關(guān)接口,所以我們應(yīng)該拋開(kāi)各種ORM框架,用原始的Statement來(lái)分析sharding-jdbc源碼
  • Statement是jdbc中用來(lái)執(zhí)行靜態(tài)sql,并得到返回的接口的抽象接口愉舔,本文主要介紹在sharding-jdbc中來(lái)實(shí)現(xiàn)Statement的實(shí)現(xiàn)類(lèi):ShardingStatement

2.總體概括

以下為官方網(wǎng)站提供的流程圖


image.png

3.看源碼

  1. 調(diào)用,基礎(chǔ)的調(diào)用方式回顧,此時(shí)的Statement就是ShardingStatement

        String sql="insert into t_order (name) VALUES  (\"我是4\")";
        Connection connection=dataSource.getConnection();
        // ShardingStatement 
        Statement statement=connection.createStatement();
        statement.executeUpdate(sql);
  1. ShardingStatement.executeUpdate;執(zhí)行更新的sql伙菜;重點(diǎn)的方法為sqlRoute,所以后續(xù)會(huì)主要分析這個(gè)方法
 @Override
    public int executeUpdate(final String sql) throws SQLException {
        try {
           //這一步是調(diào)用statementExecutor.clear方法轩缤,目的是清空上次執(zhí)行的語(yǔ)句,參數(shù),結(jié)果
            clearPrevious();
            //這個(gè)方法就實(shí)現(xiàn)了火的,SQL解析壶愤,查詢(xún)優(yōu)化,SQL路由馏鹤,SQL改寫(xiě)任務(wù)
            sqlRoute(sql);
            //賦值到StatementExecutor(一個(gè)用來(lái)執(zhí)行真正sql的包裝類(lèi))
            initStatementExecutor();
            //對(duì)多個(gè)庫(kù)進(jìn)行真正的更新操作
            return statementExecutor.executeUpdate();
        } finally {
            refreshTableMetaData();
            currentResultSet = null;
        }
    }
  1. ShardingStatement.sqlRoute;SQL解析征椒,查詢(xún)優(yōu)化,SQL路由湃累,SQL改寫(xiě)
private void sqlRoute(final String sql) {
    //獲取傳遞參數(shù)的上下文對(duì)象
        ShardingContext shardingContext = connection.getShardingContext();
//StatementRoutingEngine;用來(lái)封裝執(zhí)行分庫(kù)分表邏輯和主從邏輯勃救,此時(shí)調(diào)用此類(lèi)的route方法
        routeResult = new StatementRoutingEngine(shardingContext.getShardingRule(),
            shardingContext.getMetaData().getTable(), shardingContext.getDatabaseType(), shardingContext.isShowSQL(), shardingContext.getMetaData().getDataSource()).route(sql);
    }
  1. StatementRoutingEngine.route:里面就是調(diào)用了ShardingRouter.route方法,并將結(jié)果傳給ShardingMasterSlaveRouter.route方法
public SQLRouteResult route(final String logicSQL) {
       //解析SQL轉(zhuǎn)化為SQLStatement,表示這個(gè)SQL的對(duì)象類(lèi)
        SQLStatement sqlStatement = shardingRouter.parse(logicSQL, false);
      //首先調(diào)用ShardingRouter.route治力,得到SQLRouteResult:邏輯SQL經(jīng)過(guò)優(yōu)化蒙秒,路由,改寫(xiě)后的結(jié)果對(duì)象,如下圖
        return masterSlaveRouter.route(shardingRouter.route(logicSQL, Collections.emptyList(), sqlStatement));
    }

SQLRouteResult對(duì)象內(nèi)容:


image.png
  1. ParsingSQLRouter.parse:解析邏輯sql宵统,形成SQL執(zhí)行對(duì)象
public SQLStatement parse(final boolean useCache) {
        // SQL解析緩存,有則返回
        Optional<SQLStatement> cachedSQLStatement = getSQLStatementFromCache(useCache);
        if (cachedSQLStatement.isPresent()) {
            return cachedSQLStatement.get();
        }
        //詞法解析器
        LexerEngine lexerEngine = LexerEngineFactory.newInstance(dbType, sql);
        lexerEngine.nextToken();
       //語(yǔ)法解析結(jié)果
        SQLStatement result = SQLParserFactory.newInstance(dbType, lexerEngine.getCurrentToken().getType(), shardingRule, lexerEngine, shardingTableMetaData).parse();
       //添加緩存
        if (useCache) {
            ParsingResultCache.getInstance().put(sql, result);
        }
        return result;
    }
  1. ParsingSQLRouter.route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement):執(zhí)行查詢(xún)優(yōu)化晕讲,SQL路由,SQL改寫(xiě)榜田,當(dāng)時(shí)insert并且沒(méi)有手動(dòng)寫(xiě)入id時(shí)益兄,則此時(shí)會(huì)生成分布式ID
@Override
    public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {
        //判斷是否是insert,如果是則生成分布式主鍵ID
        GeneratedKey generatedKey = null;
        if (sqlStatement instanceof InsertStatement) {
            generatedKey = getGenerateKey(shardingRule, (InsertStatement) sqlStatement, parameters);
        }
       //初始化返回結(jié)果
        SQLRouteResult result = new SQLRouteResult(sqlStatement, generatedKey);
      //調(diào)用優(yōu)化引擎優(yōu)化SQL
        ShardingConditions shardingConditions = OptimizeEngineFactory.newInstance(shardingRule, sqlStatement, parameters, generatedKey).optimize();
       //賦值分布式主鍵
        if (null != generatedKey) {
            setGeneratedKeys(result, generatedKey);
        }
       //根據(jù)CRUD調(diào)用不同的路由引擎獲取應(yīng)該操作的物理庫(kù)和物理表,形成路由結(jié)果箭券,后續(xù)會(huì)分析該方法
        RoutingResult routingResult = route(sqlStatement, shardingConditions);
       //初始化重寫(xiě)引擎
        SQLRewriteEngine rewriteEngine = new SQLRewriteEngine(shardingRule, logicSQL, databaseType, sqlStatement, shardingConditions, parameters);
       //判斷是否路由到一個(gè)物理庫(kù)中
        boolean isSingleRouting = routingResult.isSingleRouting();
       //處理在路由到多個(gè)物理庫(kù)時(shí)處理limit語(yǔ)法
        if (sqlStatement instanceof SelectStatement && null != ((SelectStatement) sqlStatement).getLimit()) {
            processLimit(parameters, (SelectStatement) sqlStatement, isSingleRouting);
        }
        //按照路由結(jié)果重寫(xiě)SQL,生成物理庫(kù)可執(zhí)行的SQL
        SQLBuilder sqlBuilder = rewriteEngine.rewrite(!isSingleRouting);
        for (TableUnit each : routingResult.getTableUnits().getTableUnits()) {
            result.getRouteUnits().add(new RouteUnit(each.getDataSourceName(), rewriteEngine.generateSQL(each, sqlBuilder, shardingDataSourceMetaData)));
        }
        //是否打印最終的SQL
        if (showSQL) {
            SQLLogger.logSQL(logicSQL, sqlStatement, result.getRouteUnits());
        }
        return result;
    }

7.ParsingSQLRouter.route(final SQLStatement sqlStatement, final ShardingConditions shardingConditions):調(diào)用分庫(kù)分表策略生成分庫(kù)分表結(jié)果

private RoutingResult route(final SQLStatement sqlStatement, final ShardingConditions shardingConditions) {
        Collection<String> tableNames = sqlStatement.getTables().getTableNames();
        RoutingEngine routingEngine;
        if (sqlStatement instanceof UseStatement) {
            routingEngine = new IgnoreRoutingEngine();
        } else if (sqlStatement instanceof DDLStatement || (sqlStatement instanceof DCLStatement && ((DCLStatement) sqlStatement).isGrantForSingleTable())) {
            routingEngine = new TableBroadcastRoutingEngine(shardingRule, sqlStatement);
        } else if (sqlStatement instanceof ShowDatabasesStatement || sqlStatement instanceof ShowTablesStatement) {
            routingEngine = new DatabaseBroadcastRoutingEngine(shardingRule);
        } else if (sqlStatement instanceof DCLStatement) {
            routingEngine = new InstanceBroadcastRoutingEngine(shardingRule, shardingDataSourceMetaData);
        } else if (shardingConditions.isAlwaysFalse()) {
            routingEngine = new UnicastRoutingEngine(shardingRule, tableNames);
        } else if (sqlStatement instanceof DALStatement) {
            routingEngine = new UnicastRoutingEngine(shardingRule, tableNames);
        } else if (tableNames.isEmpty() && sqlStatement instanceof SelectStatement) {
            routingEngine = new UnicastRoutingEngine(shardingRule, tableNames);
        } else if (tableNames.isEmpty()) {
            routingEngine = new DatabaseBroadcastRoutingEngine(shardingRule);
        // CRUD語(yǔ)句會(huì)進(jìn)入下面其中一個(gè)
        } else if (1 == tableNames.size() || shardingRule.isAllBindingTables(tableNames) || shardingRule.isAllInDefaultDataSource(tableNames)) {
            routingEngine = new StandardRoutingEngine(shardingRule, tableNames.iterator().next(), shardingConditions);
        } else {
            // TODO config for cartesian set
            routingEngine = new ComplexRoutingEngine(shardingRule, tableNames, shardingConditions);
        }
        return routingEngine.route();
    }

StandardRoutingEngine.route:查看進(jìn)去會(huì)看到净捅,進(jìn)入到如下源碼

private Collection<DataNode> routeByShardingConditions(final TableRule tableRule) {
        Collection<DataNode> result = new LinkedList<>();
        if (shardingConditions.getShardingConditions().isEmpty()) {
            result.addAll(route(tableRule, Collections.<ShardingValue>emptyList(), Collections.<ShardingValue>emptyList()));
        } else {
            //獲取配置的分庫(kù)策略類(lèi),里面會(huì)調(diào)用我們的比如根據(jù)Id做hash的分庫(kù)算法等等
            ShardingStrategy dataBaseShardingStrategy = shardingRule.getDatabaseShardingStrategy(tableRule);
        
            ShardingStrategy tableShardingStrategy = shardingRule.getTableShardingStrategy(tableRule);
            for (ShardingCondition each : shardingConditions.getShardingConditions()) {
                List<ShardingValue> databaseShardingValues = isGettingShardingValuesFromHint(dataBaseShardingStrategy)
                        ? getDatabaseShardingValuesFromHint() : getShardingValues(dataBaseShardingStrategy.getShardingColumns(), each);
                List<ShardingValue> tableShardingValues = isGettingShardingValuesFromHint(tableShardingStrategy)
                        ? getTableShardingValuesFromHint() : getShardingValues(tableShardingStrategy.getShardingColumns(), each);
                Collection<DataNode> dataNodes = route(tableRule, databaseShardingValues, tableShardingValues);
                reviseShardingConditions(each, dataNodes);
                result.addAll(dataNodes);
            }
        }
        return result;
    }

4.總結(jié)

此文是一個(gè)簡(jiǎn)單的流程分析辩块,希望幫助大家蛔六,對(duì)整個(gè)流程有個(gè)認(rèn)知,為后續(xù)關(guān)鍵節(jié)點(diǎn)的實(shí)現(xiàn)有個(gè)整體的概覽

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末废亭,一起剝皮案震驚了整個(gè)濱河市国章,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豆村,老刑警劉巖液兽,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掌动,居然都是意外死亡四啰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)粗恢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柑晒,“玉大人,你說(shuō)我怎么就攤上這事眷射〕自蓿” “怎么了佛掖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)涌庭。 經(jīng)常有香客問(wèn)我芥被,道長(zhǎng),這世上最難降的妖魔是什么脾猛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任撕彤,我火速辦了婚禮,結(jié)果婚禮上猛拴,老公的妹妹穿的比我還像新娘羹铅。我一直安慰自己,他們只是感情好愉昆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布职员。 她就那樣靜靜地躺著,像睡著了一般跛溉。 火紅的嫁衣襯著肌膚如雪焊切。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天芳室,我揣著相機(jī)與錄音专肪,去河邊找鬼。 笑死堪侯,一個(gè)胖子當(dāng)著我的面吹牛嚎尤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伍宦,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼芽死,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了次洼?” 一聲冷哼從身側(cè)響起关贵,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卖毁,沒(méi)想到半個(gè)月后揖曾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亥啦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年炭剪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禁悠。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡念祭,死狀恐怖兑宇,靈堂內(nèi)的尸體忽然破棺而出碍侦,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布瓷产,位于F島的核電站站玄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏濒旦。R本人自食惡果不足惜株旷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尔邓。 院中可真熱鬧晾剖,春花似錦华临、人聲如沸崖叫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灯节。三九已至循头,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炎疆,已是汗流浹背卡骂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留形入,地道東北人全跨。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像唯笙,于是被迫代替她去往敵國(guó)和親螟蒸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容