Transaction 也就是所謂的事務了,通俗理解就是一件事情。從小,父母就教育咱們,作事情要善始善終,不能半途而廢。 事務也是這樣,不能作通常就不作了,要麼作完,要麼就不作。也就是說,事務必須是一個不可分割的總體,就像咱們在化學課裏學到的原子,原子是構成物質的最小單位。因而,人們就概括出事務的第一個特性:原子性(Atomicity)。我靠,一點都不神祕嘛。 java
特別是在數據庫領域,事務是一個很是重要的概念,除了原子性之外,它還有一個極其重要的特性,那就是:一致性(Consistency)。也就是說,執行完數據庫操做後,數據不會被破壞。打個比方,若是從 A 帳戶轉帳到 B 帳戶,不可能由於 A 帳戶扣了錢,而 B 帳戶沒有加錢吧。若是出現了這類事情,您必定會很是氣憤,什麼 diao 銀行啊! sql
當咱們編寫了一條 update 語句,提交到數據庫的一剎那間,有可能別人也提交了一條 delete 語句到數據庫中。也許咱們都是對同一條記錄進行操做,能夠想象,若是不稍加控制,就會出大麻煩來。咱們必須保證數據庫操做之間是「隔離」的(線程之間有時也要作到隔離),彼此之間沒有任何干擾。這就是:隔離性(Isolation)。要想真正的作到操做之間徹底沒有任何干擾是很難的,因而乎,天天上班打醬油的數據庫專家們,開始動腦筋了,「咱們要制定一個規範,讓各個數據庫廠商都支持咱們的規範!」,這個規範就是:事務隔離級別(Transaction Isolation Level)。能定義出這樣牛逼的規範真的挺不容易的,其實說白了就四個級別: 數據庫
千萬不要去翻譯,那只是一個代號而已。從上往下,級別愈來愈高,併發性愈來愈差,安全性愈來愈高,反之則反。 安全
當咱們執行一條 insert 語句後,數據庫必需要保證有一條數據永久地存放在磁盤中,這個也算事務的一條特性, 它就是:持久性(Durability)。 併發
概括一下,以上一共提到了事務的 4 條特性,把它們的英文單詞首字母合起來就是:ACID,這個就是傳說中的「事務 ACID 特性」! 高併發
真的是很是牛逼的特性啊!這 4 條特性,是事務管理的基石,必定要透徹理解。此外還要明確,這四個傢伙當中,誰纔是老大? 工具
其實想一想也就清楚了:原子性是基礎,隔離性是手段,持久性是目的,真正的老大就是一致性。數據不一致了,就至關於「江湖亂套了,流氓戴胸罩」。因此說,這三個小弟都是跟着「一致性」這個老大混,爲他全心全意服務。 性能
這四個傢伙當中,其實最難理解的反倒不是一致性,而是隔離性。由於它是保證一致性的重要手段,是工具,使用它不能有半點差池,不然後果自負!怪不得數據庫行業專家們都要來研究所謂的事務隔離級別了。其實,定義這四個級別就是爲了解決數據在高併發下所產生的問題,那又有哪些問題呢? spa
首先看看「髒讀」,看到「髒」這個字,我就想到了噁心、骯髒。數據怎麼可能髒呢?其實也就是咱們常常說的「垃圾數據」了。好比說,有兩個事務,它們在併發執行(也就是競爭)。看看如下這個表格,您必定會明白我在說什麼: 線程
時間 |
事務 A(存款) |
事務 B(取款) |
T1 |
開始事務 |
|
T2 |
|
開始事務 |
T3 |
|
查詢餘額(1000 元) |
T4 |
|
取出 1000 元(餘額 0 元) |
T5 |
查詢餘額(0 元) |
|
T6 |
|
撤銷事務(餘額恢復爲 1000 元) |
T7 |
存入 500 元(餘額 500 元) |
|
T8 |
提交事務 |
|
餘額應該爲 1500 元纔對!請看 T5 時間點,事務 A 此時查詢餘額爲 0 元,這個數據就是髒數據,它是事務 B 形成的,明顯事務沒有進行隔離,滲過來了,亂套了。
因此髒讀這件事情是很是要不得的,必定要解決掉!讓事務之間隔離起來纔是硬道理。
那第 2 條,不可重複讀又怎麼解釋呢?仍是用相似的例子來講明:
時間 |
事務 A(存款) |
事務 B(取款) |
T1 |
開始事務 |
|
T2 |
|
開始事務 |
T3 |
|
查詢餘額(1000 元) |
T4 |
查詢餘額(1000 元) |
|
T5 |
|
取出 1000 元(餘額 0 元) |
T6 |
|
提交事務 |
T7 |
查詢餘額(0 元) |
|
事務 A 其實除了查詢了兩次之外,其餘什麼事情都沒有作,結果錢就從 1000 變成 0 了,這就是重複讀了。可想而知,這是別人乾的,不是我乾的。其實這樣也是合理的,畢竟事務 B 提交了事務,數據庫將結果進行了持久化,因此事務 A 再次讀取天然就發生了變化。
這種現象基本上是能夠理解的,但在有些變態的場景下倒是不容許的。畢竟這種現象也是事務之間沒有隔離所形成的,但咱們對於這種問題,彷佛能夠忽略。
最後一條,幻讀。我去!Phantom 這個單詞不就是「幽靈、鬼魂」嗎?剛看到這個單詞時,真的把個人小弟弟都給驚呆了。怪不得這裏要翻譯成「幻讀」了,總不能翻譯成「幽靈讀」、「鬼魂讀」吧。其實意義就是鬼在讀,不是人在讀,或者說搞不清楚爲何,它就變了,很暈,真的很暈。仍是用一個示例來講話吧:
時間 |
事務 A(統計總存款) |
事務 B(存款) |
T1 |
開始事務 |
|
T2 |
|
開始事務 |
T3 |
統計總存款(10000 元) |
|
T4 |
|
存入 100 元 |
T5 |
|
提交事務 |
T6 |
統計總存款(10100 元) |
|
銀行工做人員,每次統計總存款,都看到不同的結果。不過這也確實也挺正常的,總存款增多了,確定是這個時候有人在存錢。可是若是銀行系統真的這樣設計,那算是玩完了。這一樣也是事務沒有隔離所形成的,但對於大多數應用系統而言,這彷佛也是正常的,能夠理解,也是容許的。銀行裏那些噁心的那些系統,要求很是嚴密,統計的時候,甚至會將全部的其餘操做給隔離開,這種隔離級別就算很是高了(估計要到 SERIALIZABLE 級別了)。
概括一下,以上提到了事務併發所引發的跟讀取數據有關的問題,各用一句話來描述一下:
第一條是堅定抵制的,後兩條在大多數狀況下可不做考慮。
這就是爲何必需要有事務隔離級別這個東西了,它就像一面牆同樣,隔離不一樣的事務。看下面這個表格,您就清楚了不一樣的事務隔離級別能處理怎樣的事務併發問題:
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
READ_UNCOMMITTED |
容許 | 容許 | 容許 |
READ_COMMITTED |
禁止 | 容許 | 容許 |
REPEATABLE_READ |
禁止 | 禁止 | 容許 |
SERIALIZABLE |
禁止 | 禁止 | 禁止 |
根據您的實際需求,再參考這張表,最後肯定事務隔離級別,應該再也不是一件難事了。
JDBC 也提供了這四類事務隔離級別,但默認事務隔離級別對不一樣數據庫產品而言,倒是不同的。咱們熟知的 MySQL 數據庫的默認事務隔離級別就是 READ_COMMITTED,Oracle、SQL Server、DB2等都有有本身的默認值。我認爲 READ_COMMITTED 已經能夠解決絕大多數問題了,其餘的就具體狀況具體分析吧。
若對其餘數據庫的默認事務隔離級別不太清楚,可使用如下代碼來獲取:
DatabaseMetaData meta = DBUtil.getDataSource().getConnection().getMetaData(); int defaultIsolation = meta.getDefaultTransactionIsolation();
提示:在 java.sql.Connection 類中可查看全部的隔離級別。
咱們知道 JDBC 只是鏈接 Java 程序與數據庫的橋樑而已,那麼數據庫又是怎樣隔離事務的呢?其實它就是「鎖」這個東西。當插入數據時,就鎖定表,這叫「鎖表」;當更新數據時,就鎖定行,這叫「鎖行」。固然這個已經超出了咱們今天討論的範圍,因此仍是留點空間給咱們的 DBA 同窗吧,省得他沒啥好寫的了。
除了 JDBC 給咱們提供的事務隔離級別這種解決方案之外,還有哪些解決方案能夠完善事務管理功能呢?
不妨看看 Spring 的解決方案吧,其實它是對 JDBC 的一個補充或擴展。它提供了一個很是重要的功能,就是:事務傳播行爲(Transaction Propagation Behavior)。
確實夠牛逼的,Spring 一會兒就提供了 7 種事務傳播行爲,這 7 種行爲一出現,真的是亮瞎了個人狗眼!
看了 Spring 參考手冊以後,更是暈了,這究竟是在幹嗎?
首先要明確的是,事務是從哪裏來?傳播到哪裏去?答案是,從方法 A 傳播到方法 B。Spring 解決的只是方法之間的事務傳播,那狀況就多了,好比:
這樣就是 4 種了,還有 3 種特殊狀況。仍是用個人 Style 給你們作一個分析吧:
假設事務從方法 A 傳播到方法 B,您須要面對方法 B,問本身一個問題:
方法 A 有事務嗎?
看到我上面這段解釋,小夥伴們是否已經感覺到,被打通任督二脈的感受?多讀幾遍,體會一下,就是您本身的東西了。
須要注意的是 PROPAGATION_NESTED,不要被它的名字所欺騙,Nested(嵌套),因此凡是在相似方法 A 調用方法 B 的時候,在方法 B 上使用了這種事務傳播行爲,若是您真的那樣作了,那您就錯了。由於您錯誤地覺得 PROPAGATION_NESTED 就是爲方法嵌套調用而準備的,其實默認的 PROPAGATION_REQUIRED 就能夠幫助您,作您想要作的事情了。
Spring 給咱們帶來了事務傳播行爲,這確實是一個很是強大而又實用的功能。除此之外,也提供了一些小的附加功能,好比:
最後,推薦你們使用 Spring 的註解式事務配置,而放棄 XML 式事務配置。由於註解實在是太優雅了,固然這一切都取決於您自身的狀況了。
在 Spring 配置文件中使用:
... <tx:annotation-driven /> ...在須要事務的方法上使用:
@Transactional public void xxx() { ... }
可在 @Transactional 註解中設置:事務隔離級別、事務傳播行爲、事務超時時間、是否只讀事務。
簡直是太完美了,太優雅了!
最後,有必要對本文的內容作一個總結,免費贈送一張高清無碼思惟導圖:
期盼您的對本文的評價!感謝您的關注與支持!