理解MySQL事務

2022-11-22 06:00:55

理解MySQL事務

事務是什麼

百度百科是這麼定義的:

事務(Transaction),一般是指要做的或所做的事情。在計算機術語中是指存取並可能更新資料庫中各種資料項的一個程式執行單元。在關聯式資料庫中,一個事務可以是一條SQL語句,一組SQL語句或整個程式。

維基百科:

資料庫事務表示在資料庫管理系統內針對資料庫執行的工作單元,該工作單元以獨立於其他事務的連貫可靠的方式進行處理。事務通常表示資料庫中的任何更改。資料庫環境中的事務有兩個主要目的:

  1. 提供可靠的工作單元,允許從故障中正確恢復,即使在系統故障的情況下也能保持資料庫的一致性。

    例如,當執行過早和意外停止(完全或部分)時,在這種情況下,對資料庫的許多操作仍未完成,狀態不明確。

  2. 在並行存取資料庫的程式之間提供隔離。如果不提供這種隔離,則程式的結果可能是錯誤的。在資料庫中以一致模式完成的任何邏輯計算都稱為事務。一個例子是從一個銀行賬戶轉移到另一個銀行賬戶:完整的交易需要減去從一個賬戶轉賬的金額,然後將相同的金額新增到另一個賬戶。

對比來看,維基百科中的闡述更值得分析一下:

  1. 資料庫事務是什麼?

    資料庫事務表示在資料庫管理系統內針對資料庫執行的工作單元;事務通常表示資料庫中的任何更改。

也就是說,資料庫事務是針對於資料庫中的一組操作。這一組操作根據應用場景的不同,可能一個SQL搞定,也可能需要兩個,甚至N個。這多個SQL操作,被當成一個工作單元,它是個整體。維基百科還舉了一個我們很熟的例子:銀行轉賬,A給B轉賬,對應了兩個SQL操作,A的錢減少,B的錢增加,但這個是一件事,不能有中間態,即使轉賬失敗了,A、B的錢還是原樣。

  1. 事務的作用是什麼?

    提供可靠的工作單元,允許從故障中正確恢復。

    在並行存取資料庫的程式之間提供隔離。

    在資料庫中以一致模式完成的任何邏輯計算都稱為事務。

事務要讓工作單元可靠,即使發生故障,也能正確恢復;如果有多個執行緒存取資料庫時,每個執行緒之間互不影響,他們各自的操作在自己看來都是正常的;因此,事務的目的就是要維持工作單元的一致性。

可以總結出事務的四個特性,我們把它們稱作ACID:

  1. 原子性(Atomicity):原子性保證每個事務都被視為一個「單元」,它要麼完全成功,要麼完全失敗。如果事務中一個SQL執行失敗,則其他已執行的sql語句都會回滾,資料回退到事務前的狀態。
  2. 一致性(Consistency):一致性確保事務只能將資料庫從一個一致狀態帶到另一個一致狀態,保持資料庫不變性。也就是所有寫入資料庫的資料在既定的規則下,都是有效的。
  3. 隔離性(Isolation):隔離性是指,事務內部的操作與其他事務是隔離的,並行執行的各個事務之間不能互相干擾。
  4. 永續性(Durability):事務一旦提交,資料能存入硬碟,永久儲存。

一句話總結下事務,事務將資料庫中的一組操作看成一個工作單元,它要麼全部成功,要麼全部失敗,目的是為保證資料的一致性。

按照嚴格的標準,只有同時滿足ACID特性才是事務;但是在各巨量資料庫廠商的實現中,真正滿足ACID的事務少之又少。例如MySQL的NDB Cluster事務不滿足永續性和隔離性;InnoDB預設事務隔離級別是可重複讀,不滿足隔離性;Oracle預設的事務隔離級別為READ COMMITTED,不滿足隔離性......因此,與其說ACID是事務必須滿足的條件,不如說它們是衡量事務的四個維度。

開啟事務

MySQL預設是自動提交事務的,可以用sql查詢:

select @@autocommit;

也就是說,每執行完一條sql資料庫就會自動幫我們提交事務,但在實際情況中,一個業務操作會對應多條sql,不可能每執行完一條sql就提交,事務有多條sql時,需要執行完最後一條才能提交或者回滾。

MySQL中手動開啟事務有2種方式:

start transaction; -- 使用begin也可以

或者

set autocommit = 0;

手動開啟事務或者設定事務手動提交後,每次執行完一組sql都需要手動commit或者rollback才能生效。如果你一直沒有提交,或者一直沒回滾會怎麼樣?那肯定是,你後面的所有sql操作就跟之前的操作在一個事務裡,事務沒有提交,根據事務不同的隔離級別,資料的查詢會呈現不一樣的結果。

事務隔離級別

如果說ACID是事務的標準,那資料庫的隔離級別就是為了達到這個標準而採用的策略。MySQL事務隔離級別有四種,而每一種隔離級別又存在不同的事務問題。嚴格的說,只有serializable這種事務隔離級別,才真正滿足ACID,但是serializable會強制事務序列執行(類似Java的synchronized序列鎖),就是如果一個執行緒在還沒有提交事務,另一個執行緒就會一直等待,直到前一個執行緒提交事務,它才能繼續執行。它並行效率很低,實際使用的很少。而其他事務隔離級別,又存在著各自不同的事務問題。

隔離級別 髒讀 不可重複度 幻讀
read uncommitted(讀未提交)
read committed(讀已提交) ×
repeatable read(可重複讀) × ×
serializable(序列化) × × ×

檢視事務隔離級別:

 select @@transaction_isolation;

設定事務隔離級別:

set [session|global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable}

session代表當前對談,global代表全域性對談。設定全域性事務隔離級別:

set global transaction isolation level read uncommitted;

髒讀

事務A讀到事務B未提交的資料(髒資料),這種現象叫髒讀。

不可重複度

在事務A中先後兩次讀取同一個資料,兩次讀取的結果不一樣,這種現象稱為不可重複讀。髒讀與不可重複讀的區別在於:前者讀到的是其他事務未提交的資料,後者讀到的是其他事務已提交的資料。

幻讀

在事務A中按照某個條件先後兩次查詢資料庫,兩次查詢結果的條數不同,這種現象稱為幻讀。不可重複讀與幻讀的區別可以通俗的理解為:前者是資料變了,後者是資料的行數變了。

總結

事務具有ACID屬性,資料庫不同的事務隔離級別存在各自的事務問題,這也是面試常問的問題。

參考資料

深入學習MySQL事務:ACID特性的實現原理 - 程式設計迷思 - 部落格園 (cnblogs.com)