漫談MySql中的事務

2022-03-23 12:10:24 字數 3987 閱讀 2238

為什麼要有事務?

事務廣泛的運用於訂單系統、銀行系統等多種場景。如果有以下乙個場景:a使用者和b使用者是銀行的儲戶。現在a要給b轉賬500元。那麼需要做以下幾件事:

1. 檢查a的賬戶餘500元;

2. a賬戶扣除500元;

3. b賬戶增加500元;

正常的流程走下來,a賬戶扣了500,b賬戶加了500,皆大歡喜。那如果a賬戶扣了錢之後,系統出故障了呢?a白白損失了500,而b也沒有收到本該屬於他的500。以上的案例中,隱藏著乙個前提條件:a扣錢和b加錢,要麼同時成功,要麼同時失敗。事務的需求就在於此。

事務是什麼?

與其給事務定義,不如說一說事務的特性。眾所周知,事務需要滿足acid四個特性。

1. a(atomicity) 原子性。乙個事務的執行被視為乙個不可分割的最小單元。事務裡面的操作,要麼全部成功執行,要麼全部失敗回滾,不可以只執行其中的一部分。

2. c(consistency) 一致性。乙個事務的執行不應該破壞資料庫的完整性約束。如果上述例子中第2個操作執行後系統崩潰,保證a和b的金錢總計是不會變的。

3. i(isolation) 隔離性。通常來說,事務之間的行為不應該互相影響。然而實際情況中,事務相互影響的程度受到隔離級別的影響。文章後面會詳述。

4. d(durability) 永續性。事務提交之後,需要將提交的事務持久化到磁碟。即使系統崩潰,提交的資料也不應該丟失。

事務的四種隔離級別

前文中提到,事務的隔離性受到隔離級別的影響。那麼事務的隔離級別是什麼呢?事務的隔離級別可以認為是事務的"自私"程度,它定義了事務之間的可見性。隔離級別分為以下幾種:

1.read uncommitted(未提交讀)。在ru的隔離級別下,事務a對資料做的修改,即使沒有提交,對於事務b來說也是可見的,這種問題叫髒讀。這是隔離程度較低的一種隔離級別,在實際運用中會引起很多問題,因此一般不常用。

2.read committed(提交讀)。在rc的隔離級別下,不會出現髒讀的問題。事務a對資料做的修改,提交之後會對事務b可見,舉例,事務b開啟時讀到資料1,接下來事務a開啟,把這個資料改成2,提交,b再次讀取這個資料,會讀到最新的資料2。在rc的隔離級別下,會出現不可重複讀的問題。這個隔離級別是許多資料庫的預設隔離級別。

3.repeatable read(可重複讀)。在rr的隔離級別下,不會出現不可重複讀的問題。事務a對資料做的修改,提交之後,對於先於事務a開啟的事務是不可見的。舉例,事務b開啟時讀到資料1,接下來事務a開啟,把這個資料改成2,提交,b再次讀取這個資料,仍然只能讀到1。在rr的隔離級別下,會出現幻讀的問題。幻讀的意思是,當某個事務在讀取某個範圍內的值的時候,另外乙個事務在這個範圍內插入了新記錄,那麼之前的事務再次讀取這個範圍的值,會讀取到新插入的資料。mysql預設的隔離級別是rr,然而mysql的innodb引擎間隙鎖成功解決了幻讀的問題。

4.serializable(可序列化)。可序列化是最高的隔離級別。這種隔離級別強制要求所有事物序列執行,在這種隔離級別下,讀取的每行資料都加鎖,會導致大量的鎖徵用問題,效能最差。

為了幫助理解四種隔離級別,這裡舉個例子。如圖1,事務a和事務b先後開啟,並對資料1進行多次更新。四個小人在不同的時刻開啟事務,可能看到資料1的哪些值呢?

圖1第乙個小人,可能讀到1-20之間的任何乙個。因為未提交讀的隔離級別下,其他事務對資料的修改也是對當前事務可見的。第二個小人可能讀到1,10和20,他只能讀到其他事務已經提交了的資料。第三個小人讀到的資料去決於自身事務開啟的時間點。在事務開啟時,讀到的是多少,那麼在事務提交之前讀到的值就是多少。第四個小人,只有在a end 到b start之間開啟,才有可能讀到資料,而在事務a和事務b執行的期間是讀不到資料的。因為第四小人讀資料是需要加鎖的,事務a和b執行期間,會占用資料的寫鎖,導致第四個小人等待鎖。

圖2羅列了不同隔離級別所面對的問題。

圖2很顯然,隔離級別越高,它所帶來的資源消耗也就越大(鎖),因此它的併發效能越低。準確的說,在可序列化的隔離級別下,是沒有併發的。

圖3mysql中的事務

事務的實現是基於資料庫的儲存引擎。不同的儲存引擎對事務的支援程度不一樣。mysql中支援事務的儲存引擎有innodb和ndb。innodb是mysql預設的儲存引擎,預設的隔離級別是rr,並且在rr的隔離級別下更進一步,通過多版本併發控制(mvcc,multiversion concurrency control)解決不可重複讀問題,加上間隙鎖(也就是併發控制)解決幻讀問題。因此innodb的rr隔離級別其實實現了序列化級別的效果,而且保留了比較好的併發效能。

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

1.redo log

在innodb的儲存引擎中,事務日誌通過重做(redo)日誌和innodb儲存引擎的日誌緩衝(innodb log buffer)實現。事務開啟時,事務中的操作,都會先寫入儲存引擎的日誌緩衝中,在事務提交之前,這些緩衝的日誌都需要提前重新整理到磁碟上持久化,這就是dba們口中常說的「日誌先行」(write-ahead logging)。當事務提交之後,在buffer pool中對映的資料檔案才會慢慢重新整理到磁碟。此時如果資料庫崩潰或者宕機,那麼當系統重啟進行恢復時,就可以根據redo log中記錄的日誌,把資料庫恢復到崩潰前的乙個狀態。未完成的事務,可以繼續提交,也可以選擇回滾,這基於恢復的策略而定。

在系統啟動的時候,就已經為redo log分配了一塊連續的儲存空間,以順序追加的方式記錄redo log,通過順序io來改善效能。所有的事務共享redo log的儲存空間,它們的redo log按語句的執行順序,依次交替的記錄在一起。如下乙個簡單示例:

記錄1:

記錄2:

記錄3:

記錄4:

記錄5:

2.undo log

undo log主要為事務的回滾服務。在事務執行的過程中,除了記錄redo log,還會記錄一定量的undo log。undo log記錄了資料在每個操作前的狀態,如果事務執行過程中需要回滾,就可以根據undo log進行回滾操作。單個事務的回滾,只會回滾當前事務做的操作,並不會影響到其他的事務做的操作。

以下是undo+redo事務的簡化過程

假設有2個數值,分別為a和b,值為1,2

1. start transaction;

2. 記錄 a=1 到undo log;

3. update a = 3;

4. 記錄 a=3 到redo log;

5. 記錄 b=2 到undo log;

6. update b = 4;

7. 記錄b = 4 到redo log;

8. 將redo log重新整理到磁碟

9. commit

在1-8的任意一步系統宕機,事務未提交,該事務就不會對磁碟上的資料做任何影響。如果在8-9之間宕機,恢復之後可以選擇回滾,也可以選擇繼續完成事務提交,因為此時redo log已經持久化。若在9之後系統宕機,記憶體對映中變更的資料還來不及刷回磁碟,那麼系統恢復之後,可以根據redo log把資料刷回磁碟。

所以,redo log其實保障的是事務的永續性和一致性,而undo log則保障了事務的原子性。

分布式事務的實現方式有很多,既可以採用innodb提供的原生的事務支援,也可以採用訊息佇列來實現分布式事務的最終一致性。這裡我們主要聊一下innodb對分布式事務的支援。

如圖,mysql的分布式事務模型。模型中分三塊:應用程式(ap)、資源管理器(rm)、事務管理器(tm)。

應用程式定義了事務的邊界,指定需要做哪些事務;

資源管理器提供了訪問事務的方法,通常乙個資料庫就是乙個資源管理器;

事務管理器協調參與了全域性事務中的各個事務。

分布式事務採用兩段式提交(two-phase commit)的方式。第一階段所有的事務節點開始準備,告訴事務管理器ready。第二階段事務管理器告訴每個節點是commit還是rollback。如果有乙個節點失敗,就需要全域性的節點全部rollback,以此保障事務的原子性。

總結什麼時候需要使用事務呢?我想,只要業務中需要滿足acid的場景,都需要事務的支援。尤其在訂單系統、銀行系統中,事務是不可或缺的。這篇文章主要介紹了事務的特性,以及mysql innodb對事務的支援。事務相關的知識遠不止文中所說,本文僅作拋磚引玉,不足之處還望讀者多多見諒。

mysql中事務的特性 mysql中事務的四大特性

原子性 atomicity 事務就像 原子 一樣,不可被分割,組成事務的dml操作語句要麼全成功,要麼全失敗,不可能出現部分成功部分失敗的情況。一致性 consistency 一旦事務完成,不管是成功的,還是失敗的,整個系統處於資料一致的狀態。隔離性 isolation 乙個事務的執行不會被另乙個事...

mysql中的事務和鎖 MySQL中的事務和鎖

鎖 行級鎖select for update nowaitfalse,skip false 注意必須用在事物裡面 所有匹配的行將被鎖定,知道事務結束。這意味著可以通過鎖防止資料被其他事務修改。一般情況下如果其他事務鎖定了相關行,那麼本查詢將被阻塞,直到鎖被釋放。事務事務的四大特性 1.原子性 事務包...

MySql中的事務

一.mysql的事務支援不是繫結在mysql伺服器本身,而是與儲存引擎相關 1.myisam 不支援事務,用於唯讀程式提高效能 2.innodb 支援acid事務 行級鎖 併發 二.乙個事務是乙個連續的一組資料庫操作,就好像它是乙個單一的工作單元進行。換言之,永遠不會是完整的事務,除非該組內的每個單...