分佈式事務

什麼是分佈式事務

分佈式事務,一言以蔽之,就是不一樣物理服務器上數據庫執行DML SQL語句的一致性問題。 下面的代碼就是一個典型的分佈式事務場景html

 

 

 

1.消息隊列模式:java

事務發起方不用回滾的狀況 (下訂單,減庫存)mysql

2.TCC模式:android

跨行轉帳、支付寶轉到餘額寶sql

3.2PC/3PC:數據庫

2pc 3pc都是XA協議的兩種實現服務器

 

https://www.toutiao.com/a6649556059033698819/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1558054532&app=news_article&utm_source=weixin&utm_medium=toutiao_android&req_id=20190517085531010026043033939C242&group_id=6649556059033698819網絡

 

 

https://www.toutiao.com/a6691951111181435399/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1558096114&app=news_article&utm_source=weixin&utm_medium=toutiao_android&req_id=201905172028330100220620378100170&group_id=6691951111181435399架構

 

 

https://www.javazhiyin.com/31853.htmlapp

 

復現分佈式事務問題:

建立DataSource,使用阿里的德魯伊

DruidDataSource dataSource = new DruidDataSource();

dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl(mysqlSettingParam.getUrl());
dataSource.setUsername(mysqlSettingParam.getUsername());
dataSource.setPassword(mysqlSettingParam.getPassword());
dataSource.setInitialSize(mysqlSettingParam.getInitialSize());
dataSource.setMaxActive(mysqlSettingParam.getMaxActive());
dataSource.setMinIdle(mysqlSettingParam.getMinIdle());
dataSource.setMaxWait(mysqlSettingParam.getMaxWait());
dataSource.setTestWhileIdle(mysqlSettingParam.getTestWhileIdle());
dataSource.setValidationQuery(mysqlSettingParam.getValidQuery());

 

 

txManager = new DataSourceTransactionManager(dataSource);

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = txManager.getTransaction(def);

        try {
            //操做數據庫
            adChannelService.insert(adChannelInfo);

            //進行rpc http調用,模擬分佈式事務場景
            //這個http接口實現裏面也有insert、update數據庫邏輯
            String str = restTemplate.getForObject("http://127.0.0.1:9109/save", String.class);

            System.out.println("str="+str);

            //故意製造異常,也多是RPC請求發生了TimeOutException
            int i = 1/0;

            txManager.commit(status);  //提交status中綁定的事務
        } catch (RuntimeException e) {
            txManager.rollback(status);  //回滾
            e.printStackTrace();
        }

 

 

經常使用解決方案

  1. 基於數據庫的兩階段提交 2pc 3pc(2pc基礎上引入了超時機制)
  2. 基於補償思想的TCC
  3. 基於最終一致性本地mysql消息表和MQ (ebay)
  4. RocketMQ的事務消息(4.3以上版本)

 

JTA是Java Transaction API簡稱 ,即Java事務管理器,主要管理本地事務。本地事務是隻同一個數據庫源中,屢次數據庫鏈接之間的事務,使得這多個數據表操做遵循ACID。

 

若是想讓JTA管理多臺數據庫操做的分佈式事務,須要XA支持,Open Group設計的X / Open分佈式事務處理定義了一種標準通訊架構。容許多個應用程序共享多個資源管理器提供的資源,並容許其工做協調到全局事務中。該XA接口使資源管理者參加事務,執行2PC(兩個階段提交),並在發生故障以後恢復。 

 

2PC(兩段提交)含義:首先, 事務協調員向每一個服務器詢問一遍,要求每一個數據庫都進行precommit的操做和是否可能實現 commit. 若是全部數據庫都贊成commit ,第二段開始。第二段:事務協調員要求每一個數據庫commit數據. 若是任何數據庫否決commit, 那麼全部數據庫將被要求回滾。當事務對某個數據庫鎖定時,這個數據庫或列就不可用,或者響應時間很慢。犧牲可用性了。

 

JTA + XA可以實現分佈式事務2PC 。

 

Spring提供了JTA介入方式,可是沒有提供JTA實現,目前JTA實現: Java Open Transaction Manager (JOTM), JBoss TS, Bitronix Transaction Manager (BTM), 和 Atomikos

 

 

byteTCC

 

在分佈式時代,分庫分表是很常見的,微服務系統中,各個系統一般使用獨立的數據庫,因此,事務很難靠數據庫自己保證,只能靠業務系統來解決。

例如支付寶中的餘額寶、花唄,具體不清楚,但猜想應該就是2個服務,不是同一個數據庫,咱們還花唄的時候一般都是從餘額寶中扣除的,這就是分佈式事務,一個系統中扣減錢,一個系統中增長錢。

下面咱們分析下最終一致性的實現方案,最終一致性一般都是使用消息中間件來實現的,系統結構以下:

分佈式事務方案 - 最終一致性

 

用戶向系統A發起轉帳請求,A先在本身的數據庫中扣錢,而後經過消息中間件告訴B應該加錢,B收到後在本身的數據庫中加錢。

這裏有個關鍵問題,A更新數據庫和給消息中間件發消息是2個操做,以下兩個場景怎麼處理:

先更新數據庫,成功了,但發送消息失敗了,重發屢次仍是失敗

先發消息,成功了,但數據庫更新失敗,消息撤不回來了

都是由於這2個操做不是原子的,發作誰都有問題。

那看下這樣作是否能夠,就是把更新數據庫和給消息中間件發消息放到一個事務中,這樣不就原子了嗎?

有問題,例如:

若是消息發送失敗,具體問題出在哪兒?是消息中間件根本就沒收到消息,仍是收到消息後response時出錯了?若是是根本沒收到還好一點,若是是收到了但響應失敗就麻煩了,致使A數據庫回滾,沒有扣錢,但B收到消息了,加錢了。

若是發消息時網絡延遲很高怎麼辦,數據庫事務一直被拖着,性能差,風險高。

因此,放入一個事務中這種方法是不可取的。

爲了保證原子性,能夠變通一下,添加一個消息表,A不直接往消息中間件中發消息,而是把消息寫入消息表,而後經過一個後臺程序不斷的把消息寫入消息中間件。

分佈式事務方案 - 最終一致性

 

這個後臺程序源源不斷的把消息表中的消息發到消息中間件,若是失敗就重試,能夠保證:

消息不會丟失

順序不亂

但會有消息重複的狀況,由於消息發送失敗多是寫入失敗,也多是寫入成功但響應失敗,因此消息可能會重複,這個問題須要系統B來處理。

系統B須要考慮2個問題:

消息丟失

B從消息中間件中拿到消息,還沒處理完就宕機了,這條消息怎麼辦?

須要經過ACK機制處理,消費成功的發送ACK,對於沒有ACK的消息,消息中間件會再次推送。

消息重複

ACK機制也存在消息重複的狀況,好比B已經處理完一條消息,發ACK時失敗了,那麼這條消息就還會被推過來。

還有就是上面說的後臺程序發消息時可能重複。

對於重複消息問題,能夠加一個判重表,記錄處理成功的消息,每次收到消息時,先經過判重表判斷一下,若是重複了就不處理,實現冪等性。

這樣,總體結構就變爲:

分佈式事務方案 - 最終一致性

 

以上就是經過最終一致性解決分佈式事務問題的基本思路,A 保證消息不丟,B 保證消息不漏、冪等。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息