最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

sharding-jdbc中的事務(wù)詳細(xì)解讀

 更新時(shí)間:2023年12月14日 10:41:50   作者:那個(gè)天真的人  
這篇文章主要介紹了sharding-jdbc中的事務(wù)詳細(xì)解讀,sharding-jdbc在分庫分表方面提供了很大的便利性,在使用DB的時(shí)候,通常都會(huì)涉及到事務(wù)這個(gè)概念,而在分庫分表的環(huán)境上再加上事務(wù),就會(huì)使事情變得復(fù)雜起來,需要的朋友可以參考下

序言

sharding-jdbc在分庫分表方面提供了很大的便利性

在使用DB的時(shí)候,通常都會(huì)涉及到事務(wù)這個(gè)概念,而在分庫分表的環(huán)境上再加上事務(wù),就會(huì)使事情變得復(fù)雜起來。

本章試圖剖析sharding-jdbc在事務(wù)方面的解決思路。

傳統(tǒng)事務(wù)回顧

傳統(tǒng)的事務(wù)模型如下:

Connection conn = getConnection();
try{
    Statement stmt1 = conn.parpareStatement(sql1);
    stmt1.executeUpdate();
    Statement stmt2 = conn.parpareStatement(sql2);
    stmt2.executeUpdate();
    conn.commit();
}catch(Exception e){
    conn.rollback();
}

對(duì)于同一個(gè)連接,可以執(zhí)行多條sql語句,任何一條語句出現(xiàn)錯(cuò)誤的時(shí)候,整個(gè)操作流程都可以回滾,從而達(dá)到事務(wù)的原子操作。

再來看最基本的spring事務(wù)操作:

class ServiceA(){
   public void updateA(){...}
}
class ServiceB(){
    public void updateB(){...}
}
@Transactional
class ServiceC(){
    public void updateC(){
        serviceA.updateA();
        serviceB.updateB();
    }
}

我們知道,當(dāng)updateC執(zhí)行的時(shí)候,不管是updateA還是updateB出現(xiàn)了異常,updateC都可以整體回滾,達(dá)到原子操作的效果,其主要原因是updateA和updateB共享了同一個(gè)Connection,這是spring底層通過ThreadLocal緩存了Connection實(shí)現(xiàn)的。

以上介紹的這兩種情況都只是針對(duì)單庫單表的原子操作,事務(wù)的實(shí)現(xiàn)并不難理解,那么在跨庫的情況下,sharding-jdbc又是如何解決事務(wù)問題的呢?

shrading-jdbc之弱事務(wù)

在官方文檔中,針對(duì)弱事務(wù)有如下三點(diǎn)說明:

  • 完全支持非跨庫事務(wù),例如:僅分表,或分庫但是路由的結(jié)果在單庫中。
  • 完全支持因邏輯異常導(dǎo)致的跨庫事務(wù)。例如:同一事務(wù)中,跨兩個(gè)庫更新。更新完畢后,拋出空指針,則兩個(gè)庫的內(nèi)容都能回滾。
  • 不支持因網(wǎng)絡(luò)、硬件異常導(dǎo)致的跨庫事務(wù)。例如:同一事務(wù)中,跨兩個(gè)庫更新,更新完畢后、未提交之前,第一個(gè)庫死機(jī),則只有第二個(gè)庫數(shù)據(jù)提交。

為了理解以上幾點(diǎn),我們來看看sharding-jdbc默認(rèn)是如何處理事務(wù)的。

這里寫圖片描述

這是一個(gè)非常常見的處理模式,一個(gè)總連接處理了多條sql語句,最后一次性提交整個(gè)事務(wù),每一條sql語句可能會(huì)分為多條子sql分庫分表去執(zhí)行,這意味著底層可能會(huì)關(guān)聯(lián)多個(gè)真正的數(shù)據(jù)庫連接,我們先來看看如果一切正常,commit會(huì)如何去處理。

public abstract class AbstractConnectionAdapter extends AbstractUnsupportedOperationConnection {
    @Override
    public final void commit() throws SQLException {
        Collection<SQLException> exceptions = new LinkedList<>();
        for (Connection each : cachedConnections.values()) {
            try {
                each.commit();
            } catch (final SQLException ex) {
                exceptions.add(ex);
            }
        }
        throwSQLExceptionIfNecessary(exceptions);
    }
}

引擎會(huì)遍歷底層所有真正的數(shù)據(jù)庫連接,一個(gè)個(gè)進(jìn)行commit操作,如果任何一個(gè)出現(xiàn)了異常,直接捕獲異常,但是也只是捕獲而已,然后接著下一個(gè)連接的commit,這也就很好的說明了,如果在執(zhí)行任何一條sql語句出現(xiàn)了異常,整個(gè)操作是可以原子性回滾的,因?yàn)榇藭r(shí)所有連接都不會(huì)執(zhí)行commit,但如果已經(jīng)到了commit這一步的話,如果有連接commit失敗了,是不會(huì)影響到其他連接的。

sharding-jdbc之柔性事務(wù)

sharding-jdbc的弱事務(wù)并不是完美的,有時(shí)可能會(huì)導(dǎo)致數(shù)據(jù)的一致性問題,所以針對(duì)某些特定的場景,又提出了柔性事務(wù)的概念。先來看一張官方的說明圖:

這里寫圖片描述

這里想表達(dá)兩個(gè)意思:

1. 對(duì)于sql的執(zhí)行,在執(zhí)行前記錄日志,如果執(zhí)行成功,把日志刪除,如果執(zhí)行失敗,重試一定次數(shù)(如果未達(dá)到最大嘗試次數(shù)便執(zhí)行成功了,一樣刪除日志)。

2. 異步任務(wù)不斷掃描執(zhí)行日志,如果重試次數(shù)未達(dá)到最大上限,嘗試重新執(zhí)行,如果執(zhí)行成功,刪除日志。

從上面兩點(diǎn)分析可以看出,由于采用的是重試的模式,也就是說同一條語句,是有可能被多次執(zhí)行的,所以官方提到了柔性事務(wù)的適用場景:

  • 根據(jù)主鍵刪除數(shù)據(jù)。
  • 更新記錄永久狀態(tài),如更新通知送達(dá)狀態(tài)。

而且它還有一定的限制: SQL需要滿足冪等性,具體為:

  • INSERT語句要求必須包含主鍵,且不能是自增主鍵。
  • UPDATE語句要求冪等,不能是UPDATE xxx SET x=x+1
  • DELETE語句無要求。

在有了一個(gè)大概的了解之后,我們來更加深入的了解。

sharding-jdbc使用了google的EventBus事件模型,注冊(cè)了一個(gè)Listener,監(jiān)聽器對(duì)三種事件進(jìn)行了處理,如下代碼所示:

switch (event.getEventExecutionType()) {
            case BEFORE_EXECUTE:
                transactionLogStorage.add(new TransactionLog(event.getId(), bedSoftTransaction.getTransactionId(), bedSoftTransaction.getTransactionType(), 
                        event.getDataSource(), event.getSql(), event.getParameters(), System.currentTimeMillis(), 0));
                return;
            case EXECUTE_SUCCESS: 
                transactionLogStorage.remove(event.getId());
                return;
            case EXECUTE_FAILURE: 
                boolean deliverySuccess = false;
                for (int i = 0; i < transactionConfig.getSyncMaxDeliveryTryTimes(); i++) {
                    if (deliverySuccess) {
                        return;
                    }
                    boolean isNewConnection = false;
                    Connection conn = null;
                    PreparedStatement preparedStatement = null;
                    try {
                        conn = bedSoftTransaction.getConnection().getConnection(event.getDataSource(), SQLType.DML);
                        if (!isValidConnection(conn)) {
                            bedSoftTransaction.getConnection().release(conn);
                            conn = bedSoftTransaction.getConnection().getConnection(event.getDataSource(), SQLType.DML);
                            isNewConnection = true;
                        }
                        preparedStatement = conn.prepareStatement(event.getSql());
                        //TODO for batch event need split to 2-level records
                        for (int parameterIndex = 0; parameterIndex < event.getParameters().size(); parameterIndex++) {
                            preparedStatement.setObject(parameterIndex + 1, event.getParameters().get(parameterIndex));
                        }
                        preparedStatement.executeUpdate();
                        deliverySuccess = true;
                        transactionLogStorage.remove(event.getId());
                    } catch (final SQLException ex) {
                        log.error(String.format("Delivery times %s error, max try times is %s", i + 1, transactionConfig.getSyncMaxDeliveryTryTimes()), ex);
                    } finally {
                        close(isNewConnection, conn, preparedStatement);
                    }
                }
                return;
            default: 
                throw new UnsupportedOperationException(event.getEventExecutionType().toString());
        }

以上代碼可以抽取為如下圖的描述:

這里寫圖片描述

監(jiān)聽器根據(jù)三種不同的事件類型對(duì)事務(wù)日志進(jìn)行不同的操作。有監(jiān)聽 ,必然就有事件的投遞,那么引擎是什么時(shí)候產(chǎn)生這些事件的呢? 我們知道每一條sql語句拆分后有可能對(duì)應(yīng)多條子sql語句,而每一條子sql語句是單獨(dú)執(zhí)行的,執(zhí)行是封裝在一個(gè)內(nèi)部方法的:

private <T> T executeInternal(final SQLType sqlType, final BaseStatementUnit baseStatementUnit, final List<List<Object>> parameterSets, final ExecuteCallback<T> executeCallback, 
                          final boolean isExceptionThrown, final Map<String, Object> dataMap) throws Exception {
        synchronized (baseStatementUnit.getStatement().getConnection()) {
            T result;
            ExecutorExceptionHandler.setExceptionThrown(isExceptionThrown);
            ExecutorDataMap.setDataMap(dataMap);
            List<AbstractExecutionEvent> events = new LinkedList<>();
            if (parameterSets.isEmpty()) {
                events.add(getExecutionEvent(sqlType, baseStatementUnit, Collections.emptyList()));
            }
            for (List<Object> each : parameterSets) {
                events.add(getExecutionEvent(sqlType, baseStatementUnit, each));
            }
            for (AbstractExecutionEvent event : events) {
                EventBusInstance.getInstance().post(event);
            }
            try {
                result = executeCallback.execute(baseStatementUnit);
            } catch (final SQLException ex) {
                for (AbstractExecutionEvent each : events) {
                    each.setEventExecutionType(EventExecutionType.EXECUTE_FAILURE);
                    each.setException(Optional.of(ex));
                    EventBusInstance.getInstance().post(each);
                    ExecutorExceptionHandler.handleException(ex);
                }
                return null;
            }
            for (AbstractExecutionEvent each : events) {
                each.setEventExecutionType(EventExecutionType.EXECUTE_SUCCESS);
                EventBusInstance.getInstance().post(each);
            }
            return result;
        }
    }

以上代碼可以簡化為如下流程:

這里寫圖片描述

其實(shí)執(zhí)行流程比較簡單,但還有兩個(gè)重要的細(xì)節(jié)這里沒有體現(xiàn):

  1. 當(dāng)使用柔性事務(wù)的時(shí)候,需要?jiǎng)?chuàng)建事務(wù)管理器,并獲取事務(wù)對(duì)象,調(diào)用事務(wù)對(duì)象的begin開始一個(gè)事務(wù),在這一步,會(huì)強(qiáng)制設(shè)置連接的autoCommit=true,這會(huì)導(dǎo)致所有的sql語句執(zhí)時(shí)后立即提交,想想如果能回滾,那柔性事務(wù)也就失去了意義。
  2. 當(dāng)事務(wù)執(zhí)行begin時(shí),會(huì)標(biāo)記當(dāng)前不拋出異常,這樣當(dāng)執(zhí)行sql語句有異常時(shí),會(huì)生成相應(yīng)的EXECUTE_FAILURE事件,從而進(jìn)行事務(wù)日志處理,而不是往外拋出異常,當(dāng)事務(wù)結(jié)束時(shí),調(diào)用事務(wù)對(duì)象的end方法,恢復(fù)異常的捕獲。

一個(gè)常見的代碼編寫模式如下(來自官方的demo)

private static void updateFailure(final DataSource dataSource) throws SQLException {
        String sql1 = "UPDATE t_order SET status='UPDATE_1' WHERE user_id=10 AND order_id=1000";
        String sql2 = "UPDATE t_order SET not_existed_column=1 WHERE user_id=1 AND order_id=?";
        String sql3 = "UPDATE t_order SET status='UPDATE_2' WHERE user_id=10 AND order_id=1000";
        SoftTransactionManager transactionManager = new SoftTransactionManager(getSoftTransactionConfiguration(dataSource));
        transactionManager.init();
        BEDSoftTransaction transaction = (BEDSoftTransaction) transactionManager.getTransaction(SoftTransactionType.BestEffortsDelivery);
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            transaction.begin(conn);
            PreparedStatement preparedStatement1 = conn.prepareStatement(sql1);
            PreparedStatement preparedStatement2 = conn.prepareStatement(sql2);
            preparedStatement2.setObject(1, 1000);
            PreparedStatement preparedStatement3 = conn.prepareStatement(sql3);
            preparedStatement1.executeUpdate();
            preparedStatement2.executeUpdate();
            preparedStatement3.executeUpdate();
        } finally {
            transaction.end();
            if (conn != null) {
                conn.close();
            }
        }
    }

看到這個(gè)編寫模式,你一定會(huì)想,如果我使用MyBatis和spring,這一切能否整合起來,這個(gè)話題有興趣大家可以去嘗試。

總結(jié)

分布式事務(wù)處理起來有一定的難度,sharding-jdbc采用了簡單的弱事務(wù)模式和特殊場景下的柔性事務(wù)模式,沒有最好,只有更好,根據(jù)自身業(yè)務(wù)去選擇事務(wù)模式才是最重要的。

到此這篇關(guān)于sharding-jdbc中的事務(wù)詳細(xì)解讀的文章就介紹到這了,更多相關(guān)sharding-jdbc事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能

    Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能

    這篇文章介紹了Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • SpringBoot配置Spring?Security的實(shí)現(xiàn)示例

    SpringBoot配置Spring?Security的實(shí)現(xiàn)示例

    本文主要介紹了SpringBoot配置Spring?Security的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • springboot2 jackson實(shí)現(xiàn)動(dòng)態(tài)返回類字段方式

    springboot2 jackson實(shí)現(xiàn)動(dòng)態(tài)返回類字段方式

    這篇文章主要介紹了springboot2 jackson實(shí)現(xiàn)動(dòng)態(tài)返回類字段方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • springboot中的controller注意事項(xiàng)說明

    springboot中的controller注意事項(xiàng)說明

    這篇文章主要介紹了springboot中的controller注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 客戶端設(shè)置超時(shí)時(shí)間真的很重要

    客戶端設(shè)置超時(shí)時(shí)間真的很重要

    今天小編就為大家分享一篇關(guān)于客戶端設(shè)置超時(shí)時(shí)間真的很重要,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • java 數(shù)值類型分秒時(shí)間格式化的實(shí)例代碼

    java 數(shù)值類型分秒時(shí)間格式化的實(shí)例代碼

    這篇文章主要介紹了java 數(shù)值類型分秒時(shí)間格式化的實(shí)例代碼的相關(guān)資料,將秒或分鐘的值轉(zhuǎn)換為xx天xx小時(shí)xx分鐘xx秒 如果 “xx” 為0 自動(dòng)缺省,需要的朋友可以參考下
    2017-07-07
  • idea中同一SpringBoot項(xiàng)目多端口啟動(dòng)

    idea中同一SpringBoot項(xiàng)目多端口啟動(dòng)

    本文主要介紹了idea中同一SpringBoot項(xiàng)目多端口啟動(dòng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 解決org.apache.ibatis.binding.BindingException:?Invalid?bound?statement?(not?found)問題(最新推薦)

    解決org.apache.ibatis.binding.BindingException:?Invalid?boun

    這篇文章主要介紹了解決org.apache.ibatis.binding.BindingException:?Invalid?bound?statement?(not?found)問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • Spring Boot console log 格式自定義方式

    Spring Boot console log 格式自定義方式

    這篇文章主要介紹了Spring Boot console log 格式自定義方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Springboot整合FreeMarker的實(shí)現(xiàn)示例

    Springboot整合FreeMarker的實(shí)現(xiàn)示例

    本文主要介紹了Springboot整合FreeMarker的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評(píng)論

朝阳区| 普宁市| 南岸区| 阿巴嘎旗| 江山市| 大洼县| 蒙自县| 盐城市| 岳阳县| 习水县| 平利县| 巨鹿县| 偏关县| 藁城市| 时尚| 邵阳县| 陆丰市| 南阳市| 侯马市| 金溪县| 徐汇区| 陵川县| 沁阳市| 皋兰县| 娄底市| 通榆县| 海安县| 虎林市| 朝阳区| 抚松县| 通许县| 阿合奇县| 叶城县| 岳普湖县| 上虞市| 黄大仙区| 同仁县| 云安县| 江华| 清河县| 高碑店市|