欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解MySQL如何保證數據一致性

 更新時間:2024年01月04日 08:55:58   作者:王二蛋!  
對于一個數據庫而言,除了數據的持久性、不丟失之外,一致性也是非常重要的,不然這個數據是沒有任何意義的,在使用MySQL時,數據不一致的情況也可能出現,所以,本文就來看看MySQL是如何保證數據一致的,需要的朋友可以參考下

MySQL保證的一致性

在這之前先劃清一下界限,看一下MySQL保證的是哪里的一致性。

拿一個最簡單的轉賬例子,用戶A向用戶B轉1000元,正常的sql是這樣的

update account set balance=balance-4000 where user='A' and balance >= 4000;
update account set balance=balance+4000 where user='B';

示例表數據如下

在這里插入圖片描述

如果最終用戶A賬戶沒有扣4000,而用戶B賬戶多了4000,總金額也無緣無故的多了4000。這個時候就造成了數據不一致了。出現這個問題可能存在幾個原因:

  • 在MySQL客戶端執(zhí)行sql時沒有做校驗。如果用戶A余額并不足以4000,由于沒有校驗,兩條sql都會成功執(zhí)行,用戶B就會憑空多出4000。
  • 兩條sql并不在同一事務中??赡躶ql1執(zhí)行失敗了,sql2執(zhí)行成功,由于不再一個事務中導致用戶B多了4000。
  • 在MySQL內部執(zhí)行時因為某些故障而出現了不一致情況。

很顯然,第三點是需要MySQL解決處理的。而第一點是屬于MySQL客戶端的邏輯BUG,第二點會存在客戶端在使用事務時不遵循規(guī)則的情況,都屬于外部因素,MySQL不可控。

所以,MySQL保證的一致性是:在一個事務中的DML(增刪改)操作。盡管DML本身可能存在問題。

MySQL發(fā)生不一致環(huán)節(jié)

劃清界限后再分析一下在DML執(zhí)行過程中,哪個環(huán)節(jié)會發(fā)生數據不一致。 以上面的sql為例,假設已經進行過校驗且在同一事務。

并發(fā)沖突

在執(zhí)行第一條sql時,「執(zhí)行器」會通過條件user='A' and balance >= 4000在「存儲引擎」獲取到符合條件的記錄,然后進行balance扣減操作。(不知道這個流程的可以看下前面的文章)

如果這個時候存在并發(fā)現象,扣減操作可能會執(zhí)行多次,這個balance肯定就不是預想中的結果了,也就發(fā)生數據不一致了。如下圖

在這里插入圖片描述

當3個update請求同一時間調用存儲引擎對同一數據頁更新后,正常情況下,balance值應該為0。但是因為并發(fā)操作,balance的值可能會被修改為-1000或者-2000等其他值,這樣的bug顯然是不可被接受的。

有并發(fā)經驗的應該都知道需要通過鎖資源可以避免這個情況,InnoDB也是通過加來處理的。

redolog不完整

通過上文可以知道,InnoDB是通過「雙寫緩沖」、「Redo Log」等機制保證數據不丟失的。

這種情況下,假設第一條sql執(zhí)行成功并且對應的redo log已經被刷新到磁盤中,但是第二條sql執(zhí)行失敗或者MySQL服務宕機導致其redolog未刷新到磁盤,那么在下次啟動恢復時,就會發(fā)生數據不一致了。如下圖

在這里插入圖片描述

sql示例的第一條執(zhí)行結果通過redolog恢復了,但是第二條的redolog隨著宕機丟失了,于是乎造成了數據的不一致。(redo log的刷盤機制和構建臟頁可以通過上文進行了解。)

對于這種情況,InnoDB是通過上文提到的「Undo Log」來解決的。

binlog&redolog不一致

我們知道,binlog中記錄了所有對數據更新的原始sql,以便數據備份恢復、主從復制。與redolog不一樣的是binlog屬于MySQL server層,而redolog是InnoDB的機制,用于故障恢復,兩者并不沖突,這里不過多贅述。

雖然不沖突,但是要保證兩者在事務提交后都可以持久化到磁盤,不然就會在主從復制的時候出現數據不一致現象,如下圖

在這里插入圖片描述

只要binlog和redolog有一方沒有同步持久到磁盤都會發(fā)生類似現象。針對這種情況MySQL是通過兩階段提交解決的。

以上就是DML在執(zhí)行過程中可能出現不一致的環(huán)節(jié)(沒有想到的歡迎評論交流)。接下來具體看一下InnoDB針對以上幾種情況是如何處理解決,從而保證數據一致性的。

MySQL解決不一致方案

加鎖解決并發(fā)沖突

鎖沒有什么好說的,innoDB根據隔離級別決定是否用鎖(當然,還有server層的表鎖什么的這里不展開)。這里就演示下在隔離級別REPEATABLE-READ下,鎖在SQL執(zhí)行中的具體作用和效果。

當在第一個事務中執(zhí)行 update account set balance=balance-4000 where user='A' and balance >= 4000; 時,其他事務不能對user為’A’的記錄進行更新。如下圖,當第二個事務窗口執(zhí)行 update account set balance=balance-1000 where user='A' and balance >= 1000; 時會被阻塞住,直到第一個事務提交或者超時。

在這里插入圖片描述

這個時候可以通過 select * from sys.innodb_lock_waits ; 查看一下鎖的相關信息

在這里插入圖片描述

這里的locked_type是RECORD,也就是行記錄鎖,還有一個是間隙鎖。

間隙鎖的作用是保證某個范圍內的數據在鎖定情況下不會發(fā)生任何變化。比如,當第一個事務執(zhí)行update account set balance=balance-100 where id between 7 and 9;后,第二個事務在執(zhí)行INSERT INTO account (id, user,balance) VALUES (8, 'ABD',5000);時會阻塞,但是執(zhí)行 INSERT INTO account (id, user,balance) VALUES (16, 'ABDD',5000);會成功執(zhí)行,因為插入id為16的行數據不會影響到7~9之間的數據。這個時候去查看select * from sys.innodb_lock_waits;時會發(fā)現waiting_lock_mode值為 X,GAP(間隙)

在這里插入圖片描述

所以說,鎖避免了事務的并發(fā)訪問導致的數據不一致。

undolog解決redolog不完整

InnoDB在因sql執(zhí)行失敗或者MySQL服務宕機導致redolog不完整從而出現數據不一致是這么解決的:

  • 在更新數據頁之前,InnoDB會先將數據當前的狀態(tài)記錄在「Undo Log」中。
  • 之后,再將更新后的相關數據記錄到「Redo Log」中。

這樣的話,不論出現哪種情況都可以通過undo log將數據回滾并保持一致,這個就是經常提到的原子性以及「回滾」操作。

就如上圖(redo log不完整環(huán)節(jié)),加上Undo log之后數據狀態(tài)如下圖

在這里插入圖片描述

圖中加了行記錄的隱藏字段事務ID和回滾指針以及undo log頁和undo的redo。

undo log 記錄的就是user='A’和‘B’事務提交前的數據,各為4000。

redo log 中會記錄所有的更新操作,包括undo,因為undo記錄的也是更新語句。需要說一下,這里記錄的undo是演示使用,對于一條update操作,真正的undo會記錄一條delete和一條insert操作,原因上文有介紹。

為什么redo log會記錄undo

undo log是以頁為單位,跟隨頁的刷新機制,會存在丟失的情況,所以在記錄undo后也會將該undo記錄到redo,避免undo丟失,一旦undo丟失就回滾不了了。

有了undo log后,假設第二條sql執(zhí)行失敗,這個時候就會通過行記錄中的事務ID(txidx)和回滾指針(roll_pointx、roll_pointx1)去undolog中找對應的回滾操作(如圖中的 ‘**回滾指針’**箭頭),最終將事務回滾保證原子性和一致性。

針對上圖的狀態(tài),如果發(fā)生宕機,那么在重新MySQL服務時,會有兩個操作:

  • 會先通過redo log構建「臟頁」。
  • 根據redo log中記錄的事務提交狀態(tài)來決定是否回滾。

如圖

在這里插入圖片描述

當前user='A’的事務狀態(tài)為prepare,所以需要進行回滾操作?;貪L流程是這樣的:

  • 根據數據中該記錄的事務ID(txidx)在undolog中找對應的回滾操作。
  • 發(fā)現事務ID有兩個undo操作,user='A’和‘B’的。
  • 執(zhí)行undo操作,將數據頁中的記錄回滾至事務提交前狀態(tài)。

最終的結果就是user='A’和‘B’的balance會回滾到4000。

所以說,undo避免了事務或者宕機的異常導致的數據不一致。

XA兩階段提交解決binlog和redolog的不一致

redo log中的事務狀態(tài)不僅在這里起到作用,在binlog和redolog的一致上,同樣是通過這個狀態(tài)來判斷并且決定是否需要回滾。

這個就不得不說到MySQL的XA兩階段提交協(xié)議了,在這之前,我一直以為XA是運用到MySQL與外部應用的,沒想到是應用在MySQL內部的。不過分布式事務嘛,原理基本上都一樣。

XA的兩階段分別是prepare和commit,在事務提交前,redolog中記錄的狀態(tài)都是prepare,當事務提交后,該狀態(tài)就會被更新為commit,同時將XID寫入到對應的binlog中并刷新到磁盤。如下圖

在這里插入圖片描述

這樣的話,如果發(fā)生宕機,下次啟動時可以根據redolog中的狀態(tài)以及XID去binlog中查找,如果存在意味著兩者一致,不存在就進行回滾操作。

所以說,XA兩階段提交保證了binlog和redolog邏輯一致,從而避免主從節(jié)點的數據不一致。

總結

MySQL一致性的保證基本上涉及到InnoDB存儲引擎的各個組件,「Buffer Pool」、「Log Buffer」、「Redo Log」、「Undo Log」等,還有DML操作的流程、鎖、故障恢復等功能。最后再總結下MySQL是如何保證一致性的。

  • 對于并發(fā)操作帶來的數據不一致性問題,InnoDB通過鎖來解決。
  • 對于可能會發(fā)生的redolog不完整的情況,InnoDB通過Undo Log來解決。
  • 對于redolog&binlog不一致帶來的主從節(jié)點數據不一致,MySQL是通過XA兩階段提交來解決。

以上就是詳解MySQL如何保證數據一致性的詳細內容,更多關于MySQL保證數據一致性的資料請關注腳本之家其它相關文章!

相關文章

最新評論