亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解MySQL如何保證數(shù)據(jù)一致性

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

MySQL保證的一致性

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

拿一個(gè)最簡(jiǎn)單的轉(zhuǎn)賬例子,用戶A向用戶B轉(zhuǎn)1000元,正常的sql是這樣的

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

示例表數(shù)據(jù)如下

在這里插入圖片描述

如果最終用戶A賬戶沒(méi)有扣4000,而用戶B賬戶多了4000,總金額也無(wú)緣無(wú)故的多了4000。這個(gè)時(shí)候就造成了數(shù)據(jù)不一致了。出現(xiàn)這個(gè)問(wèn)題可能存在幾個(gè)原因:

  • 在MySQL客戶端執(zhí)行sql時(shí)沒(méi)有做校驗(yàn)。如果用戶A余額并不足以4000,由于沒(méi)有校驗(yàn),兩條sql都會(huì)成功執(zhí)行,用戶B就會(huì)憑空多出4000。
  • 兩條sql并不在同一事務(wù)中??赡躶ql1執(zhí)行失敗了,sql2執(zhí)行成功,由于不再一個(gè)事務(wù)中導(dǎo)致用戶B多了4000。
  • 在MySQL內(nèi)部執(zhí)行時(shí)因?yàn)槟承┕收隙霈F(xiàn)了不一致情況。

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

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

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

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

并發(fā)沖突

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

如果這個(gè)時(shí)候存在并發(fā)現(xiàn)象,扣減操作可能會(huì)執(zhí)行多次,這個(gè)balance肯定就不是預(yù)想中的結(jié)果了,也就發(fā)生數(shù)據(jù)不一致了。如下圖

在這里插入圖片描述

當(dāng)3個(gè)update請(qǐng)求同一時(shí)間調(diào)用存儲(chǔ)引擎對(duì)同一數(shù)據(jù)頁(yè)更新后,正常情況下,balance值應(yīng)該為0。但是因?yàn)椴l(fā)操作,balance的值可能會(huì)被修改為-1000或者-2000等其他值,這樣的bug顯然是不可被接受的。

有并發(fā)經(jīng)驗(yàn)的應(yīng)該都知道需要通過(guò)鎖資源可以避免這個(gè)情況,InnoDB也是通過(guò)加來(lái)處理的。

redolog不完整

通過(guò)上文可以知道,InnoDB是通過(guò)「雙寫(xiě)緩沖」、「Redo Log」等機(jī)制保證數(shù)據(jù)不丟失的。

這種情況下,假設(shè)第一條sql執(zhí)行成功并且對(duì)應(yīng)的redo log已經(jīng)被刷新到磁盤(pán)中,但是第二條sql執(zhí)行失敗或者M(jìn)ySQL服務(wù)宕機(jī)導(dǎo)致其redolog未刷新到磁盤(pán),那么在下次啟動(dòng)恢復(fù)時(shí),就會(huì)發(fā)生數(shù)據(jù)不一致了。如下圖

在這里插入圖片描述

sql示例的第一條執(zhí)行結(jié)果通過(guò)redolog恢復(fù)了,但是第二條的redolog隨著宕機(jī)丟失了,于是乎造成了數(shù)據(jù)的不一致。(redo log的刷盤(pán)機(jī)制和構(gòu)建臟頁(yè)可以通過(guò)上文進(jìn)行了解。)

對(duì)于這種情況,InnoDB是通過(guò)上文提到的「Undo Log」來(lái)解決的。

binlog&redolog不一致

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

雖然不沖突,但是要保證兩者在事務(wù)提交后都可以持久化到磁盤(pán),不然就會(huì)在主從復(fù)制的時(shí)候出現(xiàn)數(shù)據(jù)不一致現(xiàn)象,如下圖

在這里插入圖片描述

只要binlog和redolog有一方?jīng)]有同步持久到磁盤(pán)都會(huì)發(fā)生類似現(xiàn)象。針對(duì)這種情況MySQL是通過(guò)兩階段提交解決的。

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

MySQL解決不一致方案

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

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

當(dāng)在第一個(gè)事務(wù)中執(zhí)行 update account set balance=balance-4000 where user='A' and balance >= 4000; 時(shí),其他事務(wù)不能對(duì)user為’A’的記錄進(jìn)行更新。如下圖,當(dāng)?shù)诙€(gè)事務(wù)窗口執(zhí)行 update account set balance=balance-1000 where user='A' and balance >= 1000; 時(shí)會(huì)被阻塞住,直到第一個(gè)事務(wù)提交或者超時(shí)。

在這里插入圖片描述

這個(gè)時(shí)候可以通過(guò) select * from sys.innodb_lock_waits ; 查看一下鎖的相關(guān)信息

在這里插入圖片描述

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

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

在這里插入圖片描述

所以說(shuō),鎖避免了事務(wù)的并發(fā)訪問(wèn)導(dǎo)致的數(shù)據(jù)不一致。

undolog解決redolog不完整

InnoDB在因sql執(zhí)行失敗或者M(jìn)ySQL服務(wù)宕機(jī)導(dǎo)致redolog不完整從而出現(xiàn)數(shù)據(jù)不一致是這么解決的:

  • 在更新數(shù)據(jù)頁(yè)之前,InnoDB會(huì)先將數(shù)據(jù)當(dāng)前的狀態(tài)記錄在「Undo Log」中。
  • 之后,再將更新后的相關(guān)數(shù)據(jù)記錄到「Redo Log」中。

這樣的話,不論出現(xiàn)哪種情況都可以通過(guò)undo log將數(shù)據(jù)回滾并保持一致,這個(gè)就是經(jīng)常提到的原子性以及「回滾」操作。

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

在這里插入圖片描述

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

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

redo log 中會(huì)記錄所有的更新操作,包括undo,因?yàn)閡ndo記錄的也是更新語(yǔ)句。需要說(shuō)一下,這里記錄的undo是演示使用,對(duì)于一條update操作,真正的undo會(huì)記錄一條delete和一條insert操作,原因上文有介紹。

為什么redo log會(huì)記錄undo

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

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

針對(duì)上圖的狀態(tài),如果發(fā)生宕機(jī),那么在重新MySQL服務(wù)時(shí),會(huì)有兩個(gè)操作:

  • 會(huì)先通過(guò)redo log構(gòu)建「臟頁(yè)」。
  • 根據(jù)redo log中記錄的事務(wù)提交狀態(tài)來(lái)決定是否回滾。

如圖

在這里插入圖片描述

當(dāng)前user='A’的事務(wù)狀態(tài)為prepare,所以需要進(jìn)行回滾操作。回滾流程是這樣的:

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

最終的結(jié)果就是user='A’和‘B’的balance會(huì)回滾到4000。

所以說(shuō),undo避免了事務(wù)或者宕機(jī)的異常導(dǎo)致的數(shù)據(jù)不一致。

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

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

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

XA的兩階段分別是prepare和commit,在事務(wù)提交前,redolog中記錄的狀態(tài)都是prepare,當(dāng)事務(wù)提交后,該狀態(tài)就會(huì)被更新為commit,同時(shí)將XID寫(xiě)入到對(duì)應(yīng)的binlog中并刷新到磁盤(pán)。如下圖

在這里插入圖片描述

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

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

總結(jié)

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

  • 對(duì)于并發(fā)操作帶來(lái)的數(shù)據(jù)不一致性問(wèn)題,InnoDB通過(guò)鎖來(lái)解決。
  • 對(duì)于可能會(huì)發(fā)生的redolog不完整的情況,InnoDB通過(guò)Undo Log來(lái)解決。
  • 對(duì)于redolog&binlog不一致帶來(lái)的主從節(jié)點(diǎn)數(shù)據(jù)不一致,MySQL是通過(guò)XA兩階段提交來(lái)解決。

以上就是詳解MySQL如何保證數(shù)據(jù)一致性的詳細(xì)內(nèi)容,更多關(guān)于MySQL保證數(shù)據(jù)一致性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論