面試題思考: 什么是事務(ACID)?


 

事務(Transaction)是由一系列對系統中數據進行訪問與更新的操作所組成的一個程序 執行邏輯單元(Unit)。

狹義上的事務特指數據庫事務。一方面,當多個應用程序並發訪問數據庫時,事務可以在這些應用程序之間提供一個隔離方法,以防止彼此的操作互相干擾。

另一方面,事務為數據庫操作序列提供了一個從失敗中恢復到正常狀態的方法, 同時提供了數據庫即使在異常狀態下仍能保持數據一致性的方法。

事務具有四個特征,分別足原子性(Atomicity )、一致性(Consistency )、隔離性(Isolation) 和持久性(Durability),簡稱為事務的ACID特性。

1、原子性(Atomicity )
事務的原子性是指事務必須是一個原子的操作序列單元。事務中包含的各項操作在一次執行過程中,只允許出現以下兩種狀態之一。
•全部成功執行。
•全部不執行。


任何一項操作失敗都將導致整個事務失敗,同時其他已經被執行的操作都將被撤銷並回滾,只打所有的操作全部成功,整個事務才算是成功完成。


2、一致性(Consistency )
事務的一致性是指事務的執行不能破壞數據庫數據的完整性和一致性,一個事務在執行之前和執行之后,數據庫都必須處於一致性狀態。也就是說,事務執行的結果必須是使數據庫從一個一致性狀態轉變到另一個一致性狀態,因此當數據庫只包含成功事務提交 的結果時,就能說數據庫處於一致性狀態。而如果數據庫系統在運行過程中發生故障, 有些事務尚未完成就被迫中斷,這些未完成的事務對數據庫所做的修改有一部分已寫入物理數據庫,這時數據庫就處於一種不正確的狀態,或者說是不一致的狀態。

 

3、隔離性(Isolation) 
事務的隔離性是指在並發環境中,並發的事務是相互隔離的,一個事務的執行不能被其他事務干擾。也就是說,不同的事務並發操縱相同的數據時,每個事務都有各自完整的數據空間,即一個事務內部的操作及使用的數據對其他並發事務是隔離的,並發執行的 各個事務之間不能互相干擾。


在標准SQL規范中,定義了 4個事務隔離級別,不同的隔離級別對事務的處理不同, 如未授權讀取、授權讀取、可重復讀取和串行化
①未授權讀取
未授權讀取也被稱為讀未提交(Read Uncommitted),該隔離級別允許臟讀取,其隔離級別最低。換句話說,如果一個事務正在處理某一數據,並對其進行了更新,但同時尚未完成事務,因此還沒有進行事務提交,而與此同時,允許另一個事務也能夠訪問該數據。舉個例子來說,事務A和事務B同時進行,事務A在整個執行階段,會將某數據項的值從1開始,做一系列加法操作(比如說加1操作)直到變成10之后進行事務提交,此時,事務B能夠看到這個數據項在事務A操作過程中的所有中間值(如1變成2、2變成3等),而對這一系列的中間值的讀取就是未授權讀取。

 

②授權讀取
授權讀取也被稱為讀已提交(Read Committed),它和未授權讀取非常相近,唯一的區別就是授權讀取只允許獲取已經被提交的數據。同樣以上面的例子來說,事務 A和事務同時進行,事務A進行與上述同樣的操作,此時,事務B無法看到這個數據項在事務A操作過程中的所有中間值,只能看到最終的10。

另外,如果說有一個事務C,和事務A進行非常類似的操作,只是事務C是將數據項從10加到 20,此時事務B也同樣可以讀取到20,即授權讀取允許不可重復讀取。
③可重復讀取
可重復讀取(Repeatable Read),簡單地說,就是保證在事務處理過程中,多次讀取同一個數據時,其值都和事務開始時刻是一致的。因此該事務級別禁止了不可重復讀取和臟讀取,但是有可能出現幻影數據。所謂幻影數據,就是指同樣的事務操作,在前后兩個時間段內執行對同一個數據項的讀取,可能出現不一致的結果。在上面的例子,可重復讀取隔離級別能夠保證事務B在第一次事務操作過程中,始終對數據項讀取到 1,但是在下一次事務操作中,即使事務 B (注意,事務名字雖然相同,但是指的是另一次事務操作)采用同樣的查詢方式,就吋能會讀取到10或20。

④串行化

串行化( Serializable)是最嚴格的事務隔離級別。它要求所有事務都被串行執行,即事務只能一個接一個地進行處理,不能並發執行。

 

 

 

 

4、持久性(Durability)
事務的持久性也被稱為永久性,是指一個事務一旦提交,它對數據庫中對應數據的狀態變更就應該是永久性的。換句話說,一旦某個事務成功結束,那么它對數據庫所做的更新就必須被永久保存下來——即使發生系統崩潰或機器宕機等故障,只要數據庫能夠重新啟動,那么一定能夠將其恢復到事務成功結束時的狀態。

 

以上介紹完事務的四大特性(簡稱ACID),現在重點來說明下事務的隔離性,當多個線程都開啟事務操作數據庫中的數據時,數據庫系統要能進行隔離操作,以保證各個線程獲取數據的准確性,在介紹數據庫提供的各種隔離級別之前,我們先看看如果不考慮事務的隔離性,會發生的幾種問題:

1,臟讀

  臟讀是指在一個事務處理過程里讀取了另一個未提交的事務中的數據。

  當一個事務正在多次修改某個數據,而在這個事務中這多次的修改都還未提交,這時一個並發的事務來訪問該數據,就會造成兩個事務得到的數據不一致。例如:用戶A向用戶B轉賬100元,對應SQL命令如下

update account set money=money+100 where name=’B’;  (此時A通知B)

    update account set money=money - 100 where name=’A’;

當只執行第一條SQL時,A通知B查看賬戶,B發現確實錢已到賬(此時即發生了臟讀),

而之后無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那么當B以后再次查看賬戶時就會發現錢其實並沒有轉。

2,不可重復讀

不可重復讀是指在對於數據庫中的某個數據,一個事務范圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。

例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發送了不可重復讀。

不可重復讀和臟讀的區別是,臟讀是某一事務讀取了另一個事務未提交的臟數據,而不可重復讀則是讀取了前一事務提交的數據。

在某些情況下,不可重復讀並不是問題,比如我們多次查詢某個數據當然以最后查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不同,A和B就可能打起來了……

3,虛讀(幻讀)

幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是為“1”並且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。

  幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。

 現在來看看MySQL數據庫為我們提供的四種隔離級別:

  ① Serializable (串行化):可避免臟讀、不可重復讀、幻讀的發生。

  ② Repeatable read (可重復讀):可避免臟讀、不可重復讀的發生。

  ③ Read committed (讀已提交):可避免臟讀的發生。

  ④ Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,當然級別越高,執行效率就越低。像Serializable這樣的級別,就是以鎖表的方式(類似於Java多線程中的鎖)使得其他的線程只能在鎖外等待,所以平時選用何種隔離級別應該根據實際情況。在MySQL數據庫中默認的隔離級別為Repeatable read (可重復讀)。

  在MySQL數據庫中,支持上面四種隔離級別,默認的為Repeatable read (可重復讀);而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的為Read committed級別。

  在MySQL數據庫中查看當前事務的隔離級別:

select @@tx_isolation;

在MySQL數據庫中設置事務的隔離 級別:

 set  [glogal | session]  transaction isolation level 隔離級別名稱;

    set tx_isolation=’隔離級別名稱;’

例1:查看當前事務的隔離級別:

 

 例2:將事務的隔離級別設置為Read uncommitted級別:

或:

 

 記住:設置數據庫的隔離級別一定要是在開啟事務之前!

如果是使用JDBC對數據庫的事務設置隔離級別的話,也應該是在調用Connection對象的setAutoCommit(false)方法之前。

調用Connection對象的setTransactionIsolation(level)即可設置當前鏈接的隔離級別,至於參數level,可以使用Connection對象的字段:

在JDBC中設置隔離級別的部分代碼:

 

 后記:隔離級別的設置只對當前鏈接有效。對於使用MySQL命令窗口而言,一個窗口就相當於一個鏈接,當前窗口設置的隔離級別只對當前窗口中的事務有效;

對於JDBC操作數據庫來說,一個Connection對象相當於一個鏈接,而對於Connection對象設置的隔離級別只對該Connection對象有效,與其他鏈接Connection對象無關。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM