MySQL 事務隔離級別及ACID

2022-03-31 10:57:58 字數 4809 閱讀 5468

注:begin或start transaction並不是乙個事務的起點,而是在執行它們之後的第乙個操作innodb表的語句,事務才真正開始。start transaction with consistent snapshot命令可以馬上啟動乙個事務。

讀未提交

當前事務能讀取到其他事務尚未提交的修改值。

讀提交

當前事務能讀取到其他事務已經提交的修改值。

可重複讀

乙個事務在任何時刻查詢到的結果跟事務一開始啟動時查詢的結果一樣。

序列化

強行使所有事務序列執行。

理解了隔離級別的基本概念後,我們做個小測試,看看是不是真的理解了,下面兩個會話按順序執行,最後在各個隔離級別下的v1、v2值各為多少呢?

sessiona

sessionb

啟動事務

查詢得到值1

啟動事務

把值1更新成值2

查詢得到值v1

提交事務

查詢得到值v2

提交事務

查詢得到值v3

讀未提交可以讀取到其他事務未提交的值,所以v1讀取到了sessionb未提交的更改,值為2。

讀提交不能讀取到其他事務未提交的值,所以v1的值為1,當查詢v2時,sessionb已經提交了,這時讀取到的v2為2。

可重複讀查詢得到的值為事務啟動時的值,所以v1和v2值都為1。

序列化時,sessiona啟動事務後,sessionb會被卡住,直至sessiona的事務提交後才執行,所以v1和v2都是1,v3查詢時sessionb已經提交事務,所以v3的值為2。

v1v2

v3讀未提交22

2讀提交12

2可重複讀11

2序列化11

2上面的小測試一加深了我們對事務隔離級別的了解,下面我們針對可重複讀隔離級別下對小測試一進行修改再加深理解。例子如下:

sessiona

sessionb

啟動事務

查詢得v值為1

啟動事務

set v=v+1

查詢得到值v1

提交事務

set v=v+1

查詢得到值v2

提交事務

查詢得到值v3

小測試一可重複讀隔離級別下v1、v2的值都為1,v3的值為2。根據小測試一的思路,你可能會得出小測試二的結果為v1為1,v2為2,那麼v3是多少呢?

其實正確的答案是v1為1,v2為3,v3為3。如果sessiona中的set v=v+1是set v=1+1的話,這樣就丟失了sessionb更新的操作,使資料不正確,這時因為sessionb提交了,所以sessiona中的更新同乙個值為當前讀,為set v=2+1,所以v2為3。小測試一中讀取的資料在概念中為一致性讀

如果流程中把sessionb的提交事務放在sessiona的提交事務後面呢?這個時候sessiona的set v=v+1會被阻塞,直至sessionb的事務提交後才會執行,結果還是v1為1,v2為3,v3為3。

mysql的事務隔離級別預設為可重複讀,oracle的事務隔離級別預設為讀提交,所以在把oracle遷移至mysql時需要記得把mysql的設定讀提交後操作。

可重複讀在事務啟動時會建立乙個檢視,後繼查詢都在此檢視上操作。

讀提交在事務啟動時也會建立乙個檢視,但其他事務更新操作會直接更新此檢視。

讀未提交直接讀取資料庫中最新的資料。

序列化直接進行加鎖避免其他事務修改。

view1

view212

如上**所示檢視,行資料從1更新為2的過程對應檢視view1到view2的過程。

讀提交隔離級別下,事務啟動時是view1檢視,當sessionb發生更新資料後,檢視view1會更新成檢視view2,後續查詢在view2上進行。

可重複讀隔離級別下,事務啟動時是view1檢視,當sessionb發生更新資料後,事務還是照樣讀取view1檢視。

可重複讀隔離級別下,是怎麼找到view1檢視的呢?

其實這就是mvcc的功能了,事務在啟動時會獲得系統會分配乙個按順序遞增的transaction id,然後事務在更新資料時會把這個transaction id 分配給這一行資料當做trx row_id,順便記錄undo log日誌。比如上面sessiona事務在啟動時拿到的transaction id為889,sessionb的transaction id為890,那麼sessionb更新完後資料的trx row_id為890。在查詢v2的值時,sessiona發現資料版本為890,當前自己事務id為889,所以利用undo log「回滾」顯示出上一版本的資料,上一版本可以是889也可能是比889小的trx row_id,取值。

宣告一點,讀未提交、讀提交、可重複讀、序列化隔離級別依次遞增,隔離得越嚴實,效率就越低。

髒讀

事務讀取到其他事務修改後未提交的值。

不可重複讀

事務在啟動時讀取到的值,跟事務在執行過程中重新讀取到的值不一樣。

幻讀

事務啟動後,其他事務對資料庫新插入的行被本事務讀取到了。幻讀僅專指新插入的行

根據上述髒讀、不可重複讀、幻讀的概念與隔離級別能做到的,我可以得出以下關係:

髒讀不可重複讀

幻讀讀未提交

可能可能

可能讀提交

不可能可能

可能可重複讀

不可能不可能

可能序列化

不可能不可能

不可能a.atomicity.原子性

乙個事務的操作是要麼全部成功,要麼全部失敗。例如a給b轉賬100塊,保證a賬戶會減少100塊,b賬戶增加100塊,這100塊是a轉過來的,不出現a賬戶減少了,b賬戶卻沒對應增加100塊的情況。

原子性通過undo log實現,undo log記錄了回滾資訊,當事務失敗或進行rollback時,就會根據資訊回滾到操作前的樣子。

當delete時,undo log會相應記錄被刪除資料的資訊,當發生回滾時,進行insert操作。

當update時,undo log會記錄更新操作的資訊,當發生回滾時,進行update操作。

當insert時,undo log會記錄對應的主鍵,當發生回滾時,進行delete操作。

c.consistency.一致性

事務在執行前後,資料從一種合法的狀態,轉變成另一種合法的狀態。怎麼樣才算合法,都是可以自己定義的,比如事務啟動時a使用者賬戶了1000塊,b使用者有500塊,這個時候是啟動時合法的狀態。a使用者向b使用者轉賬100塊,a使用者賬戶餘額變成900,b變成600,這是結束時合法的狀態。或者平台會扣除1%的手續費,轉賬完成後a使用者餘額為899,b使用者變成600,平台賬戶增加1塊錢,這也是合法的狀態。

一致性是通過原子性、隔離性、永續性來保證的。所以只***了aid特性,才能保證c特性。

i.isolation.隔離性

乙個事務的查詢結果不會被其他事務所影響,事務之間是隔離的。例如x事務在啟動時查詢id=1的結果為1,x事務在沒有對此記錄進行修改操作的時候,多次查詢結果還是為1,不管其他事務是否對此記錄進行了修改。

隔離性通過mvcc實現,具體實現可以參考上面中的小測試邏輯。

d.durability.永續性

乙個事務執行完成了,所修改的資料能儲存至資料庫,不管宕機還是突然斷電,資料庫重新啟動後查詢到的資料為事務操作過後的資料。

永續性通過redo log實現,mysql中有個wal的邏輯,就是編輯資料時,會先更新redo log日誌,再更新記憶體,最後再寫磁碟。寫redo log日誌是乙個非常快的操作,而更新完記憶體後,寫進磁碟是很忙的。當還沒寫進磁碟時發生宕機等,mysql重啟後會自動去對比redo log日誌與磁碟內容,把未刷進磁碟的資料刷進磁碟中,從而保證了資料的永續性。

undo log

回滾日誌記錄資料修改的逆向還原操作記錄。例如資料值從1按順序修改成了2、3、4。那麼回滾日誌就會按順序記錄:2->1,3->2,4->3

mvcc

multi-version concurrency control是mysql資料庫中的多版本併發控制,就是同一條記錄在資料庫中是可以存在多個版本。依靠undo log與樂觀鎖實現。

樂觀鎖

樂觀地認為不會衝突,在最後需要修改資料的時候去拿一下鎖認證一下就好。常見的方法有版本號控制和時間戳控制。

悲觀鎖

悲觀的認為資料會被其他事務修改,所以在拿到資料時直接加上鎖,阻止其他事務進行修改。例如for update。

MySQL事務及事務隔離級別

原子性 atomicity 事務是乙個原子操作單元,其對資料的修改,要麼全都執行,要麼全都不執行。一致性 consistent 在事務開始和完成時,資料都必須保持一致狀態。這意味著所有相關的資料規則都必須應用於事務的修改,以保持資料的完整性 事務結束時,所有的內部資料結構 如b樹索引或雙向鍊錶 也都...

mysql 事務及隔離級別

原子性 回滾日誌 undo log 永續性 重做日誌 redo log 隔離性 鎖 一致性 通過aid及使用者自定義完整性保證 一致性是事務追求的最終目標 前面提到的原子性 永續性和隔離性,都是為了保證資料庫狀態的一致性。此外,除了資料庫層面的保障,一致性的實現也需要應用層面進行保障。一致性是指事務...

mysql 事務及隔離級別

事務 transaction 是由一系列對資料庫中的資料進行操作訪問所組成的乙個程式執行單元 在同乙個事務中所進行的操作,要麼都成功,要麼就什麼都不做。理想中的事務必須滿足四大特性,這就是大名鼎鼎的acid。a atomicity 原子性 是指乙個事務要麼全部執行,要麼不執行,也就是說乙個事務不可能...