自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

講講MySQL數(shù)據(jù)庫(kù)事務(wù)怎么實(shí)現(xiàn)的!

數(shù)據(jù)庫(kù) MySQL
innodb通過(guò)mvcc解決不可重復(fù)讀的問(wèn)題,在RR數(shù)據(jù)庫(kù)隔離級(jí)別下,當(dāng)我們使用快照進(jìn)行數(shù)據(jù)讀取的時(shí)候,只會(huì)在第一次讀取的時(shí)候生成一個(gè)ReadView,后續(xù)所有快照讀都是使用同一個(gè)快照,所以就不會(huì)發(fā)生不可重復(fù)讀的問(wèn)題了。

什么是數(shù)據(jù)庫(kù)事務(wù)

數(shù)據(jù)庫(kù)事務(wù)是指一組數(shù)據(jù)庫(kù)操作,這些操作必須被視為一個(gè)不可分割的單元,要么全部執(zhí)行成功,要么全部失敗回滾。事務(wù)通常由多個(gè)SQL語(yǔ)句組成,這些語(yǔ)句可以讀取、插入、更新或刪除數(shù)據(jù)庫(kù)中的數(shù)據(jù)。事務(wù)具有ACID屬性:

1. 原子性(Atomicity):事務(wù)的所有操作被視為單個(gè)原子操作,要么全部執(zhí)行成功,要么全部執(zhí)行失敗回滾。

2. 一致性(Consistency):事務(wù)執(zhí)行的結(jié)果必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)轉(zhuǎn)換到另一個(gè)一致性狀態(tài),其中包括所有數(shù)據(jù)完整性和約束性規(guī)則的應(yīng)用。

3. 隔離性(Isolation):一個(gè)事務(wù)的執(zhí)行不能被其他并發(fā)執(zhí)行的事務(wù)干擾,每個(gè)事務(wù)應(yīng)該感覺(jué)自己在獨(dú)立地執(zhí)行。

4. 持久性(Durability):一旦事務(wù)提交,其結(jié)果應(yīng)該持久保存在數(shù)據(jù)庫(kù)中,即使系統(tǒng)故障也應(yīng)該如此。

通過(guò)實(shí)現(xiàn)事務(wù),數(shù)據(jù)庫(kù)系統(tǒng)可以確保數(shù)據(jù)的完整性和一致性,以及并發(fā)訪問(wèn)時(shí)的正確性。如果一個(gè)事務(wù)中的任何一個(gè)操作失敗,整個(gè)事務(wù)將被回滾到最初的狀態(tài),這確保了數(shù)據(jù)庫(kù)的一致性。

Mysql如何保證原子性

undo log名為回滾日志,是實(shí)現(xiàn)原子性的關(guān)鍵。InnoDB把這些為了回滾而記錄的這些東西稱(chēng)之為undo log。這里需要注意的一點(diǎn)是,由于查詢(xún)操作(SELECT)并不會(huì)修改任何用戶(hù)記錄,所以在查詢(xún)操作執(zhí)行時(shí),并不需要記錄相應(yīng)的undo log。undo log主要分為3種:

? Insert undo log :插入一條記錄時(shí),至少要把這條記錄的主鍵值記下來(lái),之后回滾的時(shí)候只需要把這個(gè)主鍵值對(duì)應(yīng)的記錄刪掉就好了。

? Update undo log:修改一條記錄時(shí),至少要把修改這條記錄前的舊值都記錄下來(lái),這樣之后回滾時(shí)再把這條記錄更新為舊值就好了。

? Delete undo log:刪除一條記錄時(shí),至少要把這條記錄中的內(nèi)容都記下來(lái),這樣之后回滾時(shí)再把由這些內(nèi)容組成的記錄插入到表中就好了。

? 刪除操作都只是設(shè)置一下老記錄的DELETED_BIT,并不真正將過(guò)時(shí)的記錄刪除。

? 為了節(jié)省磁盤(pán)空間,InnoDB有專(zhuān)門(mén)的purge線程來(lái)清理DELETED_BIT為true的記錄。為了不影響MVCC的正常工作,purge線程自己也維護(hù)了一個(gè)read view(這個(gè)read view相當(dāng)于系統(tǒng)中最老活躍事務(wù)的read view);如果某個(gè)記錄的DELETED_BIT為true,并且DB_TRX_ID相對(duì)于purge線程的read view可見(jiàn),那么這條記錄一定是可以被安全清除的。

舉個(gè)栗子:

sql

undo log

insert

delete

delete

insert

update T set v=3 where v=1

update T set v=1 where v=3

Mysql如何保證持久性

通過(guò)Innodb架構(gòu)解析我們了解到InnoDB 為了提升讀寫(xiě)效率,引入了Buffer Pool(緩存池):

  • ? 當(dāng)數(shù)據(jù)庫(kù)讀取數(shù)據(jù)時(shí),會(huì)首先從緩存池中讀取
  • ? 往數(shù)據(jù)庫(kù)寫(xiě)入數(shù)據(jù)時(shí),會(huì)先寫(xiě)入緩存池
  • ? 緩存池中更新的數(shù)據(jù)會(huì)定期刷新到磁盤(pán)中

如果MySQL宕機(jī),緩存池中更新的數(shù)據(jù)還沒(méi)有刷回到磁盤(pán)中,就會(huì)導(dǎo)致數(shù)據(jù)丟失。于是,redo log被引入進(jìn)來(lái)解決這個(gè)問(wèn)題。

圖片圖片

1. 先將原始數(shù)據(jù)從磁盤(pán)中讀入內(nèi)存中來(lái),修改數(shù)據(jù)的內(nèi)存拷貝。

2. 生成一條重做日志并寫(xiě)入redo log buffer,記錄的是數(shù)據(jù)被修改后的值。

3. 當(dāng)事務(wù)commit時(shí),將redo log buffer中的內(nèi)容刷新到 redolog file,對(duì) redo log file采用追加寫(xiě)的方式。

4. 定期將內(nèi)存中修改的數(shù)據(jù)刷新到磁盤(pán)中。

redo與undo在一次事務(wù)操作中是如何交互的?假設(shè)有A、B兩個(gè)數(shù)據(jù),值分別為1、2,開(kāi)啟事務(wù)分別對(duì)其進(jìn)行修改A → 3,B → 4,在提交,過(guò)程如下:

事務(wù)

redo&undo logo

begin;

開(kāi)啟事務(wù)


記錄A->3到redo log buffer

update T set A=3 where A=1;

A修改為3


記錄A=1到undo log


記錄B->4到redo log buffer

update T set B=4 where B=2;

B修改為4


記錄B=2到undo log


記錄A->3到redo log記錄B->4到redo log

commit;

事務(wù)提交

MySQL怎么保證隔離性

事務(wù)在并發(fā)情形下會(huì)互相干擾到的操作大體可以分為兩類(lèi),與之相對(duì)應(yīng)地,MySQL采用了兩種方式來(lái)實(shí)現(xiàn)它們的隔離:

1. 一個(gè)事務(wù)的寫(xiě)操作對(duì)另一個(gè)事務(wù)的寫(xiě)操作的影響:鎖機(jī)制保證隔離性

2. 一個(gè)事務(wù)的寫(xiě)操作對(duì)另一個(gè)事務(wù)的讀操作的影響:MVCC保證隔離性

加鎖:讀取數(shù)據(jù)之前,對(duì)其加鎖,阻止其他事務(wù)對(duì)數(shù)據(jù)進(jìn)行修改

MVCC:不加任何鎖,采用多版本并發(fā)控制實(shí)現(xiàn),把數(shù)據(jù)庫(kù)的行鎖和行的多個(gè)版本結(jié)合起來(lái),可以實(shí)現(xiàn)非鎖定讀,從而提高數(shù)據(jù)庫(kù)的并發(fā)性能。

事務(wù)隔離級(jí)別

當(dāng)數(shù)據(jù)庫(kù)上有多個(gè)事務(wù)同時(shí)執(zhí)行的時(shí)候,會(huì)帶來(lái)以下問(wèn)題:

問(wèn)題

描述

舉例

臟讀

一個(gè)事務(wù)讀到了另一個(gè)事務(wù)未提交修改的數(shù)據(jù)。

事務(wù)A開(kāi)始一個(gè)更新操作,但是還沒(méi)有提交,這時(shí)事務(wù)B讀取了這個(gè)未提交的數(shù)據(jù),就會(huì)產(chǎn)生臟讀。

幻讀

一個(gè)事務(wù)按相同的查詢(xún)條件重新讀取以前檢索過(guò)的數(shù)據(jù),卻發(fā)現(xiàn)其他事務(wù)插入了滿(mǎn)足其查詢(xún)條件的新數(shù)據(jù)。

事務(wù)A進(jìn)行一個(gè)范圍查詢(xún),此時(shí)事務(wù)B插入了一些符合該范圍查詢(xún)條件的新數(shù)據(jù),當(dāng)事務(wù)A再次進(jìn)行相同的范圍查詢(xún)時(shí),會(huì)發(fā)現(xiàn)多了一些之前沒(méi)有的行,就產(chǎn)生了幻讀。

不可重復(fù)讀

在一個(gè)事務(wù)中,多次查詢(xún)的數(shù)據(jù)不一致。

事務(wù)A讀取了一行數(shù)據(jù),然后事務(wù)B對(duì)這一行數(shù)據(jù)進(jìn)行了更新,并且提交了,當(dāng)事務(wù)A再次讀取這一行數(shù)據(jù)時(shí),會(huì)發(fā)現(xiàn)數(shù)據(jù)已經(jīng)發(fā)生了變化,就產(chǎn)生了不可重復(fù)讀。

為了避免這些問(wèn)題的出現(xiàn),數(shù)據(jù)庫(kù)引入了隔離級(jí)別的概念,通過(guò)對(duì)不同隔離級(jí)別的設(shè)置,可以控制事務(wù)之間的隔離程度,從而避免并發(fā)問(wèn)題的產(chǎn)生。不同的隔離級(jí)別有不同的特點(diǎn)和使用場(chǎng)景,需要根據(jù)實(shí)際情況進(jìn)行選擇。

以下是四個(gè)標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別:

隔離級(jí)別

含義

臟讀

不可重復(fù)讀

幻讀

讀未提交,Read Uncommitted

事務(wù)中的修改,即使沒(méi)有提交,對(duì)其他事務(wù)都是可見(jiàn)的

Y

Y

Y

讀已提交,Read Committed

事務(wù)從開(kāi)始到提交之前,所做的修改對(duì)其他事務(wù)都不可見(jiàn)

N

Y

Y

可重復(fù)讀,Repeatable read

同一事務(wù)中多次讀取同樣的記錄結(jié)果是一致的

N

N

Y

可序列化,Serializable

在讀取的每一行數(shù)據(jù)上加鎖,強(qiáng)制事務(wù)串行執(zhí)行

N

N

N

臟讀的解決

Innodb是通過(guò)在每行數(shù)據(jù)中增加一個(gè)隱藏的事務(wù)ID來(lái)實(shí)現(xiàn)mvcc,當(dāng)一個(gè)事物開(kāi)始時(shí)他會(huì)獲取一個(gè)唯一的事務(wù)ID,該事務(wù)ID用來(lái)標(biāo)記事務(wù)做的修改。當(dāng)事務(wù)讀取一行數(shù)據(jù)時(shí),innodb會(huì)檢查該行數(shù)據(jù)事務(wù)ID是否小于當(dāng)前事務(wù)ID,如果是說(shuō)明該行數(shù)據(jù)是未提交的數(shù)據(jù),innodb會(huì)阻止該事務(wù)讀取該行數(shù)據(jù),從而避免了臟讀的問(wèn)題。

不可重復(fù)讀的解決

innodb通過(guò)mvcc解決不可重復(fù)讀的問(wèn)題,在RR數(shù)據(jù)庫(kù)隔離級(jí)別下,當(dāng)我們使用快照進(jìn)行數(shù)據(jù)讀取的時(shí)候,只會(huì)在第一次讀取的時(shí)候生成一個(gè)ReadView,后續(xù)所有快照讀都是使用同一個(gè)快照,所以就不會(huì)發(fā)生不可重復(fù)讀的問(wèn)題了。

可重復(fù)讀模式下舉個(gè)栗子:事務(wù)隔離級(jí)別為RR:

圖片圖片

創(chuàng)建個(gè)測(cè)試表,并插入一條數(shù)據(jù)(1,1,1)

create table table1(
    id int(11) not null,
    a varchar(50) default null,
    b varchar(50) default null,
    primary key(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

分別開(kāi)啟兩個(gè)事務(wù)測(cè)試:

事務(wù)1

事務(wù)2

啟動(dòng)事務(wù),查詢(xún)?nèi)缦拢?img src="https://s2.51cto.com/oss/202404/08/08aef8338b0d9ec8114602ca986994fc73424a.png" alt="圖片" title="圖片" style="visibility: visible; width: 313px;" data-type="inline">

啟動(dòng)事務(wù),查詢(xún)?nèi)缦拢?img src="https://s4.51cto.com/oss/202404/08/67856ef763a74c46d45812e1817ec5379bc98d.png" alt="圖片" title="圖片" style="visibility: visible; width: 313px;" data-type="inline">

~

將a改為2,可以查到圖片

查詢(xún)a的結(jié)果還是1圖片


~

提交事務(wù)圖片

再次查詢(xún)a的結(jié)果還是1圖片


提交事務(wù),再次查詢(xún)a的結(jié)果變?yōu)?了圖片


幻讀的解決

innodb的mvcc和間隙鎖在一定程度上避免了幻讀的發(fā)生,但是沒(méi)有辦法完全避免,當(dāng)一個(gè)事務(wù)讀的時(shí)候會(huì)導(dǎo)致幻讀的發(fā)生。

幻讀的case:

  • ? 創(chuàng)建一個(gè)用戶(hù)表
create table user(
    id int not null,
    name varchar(50),
    age int,
    primary key(id)
);
  • ? 插入幾條數(shù)據(jù)
insert into user values(1,'張三',10),(2,'李四',20),(3,'王二',30);
  • ? 分別開(kāi)啟兩個(gè)事務(wù)測(cè)試:

事務(wù)1

事務(wù)2

begin;select * from user where age >10 and age<40;圖片



begin;insert into user value(4,'麻子',25);圖片

commit;

select * from user where age >10 and age<40;圖片


update user set name='呵呵' where age=25;select * from user where age >10 and age<40;圖片


MVCC實(shí)現(xiàn)

每條記錄在更新的時(shí)候都會(huì)同時(shí)記錄一條回滾操作。同一條記錄在系統(tǒng)中可以存在多個(gè)版本,這就是數(shù)據(jù)庫(kù)的多版本并發(fā)控制(MVCC)。

MySQL中每條記錄,除了我們自定義的字段之外,還有數(shù)據(jù)庫(kù)隱藏定義的三個(gè)字段:

字段

描述

DB_TRX_ID

6字節(jié),最近修改事務(wù)id,記錄創(chuàng)建這套記錄后者最后一次修改該記錄的事務(wù)id

DB_ROLL_PTR

7字節(jié),回滾指針,指向這條記錄的上一個(gè)版本,用于配合undolog

DB_ROW_ID

6字節(jié),隱藏的主鍵,如果數(shù)據(jù)表沒(méi)有主鍵,那么innodb會(huì)生成一個(gè)6字節(jié)的row_id

在 MySQL 中,實(shí)際上每條記錄在更新的時(shí)候都會(huì)同時(shí)記錄一條回滾操作。記錄上的最新值,通過(guò)回滾操作,都可以得到前一個(gè)狀態(tài)的值。

InnoDB 并不會(huì)真正地去開(kāi)辟空間存儲(chǔ)多個(gè)版本的行記錄,只是借助 undo log 記錄每次寫(xiě)操作的反向操作。所以B+ 索引樹(shù)上對(duì)應(yīng)的記錄只會(huì)有一個(gè)最新版本,InnoDB 可以根據(jù) undo log 得到數(shù)據(jù)的歷史版本,從而實(shí)現(xiàn)多版本控制。

Read View

什么是Read View,說(shuō)白了Read View就是事務(wù)進(jìn)行快照讀操作的時(shí)候生產(chǎn)的讀視圖(Read View),在該事務(wù)執(zhí)行的快照讀的那一刻,會(huì)生成數(shù)據(jù)庫(kù)系統(tǒng)當(dāng)前的一個(gè)快照,記錄并維護(hù)系統(tǒng)當(dāng)前活躍事務(wù)的ID(當(dāng)每個(gè)事務(wù)開(kāi)啟時(shí),都會(huì)被分配一個(gè)ID, 這個(gè)ID是遞增的,所以最新的事務(wù),ID值越大)

所以我們知道 Read View主要是用來(lái)做可見(jiàn)性判斷的, 即當(dāng)我們某個(gè)事務(wù)執(zhí)行快照讀的時(shí)候,對(duì)該記錄創(chuàng)建一個(gè)Read View讀視圖,把它比作條件用來(lái)判斷當(dāng)前事務(wù)能夠看到哪個(gè)版本的數(shù)據(jù),即可能是當(dāng)前最新的數(shù)據(jù),也有可能是該行記錄的undo log里面的某個(gè)版本的數(shù)據(jù)。

Read View遵循一個(gè)可見(jiàn)性算法,主要是將要被修改的數(shù)據(jù)的最新記錄中的DB_TRX_ID(即當(dāng)前事務(wù)ID)取出來(lái),與系統(tǒng)當(dāng)前其他活躍事務(wù)的ID去對(duì)比(由Read View維護(hù)),如果DB_TRX_ID跟Read View的屬性做了某些比較,不符合可見(jiàn)性,那就通過(guò)DB_ROLL_PTR回滾指針去取出Undo Log中的DB_TRX_ID再比較,即遍歷鏈表的DB_TRX_ID(從鏈?zhǔn)椎芥溛?,即從最近的一次修改查起),直到找到滿(mǎn)足特定條件的DB_TRX_ID, 那么這個(gè)DB_TRX_ID所在的舊記錄就是當(dāng)前事務(wù)能看見(jiàn)的最新老版本

假設(shè)一個(gè)值從 1 被按順序改成了 2、3、4,在回滾日志里面就會(huì)有類(lèi)似下面的記錄。

圖片圖片

當(dāng)前值是 4,但是在查詢(xún)這條記錄的時(shí)候,不同時(shí)刻啟動(dòng)的事務(wù)會(huì)有不同的 read-view。如圖中看到的,在視圖 A、B、C 里面,這一個(gè)記錄的值分別是 1、2、4,同一條記錄在系統(tǒng)中可以存在多個(gè)版本,就是數(shù)據(jù)庫(kù)的多版本并發(fā)控制(MVCC)。對(duì)于 read-view A,要得到 1,就必須將當(dāng)前值依次執(zhí)行圖中所有的回滾操作得到。同時(shí)你會(huì)發(fā)現(xiàn),即使現(xiàn)在有另外一個(gè)事務(wù)正在將 4 改成 5,這個(gè)事務(wù)跟 read-view A、B、C 對(duì)應(yīng)的事務(wù)是不會(huì)沖突的。你一定會(huì)問(wèn),回滾日志總不能一直保留吧,什么時(shí)候刪除呢?答案是,在不需要的時(shí)候才刪除。也就是說(shuō),系統(tǒng)會(huì)判斷,當(dāng)沒(méi)有事務(wù)再需要用到這些回滾日志時(shí),回滾日志會(huì)被刪除。什么時(shí)候才不需要了呢?就是當(dāng)系統(tǒng)里沒(méi)有比這個(gè)回滾日志更早的 read-view 的時(shí)候。

那么RC、RR級(jí)別下的InnoDB快照讀有什么不同?

在可重復(fù)讀隔離級(jí)別下,只需要在事務(wù)開(kāi)始的時(shí)候創(chuàng)建一致性視圖,之后事務(wù)里的其他查詢(xún)都共用這個(gè)一致性視圖;

在讀提交隔離級(jí)別下,每一個(gè)語(yǔ)句執(zhí)行前都會(huì)重新算出一個(gè)新的視圖。

責(zé)任編輯:武曉燕 來(lái)源: 半畝方塘立身
相關(guān)推薦

2017-08-22 17:10:45

數(shù)據(jù)庫(kù)MySQL事務(wù)模型

2020-06-17 16:56:36

數(shù)據(jù)庫(kù)MySQL跨行事務(wù)

2010-05-31 15:12:44

MySQL數(shù)據(jù)庫(kù)

2010-06-02 16:57:50

MySQL數(shù)據(jù)庫(kù)同步

2018-08-01 14:00:49

MySQL數(shù)據(jù)庫(kù)PHP

2024-11-27 11:39:02

2010-05-14 14:38:03

安裝MySQL

2021-07-08 07:08:21

MySQL ACID 數(shù)據(jù)庫(kù)

2019-08-19 10:24:33

分布式事務(wù)數(shù)據(jù)庫(kù)

2024-01-18 09:43:11

MySQL數(shù)據(jù)庫(kù)

2010-06-04 10:05:55

MySQL數(shù)據(jù)庫(kù)還原

2009-09-24 14:12:22

Hibernate數(shù)據(jù)

2024-04-17 08:11:01

數(shù)據(jù)庫(kù)事務(wù)流程

2018-06-15 08:23:55

Key-Value數(shù)據(jù)庫(kù)MVCC

2010-09-07 15:20:58

SQL語(yǔ)句事務(wù)

2022-05-09 15:52:23

MySQL數(shù)據(jù)庫(kù)數(shù)據(jù)庫(kù)表

2019-07-05 11:20:31

PythonMySQL數(shù)據(jù)庫(kù)

2010-10-08 09:38:55

Android數(shù)據(jù)庫(kù)事

2023-10-11 08:09:53

事務(wù)隔離級(jí)別

2018-07-20 11:10:21

數(shù)據(jù)庫(kù)事務(wù)隔離性
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)