一文帶你徹底了解MySQL事務機制
我們設想一個場景,這個場景中我們需要插入多條相關聯的數據到數據庫,不幸的是,這個過程可能會遇到下面這些問題:
數據庫中途突然因為某些原因掛掉了。客戶端突然因為網絡原因連接不上數據庫了。并發訪問數據庫時,多個線程同時寫入數據庫,覆蓋了彼此的更改。…上面的任何一個問題都可能會導致數據的不一致性。為了保證數據的一致性,系統必須能夠處理這些問題。事務就是我們抽象出來簡化這些問題的首選機制。
事務的概念起源于數據庫,目前已經成為一個比較廣泛的概念。
何謂事務?一個事情由n個單元組成,這n個單元在執行過程中,要么同時成功,要么同時失敗,這就把n個單元放在了一個事務之中。
舉個簡單的例子:在不考慮試題正確與否的前提下,一張試卷由多個題目構成,當你答完題交給老師的時候是將一整張試卷交給老師,而不是將每道題單獨交給老師,在這里試卷就可以理解成一個事務。
事務的特性:ACIDA:原子性(Atomicity),原子性是指事務是一個不可分割的工作單位,事務中的操作,要么都發生,要么都不發生。
例:假設你在購物車里添加了兩件衣服:上衣和褲子,當你把兩件衣服作為一個訂單提交支付的時候,要么兩件衣服一起支付成功,要么都失敗,不可能存在上衣付完錢了,褲子還沒付完的情況,反之亦然。
C:一致性(Consistency),在一個事務中,事務前后數據的完整性必須保持一致。
例:假設用戶A和用戶B兩者的錢加起來一共是200,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是200,這就是事務的一致性。
I:隔離性(Isolation),存在于多個事務中,事務的隔離性是指多個用戶并發訪問數據庫時,一個用戶的事務不能被其它用戶的事務所干擾,多個并發事務之間數據要相互隔離。
例:對于任意兩個并發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在并發地執行。
D:持久性(Durability),持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。
例:我們在操作數據庫時,事務提交或者回滾都會直接改變數據庫中的值。
AID是手段,C是目的,AID都是為了保證數據的一致性。
事務的操作在使用事務之前,首先我們要開啟事務,我們可以通過start或者begin命令開啟事務;如果我們想提交事務可以手動執行commit命令,如果我們想回滾事務,可以執行rollback命令。
# 開啟一個事務START TRANSACTION;# 多條 SQL 語句SQL1,SQL2...## 提交事務COMMIT;注:在MySQL中事務的提交是默認開啟的,可以執行show variables like 'autocommit'命令查看,如果是ON則證明自動提交已經開啟,如果為OFF則需要手動提交。
隔離性引發的并發問題1)臟讀:B事務讀取到了A事務尚未提交的數據;
一個事務讀取數據并且對數據進行了修改,這個修改對其他事務來說是可見的,即使當前事務沒有提交。這時另外一個事務讀取了這個還未提交的數據,但第一個事務突然回滾,導致數據并沒有被提交到數據庫,那第二個事務讀取到的就是臟數據,這也就是臟讀的由來。
2)不可重復讀:B事務讀到了A事務已經提交的數據,即B事務在A事務提交之前和提交之后讀取到的數據內容不一致(AB事務操作的是同一條數據);
事務 1 讀取某表中的數據 A=20,事務 2 也讀取 A=20,事務 1 修改 A=A-1,事務 2 再次讀取 A =19,此時讀取的結果和第一次讀取的結果不同。
3)幻讀/虛讀:B事務讀到了A事務已經提交的數據,即A事務執行插入操作,B事務在A事務前后讀到的數據數量不一致。
事務 2 讀取某個范圍的數據,事務 1 在這個范圍插入了新的數據,事務 2 再次讀取這個范圍的數據發現相比于第一次讀取的結果多了新的數據。
不可重復讀和幻讀有什么區別不可重復讀的重點是內容修改或者記錄減少比如多次讀取一條記錄發現其中某些記錄的值被修改;幻讀的重點在于記錄新增比如多次執行同一條查詢語句(DQL)時,發現查到的記錄增加了。幻讀其實可以看作是不可重復讀的一種特殊情況,單獨把區分幻讀的原因主要是解決幻讀和不可重復讀的方案不一樣。
舉個例子:執行 delete 和 update 操作的時候,可以直接對記錄加鎖,保證事務安全。而執行 insert 操作的時候,由于記錄鎖(Record Lock)只能鎖住已經存在的記錄,為了避免插入新記錄,需要依賴間隙鎖(Gap Lock)。也就是說執行 insert 操作的時候需要依賴 Next-Key Lock(Record Lock+Gap Lock) 進行加鎖來保證不出現幻讀。
事務的隔離級別為了解決以上隔離性引發的并發問題,數據庫提供了事物的隔離機制。
read uncommitted(讀未提交): 一個事務還沒提交時,它做的變更就能被別的事務看到,讀取尚未提交的數據,哪個問題都不能解決;read committed(讀已提交):一個事務提交之后,它做的變更才會被其他事務看到,讀取已經提交的數據,可以解決臟讀 ---- oracle默認的;repeatable read(可重復讀):一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的,可以解決臟讀和不可重復讀 —mysql默認的;serializable(串行化):顧名思義是對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。可以解決臟讀、不可重復讀和虛讀—相當于鎖表。雖然serializable級別可以解決所有的數據庫并發問題,但是它會在讀取的每一行數據上都加鎖,這就可能導致大量的超時和鎖競爭問題,從而導致效率下降。所以我們在實際應用中也很少使用serializable,只有在非常需要確保數據的一致性而且可以接受沒有并發的情況下,才考慮采用該級別。
MySQL 的隔離級別是基于鎖實現的嗎?MySQL 的隔離級別基于鎖和 MVCC 機制共同實現的。
SERIALIZABLE 隔離級別是通過鎖來實現的,READ-COMMITTED 和 REPEATABLE-READ 隔離級別是基于 MVCC 實現的。不過, SERIALIZABLE 之外的其他隔離級別可能也需要用到鎖機制,就比如 REPEATABLE-READ 在當前讀情況下需要使用加鎖讀來保證不會出現幻讀。
默認隔離級別MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重復讀)。我們可以通過 SELECT @@tx_isolation;命令來查看,MySQL 8.0 該命令改為SELECT @@transaction_isolation;
# 開啟一個事務START TRANSACTION;# 多條 SQL 語句SQL1,SQL2...## 提交事務COMMIT;從上面對 SQL 標準定義了四個隔離級別的介紹可以看出,標準的 SQL 隔離級別定義里,REPEATABLE-READ(可重復讀)是不可以防止幻讀的。
但是 InnoDB 實現的 REPEATABLE-READ 隔離級別其實是可以解決幻讀問題發生的,主要有下面兩種情況:
快照讀 :由 MVCC 機制來保證不出現幻讀。當前讀 :使用 Next-Key Lock(臨鍵鎖) 進行加鎖來保證不出現幻讀,Next-Key Lock 是行鎖(Record Lock)和間隙鎖(Gap Lock)的結合,行鎖只能鎖住已經存在的行,為了避免插入新行,需要依賴間隙鎖。InnoDB 存儲引擎在分布式事務的情況下一般會用到 SERIALIZABLE 隔離級別。
解決幻讀的方法解決幻讀的方式有很多,但是它們的核心思想就是一個事務在操作某張表數據的時候,另外一個事務不允許新增或者刪除這張表中的數據了。解決幻讀的方式主要有以下幾種:
將事務隔離級別調整為 SERIALIZABLE 。在可重復讀的事務級別下,給事務操作的這張表添加表鎖。在可重復讀的事務級別下,給事務操作的這張表添加 Next-key Lock(Record Lock+Gap Lock)總結看到這,你對事務是否已經有了清晰的認識?接下來我們就簡單總結下今天的知識點:
事務的概念:一個事情由n個單元組成,這n個單元在執行過程中,要么同時成功,要么同時失敗,這就把n個單元放在了一個事務之中。事務的特性:ACID事務的操作:開啟事務、提交事務、回滾事務事務的隔離級別:讀未提交、讀已提交、可重復讀、串行化解決幻讀的方法:鎖機制和MVCC機制以上就是一文帶你徹底了解MySQL事務機制的詳細內容,更多關于MySQL事務機制的資料請關注好吧啦網其它相關文章!
相關文章:
