1. 程式人生 > >MySql-事物

MySql-事物

事務(Transaction)及其ACID屬性

事務是由一組SQL語句組成的邏輯處理單元,事務具有以下4個屬性,通常簡稱為事務的ACID屬性。    

      1.原子性(Atomicity):事務是一個原子操作單元,在同一個事物中其對資料的修改,要麼全都執行,要麼全都不執行。不會結束在中間某個環節執行一部分。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
      2.一致性(Consistent):在事務開始和完成時,資料都必須保持一致狀態。這意味著所有相關的資料規則都必須應用於事務的修改,以保持資料的完整性;事務結束時,所有的內部資料結構(如B樹索引或雙向連結串列)也都必須是正確的。當然這個含義中也隱含著對開發者的要求,就是不能寫出錯誤的事務邏輯,比如銀行的轉賬不能只加錢不減錢,這是應用層面的一致性要求。比如A賬戶想B賬戶轉賬5000元:①檢測A賬戶餘額 > 5000元 ②A賬戶餘額減去 5000元 ③B賬戶餘額增加5000元。一致性保證A和B的金錢總計是不會變的。
      3.隔離性(Isolation):資料庫允許多個併發事務同時對其資料進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和序列化(Serializable)。
      4. 永續性(Durable):事務完成之後,它對於資料的修改是永久性的,需要將提交的事務持久化到磁碟。即使出現系統故障也能夠保持。

思考:

  一致性是指資料處於一種語義上的有意義且正確的狀態。一致性是對資料可見性的約束,保證在一個事務中的多次操作的資料中間狀態對其他事務不可見的。因為這些中間狀態,是一個過渡狀態,與事務的開始狀態和事務的結束狀態是不一致的。
  舉個粒子,張三給李四轉賬100元。事務要做的是從張三賬戶上減掉100元,李四賬戶上加上100元。一致性的含義是其他事務要麼看到張三還沒有給李四轉賬的狀態,要麼張三已經成功轉賬給李四的狀態,而對於張三少了100元,李四還沒加上100元這個中間狀態是不可見的。
  那麼反駁的聲音來了:
  要麼轉賬操作全部成功,要麼全部失敗,這是原子性。從例子上看全部成功,那麼一致性就是原子性的一部分咯,為什麼還要單獨說一致性和原子性?
  你說的不對。在未提交讀的隔離級別下是事務內部操作是可見的,明顯違背了一致性,怎麼解釋?
  好吧,需要注意的是:
原子性和一致性的的側重點不同:原子性關注狀態,要麼全部成功,要麼全部失敗,不存在部分成功的狀態。

一致性關注資料的可見性,中間狀態的資料對外部不可見,只有最初狀態和最終狀態的資料對外可見

  隔離性是多個事物的時候, 相互不能干擾,一致性是要保證操作前和操作後資料或者資料結構的一致性,而我提到的事務的一致性是關注資料的中間狀態,也就是一致性需要監視中間狀態的資料,如果有變化,即刻回滾

事務的隔離性是通過鎖實現的,而事務的原子性、一致性、永續性則是通過事務日誌(redo 和 undo)實現的。

併發事務處理帶來的問題

相對於序列處理來說,併發事務處理能大大增加資料庫資源的利用率,提高資料庫系統的事務吞吐量,從而可以支援可以支援更多的使用者。但併發事務處理也會帶來一些問題,主要包括以下幾種情況。

   1.更新丟失(Lost Update)
        [1]Transaction01將某條記錄的AGE值從20修改為30提交。
        [2]Transaction02將某條記錄的AGE值從20修改為40提交。
        [3]Transaction02的修改覆蓋了Transaction01的修改
    2.髒讀(Dirty Reads) 讀未提交
        [1]Transaction01將某條記錄的AGE值從20修改為30。
        [2]Transaction02讀取了Transaction01更新後的值:30。
        [3]Transaction01回滾,AGE值恢復到了20。
        [4]Transaction02讀取到的30就是一個無效的值。
        一句話:事務A讀取到了事務B已修改但尚未提交的的資料,還在這個資料基礎上做了操作。此時,如果B事務回滾,A讀取的資料無效,不符合一致性要求。
    3.不可重複讀(Non-Repeatable Reads)  讀已提交
        [1]Transaction01讀取了AGE值為20。
        [2]Transaction02將AGE值修改為30。
        [3]Transaction01再次讀取AGE值為30,和第一次讀取不一致。
        一個事務在讀取某些資料後的某個時間(還未提交,但是沒有進行更新操鎖,沒有鎖表,其他事物可以更新之前查詢過的表:之前select * from .... for update可以避免其他的事物操作本條記錄),再次讀取以前讀過的資料,卻發現其讀出的資料已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重複讀”。
        一句話:事務A讀取到了事務B已經提交的修改資料,不符合隔離性
    4.幻讀(Phantom Reads)
        [1]Transaction01讀取了STUDENT表中的一部分資料。
        [2]Transaction02向STUDENT表中插入了新的行。
        [3]Transaction01讀取了STUDENT表時,多出了一些行。
        一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料,這種現象就稱為“幻讀”。
        一句話:事務A讀取到了事務B體提交的新增資料,不符合隔離性。
        多說一句:幻讀和髒讀有點類似,應該是和不可重複讀類似吧?
        髒讀是事務B裡面修改了資料,
        幻讀是事務B裡面新增了資料。

事物的隔離級別

髒讀”、“不可重複讀”和“幻讀”,其實都是資料庫讀一致性問題,必須由資料庫提供一定的事務隔離機制來解決

(有四個級別 1 2 4 8 mysql預設隔離級別為4)

    ①讀未提交:READ UNCOMMITTED  1
        允許Transaction01讀取Transaction02未提交的修改。會有髒讀 不可重複讀 幻讀
    ②讀已提交:READ COMMITTED   2
        要求Transaction01只能讀取Transaction02已提交的修改。  會有不可重複讀 幻讀
    ③可重複讀:REPEATABLE READ  4
        確保Transaction01可以多次從一個欄位中讀取到相同的值,即Transaction01執行期間禁止其它事務對這個欄位進行更新。會有幻讀
    ④序列化:SERIALIZABLE  8
         確保Transaction01可以多次從一個表中讀取到相同的行,在Transaction01執行期間,禁止其它事務對這個表進行新增、更新、刪除操作。可以避免任何併發問題,但效能十分低下。

事物的常見傳播行為

①propagation=Propagetion.REQUIRED
    a()方法呼叫b()方法
        如果a()方法沒有事物,那麼b()方法自己開啟一個事物
        如果a()方法有事物,那麼b()方法加入a()方法的事物中

②propagation=Propagetion.REQUIRES_NEW
    a()方法呼叫b()方法
        不管a()方法是否有事物,b()方法總為自己開啟一個新的事物
        如果a()方法有事物,b()方法事物在執行時,a()方法事物需要掛起,等b()方法事物結束後,a()方法事物繼續執行