事務的四大特性--ACID
ACID ---- 原子性、一致性、隔離性、永續性
原子性
就是一系列的操作,要麼都執行,要麼都不執行。當一個事務發生異常的時候,就會通過回滾來保證原子性。在mysql中,原子性是通過回滾日誌來實現的,回滾日誌就是我們的操作的逆操作。
永續性
事務提交之後一定會儲存到資料庫中,如果要回滾只能手動執行相反的操作了
隔離性
事務的隔離性會跟併發等相關概念聯絡的非常密切,因為它主要就是為了保證並行事務處理能夠達到“互不干擾”的效果。
我們在一致性中討論過事務在併發情況下執行時,可能發生的一系列問題:雖然單個事務執行並沒有錯誤,但是它的執行可能會牽連到其他事務的執行,最終導致資料庫的整體一致性出現偏差。
談到這裡我們就要看看事務之間的互相干擾都有哪些層級,也就是我們資料庫中非常重要的概念:
事務的隔離級別
事務的隔離級別,其實是資料庫對資料隔離效能的一種約束,選擇不同的隔離級別會影響資料一致性的程度,同時也會影響資料庫的操作效能。
標準SQL中定義了以下4種隔離級別:
-
讀未提交
使用查詢語句不會加鎖,可能會讀到未提交的行(髒讀)
-
讀已提交
只對記錄加記錄鎖,而不會在記錄之間增加間隙鎖,所以允許新的記錄被插入到被鎖定記錄附近,在多次使用查詢語句時,可能會得到不同的結果(不可重複讀)。例如:事務A讀取資料,然後事務B修改資料,之後A再次讀取,這是讀取到的資料和第一次讀取的資料不一致
-
可重複讀
多次讀取同一範圍的資料會返回第一次查詢的快照,不會返回不同的資料行,但是可能發生幻讀。幻讀 : 是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。 同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象 發生了幻覺一樣。
-
序列化
隱式地將全部的查詢語句都加上了共享鎖。從上到下一致性逐漸增強,但是資料庫的讀寫效能也逐漸變差。大部分資料庫中使用提交讀作為預設的隔離級別,這是出於效能和一致性的平衡,而MySQL中則預設採用可重複讀作為配置。對於開發者而言,不必去了解每個隔離級別具體的實現,但要能夠根據不同的場景選擇最合適的隔離級別。
一致性
http://geyifan.cn/2016/07/17/talk-about-transaction/
狀態是不一致的。
舉個栗子,張三給李四轉賬100元。事務要做的是從張三賬戶上減掉100元,李四賬戶上加上100元。一致性的含義是其他事務要麼看到張三還沒有給李四轉賬的狀態,要麼張三已經成功轉賬給李四的狀態,而對於張三少了100元,李四還沒加上100元這個中間狀態是不可見的。
那麼反駁的聲音來了:
- 要麼轉賬操作全部成功,要麼全部失敗,這是原子性。從例子上看全部成功,那麼一致性就是原子性的一部分咯,為什麼還要單獨說一致性和原子性?
- 你說的不對。在未提交讀的隔離級別下是事務內部操作是可見的,這時候會出現髒讀,明顯違背了一致性,怎麼解釋?
好吧,你的疑問很有道理,也很充分,這正說明你對事務的ACID特性理解的很到位。不過,需要注意的是:
- 原子性和一致性的的側重點不同:原子性關注狀態,要麼全部成功,要麼全部失敗,不存在部分成功的狀態。而一致性關注資料的可見性,中間狀態的資料對外部不可見,只有最初狀態和最終狀態的資料對外可見。
- 在未提交讀的隔離級別下,會造成髒讀,這就是因為一個事務讀到了另一個事務操作內部的資料。ACID中是的一致性描述的是一個最理想的事務應該怎樣的,是一個強一致性狀態,如果要做到這點,需要使用排它鎖把事務排成一隊,即Serializable的隔離級別,這樣效能就大大降低了。現實是骨感的,所以使用隔離性的不同隔離級別來破壞一致性,來獲取更好的效能。
補充:不可重複讀和幻讀
很多人容易搞混不可重複讀和幻讀,確實這兩者有些相似。但不可重複讀重點在於update和delete,而幻讀的重點在於insert。
如果使用鎖機制來實現這兩種隔離級別,在可重複讀中,該sql第一次讀取到資料後,就將這些資料加鎖,其它事務無法修改這些資料,就可以實現可重複 讀了。但這種方法卻無法鎖住insert的資料,所以當事務A先前讀取了資料,或者修改了全部資料,事務B還是可以insert資料提交,這時事務A就會 發現莫名其妙多了一條之前沒有的資料,這就是幻讀,不能通過行鎖來避免。需要Serializable隔離級別 ,讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥,這麼做可以有效的避免幻讀、不可重複讀、髒讀等問題,但會極大的降低資料庫的併發能力。