MySQL事務(transaction)看這篇就足夠瞭

MySQL事務處理(TransAction)

思考瞭很久,決定寫一篇關於mysql事務(transaction)的博客,一來嘛,因為最近在復習mysql的相關知識,幫自己回顧總結一下,其次就是想把這篇博客分享給大傢,如果你才剛剛開始學習mysql,那麼希望這篇博客對你有一點啟發;亦或者你早已是一個mysql老油條,這篇博客也會使你對mysql事務有一個更深的印象。

首先,什麼是事務呢?

事務就是由單獨單元的一個或多個sql語句組成,在這個單元中,每個sql語句都是相互依賴的。而整個單獨單元是作為一個不可分割的整體存在,類似於物理當中的原子(一種不可分割的最小單位)。

往通俗的講就是,事務就是一個整體,裡面的內容要麼都執行成功,要麼都不成功。不可能存在部分執行成功而部分執行不成功的情況。

就是說如果單元中某條sql語句一旦執行失敗或者產生錯誤,那麼整個單元將會回滾(返回最初狀態)。所有受到影響的數據將返回到事務開始之前的狀態,但是如果單元中的所有sql語句都執行成功的話,那麼該事務也就被順利執行。

大傢都知道,我們的數據都是通過各種不同技術的存儲引擎來引導存儲的,不同的存儲引擎,都有各自的特點。在mysql中,常見的存儲引擎有innodb、myisam,memory等。其中innodb支持事務(transaction),而myisam,memory等不支持事務。

可以通過show engines;語句來查看mysql支持的存儲引擎

一、事務的四個特性(ACID)【面試常考項】

  • 原子性(Atomicity):指事務是一個不可分割的最小工作單位,事務中的操作隻有都發生和都不發生兩種情況
  • 一致性(Consistency):事務必須使數據庫從一個一致狀態變換到另外一個一致狀態,舉一個栗子,李二給王五轉賬50元,其事務就是讓李二賬戶上減去50元,王五賬戶上加上50元;一致性是指其他事務看到的情況是要麼李二還沒有給王五轉賬的狀態,要麼王五已經成功接收到李二的50元轉賬。而對於李二少瞭50元,王五還沒加上50元這個中間狀態是不可見的。
  • 隔離性(Isolation):一個事務的執行不能被其他事務幹擾,即一個事務內部的操作及使用的數據對並發的其他事務是隔離的,並發執行的各個事務之間不能互相幹擾。
  • 持久性(Durability):一個事務一旦提交成功,它對數據庫中數據的改變將是永久性的,接下來的其他操作或故障不應對其有任何影響。

二、事務的分類

事務分為隱式事務和顯式事務兩種。我們的DML語句(insert、update、delete)就是隱式事務。

1.隱式事務:該事務沒有明顯的開啟和結束標記,它們都具有自動提交事務的功能;不妨思考一下,update語句修改數據時,是不是對表中數據進行改變瞭,它的本質其實就相當於一個事務。

舉一個栗子:張三同學買瞭一個csdn定制保溫杯花瞭99元,是不是就是update語句對字段name為張三的同學的餘額balance進行減99元的處理呢?代碼如下:

2.顯示事務:該事務具有明顯的開啟和結束標記;也是本文重點要講的東西。使用顯式事務的前提是你得先把自動提交事務的功能給禁用。禁用自動提交功能就是設置autocommit變量值為0(0:禁用 1:開啟)

先查看一下當前的autocommit變量值,發現當前處於開啟自動提交事務的狀態

禁用自動提交事務的功能並查看當前狀態

三、開啟事務的步驟

假設t_account表已經存在

#步驟一:開啟事務(可選)
start transaction;
#步驟二:編寫事務中的sql語句(insert、update、delete)
#這裡實現一下"李二給王五轉賬"的事務過程
update t_account set balance = 50 where vname = "李二";
update t_account set balance = 130 where vname = "王五";
#步驟三:結束事務
commit; #提交事務
# rollback; #回滾事務:就是事務不執行,回滾到事務執行前的狀態

運行結果:

四、事務並發時出現的問題

但是呢,因為某一刻不可能總隻有一個事務在運行,可能出現A在操作t_account表中的數據,B也同樣在操作t_account表,那麼就會出現並發問題,對於同時運行的多個事務,當這些事務訪問數據庫中相同的數據時,如果沒有采用必要的隔離機制,就會發生以下各種並發問題。

  1. 🌴臟讀:對於兩個事務T1,T2,T1讀取瞭已經被T2更新但還沒有被提交的字段之後,若T2回滾,T1讀取的內容就是臨時且無效的
  2. 🌴不可重復讀 :對於兩個事務T1,T2,T1讀取瞭一個字段,然後T2更新瞭該字段之後,T1在讀取同一個字段,值就不同瞭
  3. 🌴 幻讀:對於兩個事務T1,T2,T1在A表中讀取瞭一個字段,然後T2又在A表中插入瞭一些新的數據時,T1再讀取該表時,就會發現神不知鬼不覺的多出幾行瞭…

所以,為瞭避免以上出現的各種並發問題,我們就必然要采取一些手段。mysql數據庫系統提供瞭四種事務的隔離級別,用來隔離並發運行各個事務,使得它們相互不受影響,這就是數據庫事務的隔離性。

五、事務的隔離級別

mysql中的四種事務隔離級別如下:

1. read uncommitted(讀未提交數據):允許事務讀取未被其他事務提交的變更。(臟讀、不可重復讀和幻讀的問題都會出現)。

2. read committed(讀已提交數據):隻允許事務讀取已經被其他事務提交的變更。(可以避免臟讀,但不可重復讀和幻讀的問題仍然可能出現)

3.repeatable read(可重復讀):確保事務可以多次從一個字段中讀取相同的值,在這個事務持續期間,禁止其他事務對這個字段進行更新(update)。(可以避免臟讀和不可重復讀,但幻讀仍然存在)

4. serializable(串行化):確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入、更新和刪除操作,所有並發問題都可避免,但性能十分低下(因為你不完成就都不可以弄,效率太低)

瞭解: oracle支持兩種事務隔離級別:read committed、serializable。

oracle默認的事務隔離級別是:read committed。

mysql的默認事務隔離級別是:repeatable read。

一個事務與其他事務隔離的程度稱為隔離級別。數據庫規定瞭多種事務隔離級別,不同隔離級別對應不同的幹擾程度,隔離級別越高,數據一致性就越好,但並發性就越差。

這裡通過一個例子向大傢簡單介紹一下並發:

一個人🚶 在邊開車🚕 邊打電話📞,首先,人隻有一個大腦(cpu),但是在同一時刻他卻在執行2件事情,其實內部就是靠他的大腦在不斷的切換執行,之所以人民警察👮 不允許司機開車時打電話,就是怕人腦在那一瞬間切換不過來,從而導致交通事故的發生,並發和這個例子是差不多的意思。但在這裡,電腦cpu可比人腦快多瞭,所以出錯的概率也相對來說小很多。

接一下,演示一下在幾種不同的事務隔離級別下所發生的不同情況😶 。

在演示之前呢,還需要知道如何查看和設置事務的隔離級別

查看當前的事務隔離級別通過 tx_isolation變量或者transaction_isolation(版本8.0以上使用);

語法:select @@tx_isolation;

註意:在mysql8.0之後,就已經拋棄瞭tx_isolation變量瞭,而是用 transaction_isolation變量代替瞭。

語法:select @@transaction_isolation;

#設置當前mysql連接的隔離級別:
set session transaction isolation level read uncommitted;
#設置數據庫系統的全局的隔離級別:
set global transaction isolation level read uncommitted;

註意:當前mysql連接的隔離級別和mysql全局的隔離級別的區別是什麼?

如果隻設置當前的隔離級別,也就是session,那麼另外一個並發的“mysqy程序”的隔離級別不會受到當前連接的影響,而是保持默認的repeatable read。

但是如果是設置全局的事務隔離級別,則整個mysql數據庫(包括所有打開的mysql程序連接)的隔離級別都會隨之改變,除非服務器重啟,不然就不會恢復默認瞭。

兩者僅僅一詞之差,其效果卻天差地別。

好,瞭解完如何設置事務的隔離級別之後,下面將正式進入…

呃呃呃… 等一下

這裡的講解主要是為瞭知道在並發的環境下,不同的事務隔離級別所表現出的不同特點,那麼自然還是要先模擬一下並發環境

這裡打開兩個獨立的mysql數據庫連接(mysql程序1和mysql程序2),用來模擬並發環境

咳咳咳… 正片開始

同志們打起精神認真看啊!!

  • read uncommitted(讀未提交)

首先,我們需要先將兩個會話的事務隔離級別都設置為read uncommitted;語句如下:

read uncommitted可以讀到其他事務還沒提交的變更,這裡舉例:程序2對t_account表中的數據進行更改,看程序1多次查詢的結果是否一致。

可以看到程序2改變瞭t_account表中的vname字段,將李二改為瞭張三。但是程序1呢,連續2個select查詢語句的結果竟然不一致,估計現在程序1的表情和你手機裡的第三個表情包一樣。這就是read uncommitted隔離級別的特點。
不管你事務是否提交,隻要數據發生改變我就可以察覺到…嘻嘻。是不是感覺它很強大。什麼事情都逃不過它的法眼。

接下來要看的是read committed(讀已提交)

  • read committed(讀已提交)

測試前一定要記住設置事務的隔離級別為read committed;並且禁用自動提交事務【設置autocommit為0】(後面的每個測試都是一樣的)

# 設置事務隔離級別為read committed
set session transaction isolation level read committed;
# 禁用自動提交事務功能
set autocommit = 0; 
#接下來的 repeatable read 隔離級別和 serializable 隔離級別也是同樣的操作

以上例子實現瞭王五為張三轉賬的事務,可以看到程序2中事務提交前與提交後對程序1中的查詢語句產生的影響,前2個查詢是事務沒提交的結果,最後一個查詢是事務提交後的結果。 相信上面的例子已經很充分的詮釋瞭read committed的特點。

  • repeatable read(可重復讀)

該隔離級別為mysql的默認隔離級別;它對某字段進行操作時,其他事務禁止操作該字段。它總能保持你讀取的數據是一致的。

以下代碼中,程序1模擬"王五向張三轉賬30元",程序2則在程序1在處理事務時,對張三的餘額進行清空處理

因為當前的事務隔離級別為repeatable read級別,所以在程序1操作t_account表時,程序2是無權對t_account表進行任何操作,如果強行操作的話,就會發生error (錯誤)

“ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction”;

其中文意思是:“超過鎖定等待超時;嘗試重新啟動事務”

隻有當程序1對t_account表操作完成後(結束事務後),程序2才可以對t_account表進行操作。

  • serializable(串行化)

該隔離模式下執行的事務在對某表進行操作期間,禁止其他所有事務對該表進行任何操作

如果強行操作也會報錯(和上面那個錯誤一致),因為serializable用的相對比較少,這裡就不做演示瞭。同學們理解瞭就好。

  • 事務的保存點(回滾點)

回滾點表示的就是使事務回滾到指定回滾點

語法: savepoint 節點名稱 ;

註意:保存點隻允許搭配rollback回滾來使用,不能和commit一起使用

已知表t_stu存在,其數據如下:

代碼舉例如下:

 #禁用自動提交事務
set autocommit = 0;
 #開啟事務
start transaction;
 #刪除id為2的記錄
delete from t_stu where id = 2;
 #設置保存點名為AA
savepoint AA;
 #刪除id為3的記錄
delete from t_stu where id = 3;
  #回滾到AA保存點處
rollback to AA;

運行結果如下:

可以看到id為2的李四被刪除瞭,而王五卻還在,就是因為事務回滾到瞭AA處,所以id為3的那條記錄被回滾掉瞭。

總結

到此這篇關於MySQL事務(transaction)的文章就介紹到這瞭,更多相關MySQL事務transaction內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: