圖解 & 深刻淺出JavaWeb:事務必會必知

事務,你們所熟悉的事務(Transcation),基本上會就往Spring事務靠。其實Spring事務管理基於底層數據庫自己的事務處理機制。數據庫事務的基礎,是掌握Spring事務管理的基礎。這篇總結下數據庫事務。java

1、數據庫事務

它的思想:we are 伐木累。就是多個SQL語句(一個團隊),要麼全部執行success,否則就fail。git

它最終的目標:數據不會被破壞。即事務操做成功,數據的結果和業務期待的結果是一致的。這也就是ACID中的一致性(Consistency)。那什麼是ACID呢?github

 

2、ACID

上面是思想,牛人立刻根據思想建模,DBMS中數據庫事務知足4各特性,即原子性、一致性、隔離性和持久性。下面一一輩子動解釋:sql

a)原子性數據庫

原子是物質的最小單元,即不可再分。安全

例如,以MySQL爲例,每個簡單的 SQL 語句即包含在一個事務中,具備原子性。這時候有人問了,那多個SQL呢?併發

BEGIN TRANSACTION;



INSERT INTO `test`.`city` (`state`, `country`, `name`)



VALUES



('1', 'China', 'CHINA','錯誤語句多了個VALUE');



INSERT INTO `test`.`city` (`state`, `country`, `name`)



VALUES



('1', 'China', 'CHINA');



COMMIT;


結果:執行不經過。行3-5:爲一個錯誤SQL。行6-8:是一個正確的SQL。它們各自被包裹在各自的隱式事務中,即Read Uncommited。T-all包裹了上面具備原子性的T-1和T-2,實現了更大的原子,以下圖。socket

2

b)一致性分佈式

終極目標:數據不會被破壞。(這不是廢話?確實有點)具體說,事務操做成功後,數據庫所處的狀態和它的業務規則是一致的,即數據不會被破壞。舉個栗子:兩句UPDATE語句,從A帳戶轉帳到B帳戶,無論成功失敗,A和B帳戶的總額是不變的。高併發

c)隔離性

隔離:表示互不干擾。事務與事務之間沒法干擾,即每一個事務獨立,不會交叉。這樣可讓多個線程併發訪問數據庫。如圖:

3

可是聰明的小夥伴知道,若是事務徹底隔離,每次只容許一個事務能訪問數據庫,那其餘都是阻塞。會很是慢。

可是聰明的小夥伴也知道,這樣會形成數據的併發問題。(是的,在下面第三節講)。

d)持久性

數據必須持久化到數據庫(存儲在磁盤)中。已提交的事務,即便在提交後數據庫崩潰,重啓數據庫時也可以根據日誌對未持久化的數據進行重執行操做。(同窗會問,那沒提交的事務呢?那就悲劇了(>﹏<))

小結:數據的一致性是最終目標,其餘特性都是其要求或手段。

 

3、隔離性中的問題:髒讀、不可重複讀和幻讀

對應上面的隔離性,事務併發訪問的時候會出現:髒讀、不可重複讀和幻讀。案例轉自勇哥博客

髒讀:A事務讀取了B事務未提交的更改數據。通常數據庫事務默認不容許該問題出現。

好比這裏查詢應該是1500,如今出現了髒讀。

     
時間 事務 A(存款) 事務 B(取款)
T1 開始事務  
T2   開始事務
T3   查詢餘額(1000 元)
T4   取出 1000 元(餘額 0 元)
T5 查詢餘額(0 元)  
T6   撤銷事務(餘額恢復爲 1000 元)
T7 存入 500 元(餘額 500 元)  
T8 提交事務  

不可重複讀:A事務讀取了B事務已提交的更改數據。

幻讀:A事務讀取了B事務提交的新增數據。

上面的案例腦補吧,主要仍是看下面。

 

不可重複讀和幻讀區別:一個更改,一個新增數據。其實兩個區別在於一個是新增(insert語句),處理幻讀這個操做須要加表級別的鎖,將整個表鎖定,防止新增數據形成幻讀。另外一個則是更改(update delete),這時候避免這個狀況只須要添加行級鎖組織該行發生變化便可。

 

4、事務隔離級別

既要求高的隔離性(安全性),又要求高併發性。這種是不可能的任務。根據各類鎖的操做機制出現了一個事務隔離級別。即相同狀況下的輸入,不一樣隔離級別結果不一樣。爲啥了,固然是在併發性和安全性的抉擇。如圖:

6

按着圖說的,根據程序的併發性和安全性的抉擇。魚和熊掌不可兼得也~ 但分佈式的時候,能夠吧安全性關鍵的單獨分佈式鎖。

好了,案例說了不少下面代碼實戰。

 

ps: 休息下,泥瓦匠的代碼都會這github上~ ,這段代碼地址:https://github.com/JeffLi1993/jee-component-learning

 

五 、JDBC事務實戰

下面利用MYSQL JDBC驅動鏈接MySQL,代碼以下:

public class TransactionLevels extends BaseJDBC {

    public static void main(String[] args) {

        try {

            // 加載數據庫驅動

            Class.forName(DRIVER);

            // 數據庫鏈接

            Connection conn = DriverManager.getConnection(URL,USER,PWD);

            // 數據庫元數據

            DatabaseMetaData metaData = conn.getMetaData();



            // 是否支持事務

            boolean isSupport = metaData.supportsTransactions();

            System.out.println(isSupport);

            // 是否支持的事務

            boolean isSupportLevel = metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE);

            System.out.println(isSupportLevel);

            // 獲取默認事務

            int defaultIsolation = metaData.getDefaultTransactionIsolation();

            System.out.println(defaultIsolation);



            /** 關閉數據庫鏈接 */

            if (conn != null) {

                try {

                    conn.close();

                } catch (SQLException e) {

                    e.printStackTrace();

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        }



    }

}


第 9 行:獲取數據庫元數據,這是包含數據庫鏈接信息第 五、7行是鏈接數據庫

第 12 行:從元數據中,判斷是否支持事務

第 15 行:從元數據中,判斷是否支持事務級別 TRANSACTION_SERIALIZABLE

第 18 行:這裏能夠看出MySQL默認支持的事務級別是 READ_COMMITTED,默認會隔離髒讀。

具體源碼以下:

4

所以在安全性要求不高,支持高併發的狀況下,選擇MySQL默認事務等級。但在安全性極高,幾乎不會出現高併發狀況下,選擇更高的事務等級。根據上小節的圖一幕瞭然。

 

6、補充

關於事務,還有你們熟悉的Spring事務管理、具體數據庫事務的實現,推薦一本書《MySQL技術內幕InnoDB存儲引擎 》。

下一篇:ThreadLocal的工做機制,揭示Spring事務同步管理器的工做原理

如以上文章或連接對你有幫助的話,別忘了分享到朋友圈,讓更多的人閱讀這篇文章。


 

標籤:Transcation事務

相關文章
相關標籤/搜索