一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MySQL事務(wù)的四大隔離級(jí)別詳解

數(shù)據(jù)分析與開(kāi)發(fā) ? 來(lái)源:撿田螺的小男孩 ? 作者:撿田螺的小男孩 ? 2020-11-27 16:07 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

之前分析一個(gè)死鎖問(wèn)題,發(fā)現(xiàn)自己對(duì)數(shù)據(jù)庫(kù)隔離級(jí)別理解還不夠深入,所以趁著這幾天假期,整理一下MySQL事務(wù)的四大隔離級(jí)別相關(guān)知識(shí),希望對(duì)大家有幫助~

事務(wù)

什么是事務(wù)?

事務(wù),由一個(gè)有限的數(shù)據(jù)庫(kù)操作序列構(gòu)成,這些操作要么全部執(zhí)行,要么全部不執(zhí)行,是一個(gè)不可分割的工作單位。

假如A轉(zhuǎn)賬給B 100 元,先從A的賬戶(hù)里扣除 100 元,再在 B 的賬戶(hù)上加上 100 元。如果扣完A的100元后,還沒(méi)來(lái)得及給B加上,銀行系統(tǒng)異常了,最后導(dǎo)致A的余額減少了,B的余額卻沒(méi)有增加。所以就需要事務(wù),將A的錢(qián)回滾回去,就是這么簡(jiǎn)單。

事務(wù)的四大特性

原子性:事務(wù)作為一個(gè)整體被執(zhí)行,包含在其中的對(duì)數(shù)據(jù)庫(kù)的操作要么全部都執(zhí)行,要么都不執(zhí)行。

一致性:指在事務(wù)開(kāi)始之前和事務(wù)結(jié)束以后,數(shù)據(jù)不會(huì)被破壞,假如A賬戶(hù)給B賬戶(hù)轉(zhuǎn)10塊錢(qián),不管成功與否,A和B的總金額是不變的。

隔離性:多個(gè)事務(wù)并發(fā)訪問(wèn)時(shí),事務(wù)之間是相互隔離的,一個(gè)事務(wù)不應(yīng)該被其他事務(wù)干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。。

持久性:表示事務(wù)完成提交后,該事務(wù)對(duì)數(shù)據(jù)庫(kù)所作的操作更改,將持久地保存在數(shù)據(jù)庫(kù)之中。

事務(wù)并發(fā)存在的問(wèn)題

事務(wù)并發(fā)執(zhí)行存在什么問(wèn)題呢,換句話(huà)說(shuō)就是,一個(gè)事務(wù)是怎么干擾到其他事務(wù)的呢?看例子吧~

假設(shè)現(xiàn)在有表:

CREATE TABLE `account`(

`id`int(11) NOT NULL,

`name` varchar(255) DEFAULT NULL,

`balance`int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `un_name_idx`(`name`) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

表中有數(shù)據(jù):

臟讀(dirty read

假設(shè)現(xiàn)在有兩個(gè)事務(wù)A、B:

假設(shè)現(xiàn)在A的余額是100,事務(wù)A正在準(zhǔn)備查詢(xún)Jay的余額

這時(shí)候,事務(wù)B先扣減Jay的余額,扣了10

最后A 讀到的是扣減后的余額

由上圖可以發(fā)現(xiàn),事務(wù)A、B交替執(zhí)行,事務(wù)A被事務(wù)B干擾到了,因?yàn)槭聞?wù)A讀取到事務(wù)B未提交的數(shù)據(jù),這就是臟讀。

不可重復(fù)讀(unrepeatable read)

假設(shè)現(xiàn)在有兩個(gè)事務(wù)A和B:

事務(wù)A先查詢(xún)Jay的余額,查到結(jié)果是100

這時(shí)候事務(wù)B 對(duì)Jay的賬戶(hù)余額進(jìn)行扣減,扣去10后,提交事務(wù)

事務(wù)A再去查詢(xún)Jay的賬戶(hù)余額發(fā)現(xiàn)變成了90

事務(wù)A又被事務(wù)B干擾到了!在事務(wù)A范圍內(nèi),兩個(gè)相同的查詢(xún),讀取同一條記錄,卻返回了不同的數(shù)據(jù),這就是不可重復(fù)讀。

幻讀

假設(shè)現(xiàn)在有兩個(gè)事務(wù)A、B:

事務(wù)A先查詢(xún)id大于2的賬戶(hù)記錄,得到記錄id=2和id=3的兩條記錄

這時(shí)候,事務(wù)B開(kāi)啟,插入一條id=4的記錄,并且提交了

事務(wù)A再去執(zhí)行相同的查詢(xún),卻得到了id=2,3,4的3條記錄了。

事務(wù)A查詢(xún)一個(gè)范圍的結(jié)果集,另一個(gè)并發(fā)事務(wù)B往這個(gè)范圍中插入/刪除了數(shù)據(jù),并靜悄悄地提交,然后事務(wù)A再次查詢(xún)相同的范圍,兩次讀取得到的結(jié)果集不一樣了,這就是幻讀。

事務(wù)的四大隔離級(jí)別實(shí)踐

既然并發(fā)事務(wù)存在臟讀、不可重復(fù)、幻讀等問(wèn)題,InnoDB實(shí)現(xiàn)了哪幾種事務(wù)的隔離級(jí)別應(yīng)對(duì)呢?

讀未提交(Read Uncommitted)

讀已提交(Read Committed)

可重復(fù)讀(Repeatable Read)

串行化(Serializable)

讀未提交(Read Uncommitted)

想學(xué)習(xí)一個(gè)知識(shí)點(diǎn),最好的方式就是實(shí)踐之。好了,我們?nèi)?shù)據(jù)庫(kù)給它設(shè)置讀未提交隔離級(jí)別,實(shí)踐一下吧~

先把事務(wù)隔離級(jí)別設(shè)置為read uncommitted,開(kāi)啟事務(wù)A,查詢(xún)id=1的數(shù)據(jù)

set session transaction isolation level read uncommitted;

begin;

select* from account where id =1;

結(jié)果如下:

這時(shí)候,另開(kāi)一個(gè)窗口打開(kāi)mysql,也把當(dāng)前事務(wù)隔離級(jí)別設(shè)置為read uncommitted,開(kāi)啟事務(wù)B,執(zhí)行更新操作

set session transaction isolation level read uncommitted;

begin;

update account set balance=balance+20where id =1;

接著回事務(wù)A的窗口,再查account表id=1的數(shù)據(jù),結(jié)果如下:

可以發(fā)現(xiàn),在讀未提交(Read Uncommitted)隔離級(jí)別下,一個(gè)事務(wù)會(huì)讀到其他事務(wù)未提交的數(shù)據(jù)的,即存在臟讀問(wèn)題。事務(wù)B都還沒(méi)commit到數(shù)據(jù)庫(kù)呢,事務(wù)A就讀到了,感覺(jué)都亂套了。。。實(shí)際上,讀未提交是隔離級(jí)別最低的一種。

讀已提交(READ COMMITTED)

為了避免臟讀,數(shù)據(jù)庫(kù)有了比讀未提交更高的隔離級(jí)別,即讀已提交。

把當(dāng)前事務(wù)隔離級(jí)別設(shè)置為讀已提交(READ COMMITTED),開(kāi)啟事務(wù)A,查詢(xún)account中id=1的數(shù)據(jù)

set session transaction isolation level read committed;

begin;

select* from account where id =1;

另開(kāi)一個(gè)窗口打開(kāi)mysql,也把事務(wù)隔離級(jí)別設(shè)置為read committed,開(kāi)啟事務(wù)B,執(zhí)行以下操作

set session transaction isolation level read committed;

begin;

update account set balance=balance+20where id =1;

接著回事務(wù)A的窗口,再查account數(shù)據(jù),發(fā)現(xiàn)數(shù)據(jù)沒(méi)變:

我們?cè)偃サ绞聞?wù)B的窗口執(zhí)行commit操作:

commit;

最后回到事務(wù)A窗口查詢(xún),發(fā)現(xiàn)數(shù)據(jù)變了:

由此可以得出結(jié)論,隔離級(jí)別設(shè)置為已提交讀(READ COMMITTED)時(shí),已經(jīng)不會(huì)出現(xiàn)臟讀問(wèn)題了,當(dāng)前事務(wù)只能讀取到其他事務(wù)提交的數(shù)據(jù)。但是,你站在事務(wù)A的角度想想,存在其他問(wèn)題嗎?

讀已提交的隔離級(jí)別會(huì)有什么問(wèn)題呢?

在同一個(gè)事務(wù)A里,相同的查詢(xún)sql,讀取同一條記錄(id=1),讀到的結(jié)果是不一樣的,即不可重復(fù)讀。所以,隔離級(jí)別設(shè)置為read committed的時(shí)候,還會(huì)存在不可重復(fù)讀的并發(fā)問(wèn)題。

可重復(fù)讀(Repeatable Read)

如果你的老板要求,在同個(gè)事務(wù)中,查詢(xún)結(jié)果必須是一致的,即老板要求你解決不可重復(fù)的并發(fā)問(wèn)題,怎么辦呢?老板,臣妾辦不到?來(lái)實(shí)踐一下可重復(fù)讀(Repeatable Read)這個(gè)隔離級(jí)別吧~

哈哈,步驟1、2、6的查詢(xún)結(jié)果都是一樣的,即repeatable read解決了不可重復(fù)讀問(wèn)題,是不是心里美滋滋的呢,終于解決老板的難題了~

RR級(jí)別是否解決了幻讀問(wèn)題呢?

再來(lái)看看網(wǎng)上的一個(gè)熱點(diǎn)問(wèn)題,有關(guān)于RR級(jí)別下,是否解決了幻讀問(wèn)題?我們來(lái)實(shí)踐一下:

由圖可得,步驟2和步驟6查詢(xún)結(jié)果集沒(méi)有變化,看起來(lái)RR級(jí)別是已經(jīng)解決幻讀問(wèn)題了~但是呢,RR級(jí)別還是存在這種現(xiàn)象:

其實(shí),上圖如果事務(wù)A中,沒(méi)有 update accountsetbalance=200whereid=5;這步操作, select*fromaccountwhereid>2查詢(xún)到的結(jié)果集確實(shí)是不變,這種情況沒(méi)有幻讀問(wèn)題。但是,有了update這個(gè)騷操作,同一個(gè)事務(wù),相同的sql,查出的結(jié)果集不同,這個(gè)是符合了幻讀的定義~

這個(gè)問(wèn)題,親愛(ài)的朋友,你覺(jué)得它算幻讀問(wèn)題嗎?

串行化(Serializable)

前面三種數(shù)據(jù)庫(kù)隔離級(jí)別,都有一定的并發(fā)問(wèn)題,現(xiàn)在放大招吧,實(shí)踐SERIALIZABLE隔離級(jí)別。

把事務(wù)隔離級(jí)別設(shè)置為Serializable,開(kāi)啟事務(wù)A,查詢(xún)account表數(shù)據(jù)

set session transaction isolation level serializable;

select@@tx_isolation;

begin;

select* from account;

另開(kāi)一個(gè)窗口打開(kāi)mysql,也把事務(wù)隔離級(jí)別設(shè)置為Serializable,開(kāi)啟事務(wù)B,執(zhí)行插入一條數(shù)據(jù):

set session transaction isolation level serializable;

select@@tx_isolation;

begin;

insert into account(id,name,balance) value(6,'Li',100);

執(zhí)行結(jié)果如下:

由圖可得,當(dāng)數(shù)據(jù)庫(kù)隔離級(jí)別設(shè)置為serializable的時(shí)候,事務(wù)B對(duì)表的寫(xiě)操作,在等事務(wù)A的讀操作。其實(shí),這是隔離級(jí)別中最嚴(yán)格的,讀寫(xiě)都不允許并發(fā)。它保證了最好的安全性,性能卻是個(gè)問(wèn)題~

MySql隔離級(jí)別的實(shí)現(xiàn)原理

實(shí)現(xiàn)隔離機(jī)制的方法主要有兩種:

讀寫(xiě)鎖

一致性快照讀,即 MVCC

MySql使用不同的鎖策略(Locking Strategy)/MVCC來(lái)實(shí)現(xiàn)四種不同的隔離級(jí)別。RR、RC的實(shí)現(xiàn)原理跟MVCC有關(guān),RU和Serializable跟鎖有關(guān)。

讀未提交(Read Uncommitted)

官方說(shuō)法:

SELECT statements are performed in a nonlocking fashion, but a possible earlier version of a row might be used. Thus, using this isolation level, such reads are not consistent.

讀未提交,采取的是讀不加鎖原理。

事務(wù)讀不加鎖,不阻塞其他事務(wù)的讀和寫(xiě)

事務(wù)寫(xiě)阻塞其他事務(wù)寫(xiě),但不阻塞其他事務(wù)讀;

串行化(Serializable)

官方的說(shuō)法:

InnoDB implicitly converts all plain SELECT statements to SELECT ... FOR SHARE if autocommit is disabled. If autocommit is enabled, the SELECT is its own transaction. It therefore is known to be read only and can be serialized if performed as a consistent (nonlocking) read and need not block for other transactions. (To force a plain SELECT to block if other transactions have modified the selected rows, disable autocommit.)

所有SELECT語(yǔ)句會(huì)隱式轉(zhuǎn)化為SELECT...FOR SHARE,即加共享鎖。

讀加共享鎖,寫(xiě)加排他鎖,讀寫(xiě)互斥。如果有未提交的事務(wù)正在修改某些行,所有select這些行的語(yǔ)句都會(huì)阻塞。

MVCC的實(shí)現(xiàn)原理

MVCC,中文叫多版本并發(fā)控制,它是通過(guò)讀取歷史版本的數(shù)據(jù),來(lái)降低并發(fā)事務(wù)沖突,從而提高并發(fā)性能的一種機(jī)制。它的實(shí)現(xiàn)依賴(lài)于隱式字段、undo日志、快照讀&當(dāng)前讀、Read View,因此,我們先來(lái)了解這幾個(gè)知識(shí)點(diǎn)。

隱式字段

對(duì)于InnoDB存儲(chǔ)引擎,每一行記錄都有兩個(gè)隱藏列DBTRXID,DBROLLPTR,如果表中沒(méi)有主鍵和非NULL唯一鍵時(shí),則還會(huì)有第三個(gè)隱藏的主鍵列 DBROWID。

DBTRXID,記錄每一行最近一次修改(修改/更新)它的事務(wù)ID,大小為6字節(jié);

DBROLLPTR,這個(gè)隱藏列就相當(dāng)于一個(gè)指針,指向回滾段的undo日志,大小為7字節(jié);

DBROWID,單調(diào)遞增的行ID,大小為6字節(jié);

undo日志

事務(wù)未提交的時(shí)候,修改數(shù)據(jù)的鏡像(修改前的舊版本),存到undo日志里。以便事務(wù)回滾時(shí),恢復(fù)舊版本數(shù)據(jù),撤銷(xiāo)未提交事務(wù)數(shù)據(jù)對(duì)數(shù)據(jù)庫(kù)的影響。

undo日志是邏輯日志??梢赃@樣認(rèn)為,當(dāng)delete一條記錄時(shí),undo log中會(huì)記錄一條對(duì)應(yīng)的insert記錄,當(dāng)update一條記錄時(shí),它記錄一條對(duì)應(yīng)相反的update記錄。

存儲(chǔ)undo日志的地方,就是回滾段。

多個(gè)事務(wù)并行操作某一行數(shù)據(jù)時(shí),不同事務(wù)對(duì)該行數(shù)據(jù)的修改會(huì)產(chǎn)生多個(gè)版本,然后通過(guò)回滾指針(DBROLLPTR)連一條Undo日志鏈。

我們通過(guò)例子來(lái)看一下~

mysql> select* from account ;

+----+------+---------+

| id | name | balance |

+----+------+---------+

| 1| Jay| 100|

+----+------+---------+

1 row inset(0.00 sec)

假設(shè)表accout現(xiàn)在只有一條記錄,插入該該記錄的事務(wù)Id為100

如果事務(wù)B(事務(wù)Id為200),對(duì)id=1的該行記錄進(jìn)行更新,把balance值修改為90

事務(wù)B修改后,形成的Undo Log鏈如下:

快照讀&當(dāng)前讀

快照讀:

讀取的是記錄數(shù)據(jù)的可見(jiàn)版本(有舊的版本),不加鎖,普通的select語(yǔ)句都是快照讀,如:

select* from account where id>2;

當(dāng)前讀:

讀取的是記錄數(shù)據(jù)的最新版本,顯示加鎖的都是當(dāng)前讀

select* from account where id>2lockin share mode;

select* from account where id>2for update;

Read View

Read View就是事務(wù)執(zhí)行快照讀時(shí),產(chǎn)生的讀視圖。

事務(wù)執(zhí)行快照讀時(shí),會(huì)生成數(shù)據(jù)庫(kù)系統(tǒng)當(dāng)前的一個(gè)快照,記錄當(dāng)前系統(tǒng)中還有哪些活躍的讀寫(xiě)事務(wù),把它們放到一個(gè)列表里。

Read View主要是用來(lái)做可見(jiàn)性判斷的,即判斷當(dāng)前事務(wù)可見(jiàn)哪個(gè)版本的數(shù)據(jù)~

為了下面方便討論Read View可見(jiàn)性規(guī)則,先定義幾個(gè)變量

m_ids:當(dāng)前系統(tǒng)中那些活躍的讀寫(xiě)事務(wù)ID,它數(shù)據(jù)結(jié)構(gòu)為一個(gè)List。

minlimitid:m_ids事務(wù)列表中,最小的事務(wù)ID

maxlimitid:m_ids事務(wù)列表中,最大的事務(wù)ID

如果DBTRXID < minlimitid,表明生成該版本的事務(wù)在生成ReadView前已經(jīng)提交(因?yàn)槭聞?wù)ID是遞增的),所以該版本可以被當(dāng)前事務(wù)訪問(wèn)。

如果DBTRXID > m_ids列表中最大的事務(wù)id,表明生成該版本的事務(wù)在生成ReadView后才生成,所以該版本不可以被當(dāng)前事務(wù)訪問(wèn)。

如果 minlimitid =

注意啦?。R跟RC隔離級(jí)別,最大的區(qū)別就是:RC每次讀取數(shù)據(jù)前都生成一個(gè)ReadView,而RR只在第一次讀取數(shù)據(jù)時(shí)生成一個(gè)ReadView。

已提交讀(READ COMMITTED) 存在不可重復(fù)讀問(wèn)題的分析歷程

我覺(jué)得理解一個(gè)新的知識(shí)點(diǎn),最好的方法就是居于目前存在的問(wèn)題/現(xiàn)象,去分析它的來(lái)龍去脈~ RC的實(shí)現(xiàn)也跟MVCC有關(guān),RC是存在重復(fù)讀并發(fā)問(wèn)題的,所以我們來(lái)分析一波RC吧,先看一下執(zhí)行流程

假設(shè)現(xiàn)在系統(tǒng)里有A,B兩個(gè)事務(wù)在執(zhí)行,事務(wù)ID分別為100、200,并且假設(shè)存在的老數(shù)據(jù),插入事務(wù)ID是50哈~

事務(wù)A 先執(zhí)行查詢(xún)1的操作

# 事務(wù)A,Transaction ID 100

begin;

查詢(xún)1:select* from account WHERE id = 1;

事務(wù) B 執(zhí)行更新操作,id =1記錄的undo日志鏈如下

begin;

update account set balance =balance+20where id =1;

回到事務(wù)A,執(zhí)行查詢(xún)2的操作

begin;

查詢(xún)1:select* from account WHERE id = 1;

查詢(xún)2:select* from account WHERE id = 1;

查詢(xún)2執(zhí)行分析:

事務(wù)A在執(zhí)行到SELECT語(yǔ)句時(shí),重新生成一個(gè)ReadView,因?yàn)槭聞?wù)B(200)在活躍,所以ReadView的m_ids列表內(nèi)容就是[200]

由上圖undo日志鏈可得,最新版本的balance為1000,它的事務(wù)ID為200,在活躍事務(wù)列表里,所以當(dāng)前事務(wù)(事務(wù)A)不可見(jiàn)。

我們繼續(xù)找下一個(gè)版本,balance為100這行記錄,事務(wù)Id為50,小于活躍事務(wù)ID列表最小記錄200,所以這個(gè)版本可見(jiàn),因此,查詢(xún)2的結(jié)果,就是返回balance=100這個(gè)記錄~~

我們回到事務(wù)B,執(zhí)行提交操作,這時(shí)候undo日志鏈不變

begin;

update account set balance =balance+20where id =1;

commit

再次回到事務(wù)A,執(zhí)行查詢(xún)3的操作

begin;

查詢(xún)1:select* from account WHERE id = 1;

查詢(xún)2:select* from account WHERE id = 1;

查詢(xún)3:select* from account WHERE id = 1;

查詢(xún)3執(zhí)行分析:

事務(wù)A在執(zhí)行到SELECT語(yǔ)句時(shí),重新生成一個(gè)ReadView,因?yàn)槭聞?wù)B(200)已經(jīng)提交,不再活躍,所以ReadView的m_ids列表內(nèi)容就是空的了。

所以事務(wù)A直接讀取最新記錄,讀取到balance =120這個(gè)版本的數(shù)據(jù)。

所以,這就是RC存在不可重復(fù)讀問(wèn)題的過(guò)程啦~有不理解的地方可以多讀幾遍哈~

可重復(fù)讀(Repeatable Read)解決不可重復(fù)讀問(wèn)題的一次分析

我們?cè)賮?lái)分析一波,RR隔離級(jí)別是如何解決不可重復(fù)讀并發(fā)問(wèn)題的吧~

你可能會(huì)覺(jué)得兩個(gè)并發(fā)事務(wù)的例子太簡(jiǎn)單了,好的!我們現(xiàn)在來(lái)點(diǎn)刺激的,開(kāi)啟三個(gè)事務(wù)~

假設(shè)現(xiàn)在系統(tǒng)里有A,B,C兩個(gè)事務(wù)在執(zhí)行,事務(wù)ID分別為100、200,300,存量數(shù)據(jù)插入的事務(wù)ID是50~

# 事務(wù)A,Transaction ID 100

begin;

UPDATE account SET balance = 1000 WHERE id = 1;

# 事務(wù)B,Transaction ID 200

begin; //開(kāi)個(gè)事務(wù),占坑先

這時(shí)候,account表中,id =1記錄的undo日志鏈如下:

# 事務(wù)C,Transaction ID 300

begin;

//查詢(xún)1:select * from account WHERE id = 1;

查詢(xún)1執(zhí)行過(guò)程分析:

事務(wù)C在執(zhí)行SELECT語(yǔ)句時(shí),會(huì)先生成一個(gè)ReadView。因?yàn)槭聞?wù)A(100)、B(200)在活躍,所以ReadView的m_ids列表內(nèi)容就是[100, 200]。

由上圖undo日志鏈可得,最新版本的balance為1000,它的事務(wù)ID為100,在活躍事務(wù)列表里,所以當(dāng)前事務(wù)(事務(wù)C)不可見(jiàn)。

我們繼續(xù)找下一個(gè)版本,balance為100這行記錄,事務(wù)Id為50,小于活躍事務(wù)ID列表最小記錄100,所以這個(gè)版本可見(jiàn),因此,查詢(xún)1的結(jié)果,就是返回balance=100這個(gè)記錄~~

接著,我們把事務(wù)A提交一下:

# 事務(wù)A,Transaction ID 100

begin;

UPDATE account SET balance = 1000 WHERE id = 1;

commit;

在事務(wù)B中,執(zhí)行更新操作,把id=1的記錄balance修改為2000,更新完后,undo 日志鏈如下:

# 事務(wù)B,Transaction ID 200

begin; //開(kāi)個(gè)事務(wù),占坑先

UPDATE account SET balance = 2000 WHERE id = 1;

回到事務(wù)C,執(zhí)行查詢(xún)2

# 事務(wù)C,Transaction ID 300

begin;

//查詢(xún)1:select * from account WHERE id = 1;

//查詢(xún)2:select * from account WHERE id = 1;

查詢(xún)2:執(zhí)行分析:

在RR 級(jí)別下,執(zhí)行查詢(xún)2的時(shí)候,因?yàn)榍懊鍾eadView已經(jīng)生成過(guò)了,所以直接服用之前的ReadView,活躍事務(wù)列表為[100,200].

由上圖undo日志鏈可得,最新版本的balance為2000,它的事務(wù)ID為200,在活躍事務(wù)列表里,所以當(dāng)前事務(wù)(事務(wù)C)不可見(jiàn)。

我們繼續(xù)找下一個(gè)版本,balance為1000這行記錄,事務(wù)Id為100,也在活躍事務(wù)列表里,所以當(dāng)前事務(wù)(事務(wù)C)不可見(jiàn)。

繼續(xù)找下一個(gè)版本,balance為100這行記錄,事務(wù)Id為50,小于活躍事務(wù)ID列表最小記錄100,所以這個(gè)版本可見(jiàn),因此,查詢(xún)2的結(jié)果,也是返回balance=100這個(gè)記錄~~

鎖相關(guān)概念補(bǔ)充(附):

共享鎖與排他鎖

InnoDB 實(shí)現(xiàn)了標(biāo)準(zhǔn)的行級(jí)鎖,包括兩種:共享鎖(簡(jiǎn)稱(chēng) s 鎖)、排它鎖(簡(jiǎn)稱(chēng) x 鎖)。

共享鎖(S鎖):允許持鎖事務(wù)讀取一行。

排他鎖(X鎖):允許持鎖事務(wù)更新或者刪除一行。

如果事務(wù) T1 持有行 r 的 s 鎖,那么另一個(gè)事務(wù) T2 請(qǐng)求 r 的鎖時(shí),會(huì)做如下處理:

T2 請(qǐng)求 s 鎖立即被允許,結(jié)果 T1 T2 都持有 r 行的 s 鎖

T2 請(qǐng)求 x 鎖不能被立即允許

如果 T1 持有 r 的 x 鎖,那么 T2 請(qǐng)求 r 的 x、s 鎖都不能被立即允許,T2 必須等待T1釋放 x 鎖才可以,因?yàn)閄鎖與任何的鎖都不兼容。

記錄鎖(Record Locks)

記錄鎖是最簡(jiǎn)單的行鎖,僅僅鎖住一行。如:SELECT c1 FROM t WHERE c1=10FOR UPDATE

記錄鎖永遠(yuǎn)都是加在索引上的,即使一個(gè)表沒(méi)有索引,InnoDB也會(huì)隱式的創(chuàng)建一個(gè)索引,并使用這個(gè)索引實(shí)施記錄鎖。

會(huì)阻塞其他事務(wù)對(duì)其插入、更新、刪除

記錄鎖的事務(wù)數(shù)據(jù)(關(guān)鍵詞:lock_mode X locks rec butnotgap),記錄如下:

RECORD LOCKS space id 58 page no3 n bits 72 index `PRIMARY` of table `test`.`t`

trx id 10078 lock_mode X locks rec but not gap

Recordlock, heap no2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0

0: len 4; hex 8000000a; asc ;;

1: len 6; hex 00000000274f; asc 'O;;

2: len 7; hex b60000019d0110; asc ;;

間隙鎖(Gap Locks)

間隙鎖是一種加在兩個(gè)索引之間的鎖,或者加在第一個(gè)索引之前,或最后一個(gè)索引之后的間隙。

使用間隙鎖鎖住的是一個(gè)區(qū)間,而不僅僅是這個(gè)區(qū)間中的每一條數(shù)據(jù)。

間隙鎖只阻止其他事務(wù)插入到間隙中,他們不阻止其他事務(wù)在同一個(gè)間隙上獲得間隙鎖,所以 gap x lock 和 gap s lock 有相同的作用。

Next-Key Locks

Next-key鎖是記錄鎖和間隙鎖的組合,它指的是加在某條記錄以及這條記錄前面間隙上的鎖。

RC級(jí)別存在幻讀分析

因?yàn)镽C是存在幻讀問(wèn)題的,所以我們先切到RC隔離級(jí)別,分析一波~

假設(shè)account表有4條數(shù)據(jù)。

開(kāi)啟事務(wù)A,執(zhí)行當(dāng)前讀,查詢(xún)id>2的所有記錄。

再開(kāi)啟事務(wù)B,插入id=5的一條數(shù)據(jù)。

事務(wù)B插入數(shù)據(jù)成功后,再修改id=3的記錄

回到事務(wù)A,再次執(zhí)行id>2的當(dāng)前讀查詢(xún)

事務(wù)B可以插入id=5的數(shù)據(jù),卻更新不了id=3的數(shù)據(jù),陷入阻塞。證明事務(wù)A在執(zhí)行當(dāng)前讀的時(shí)候在id =3和id=4這兩條記錄上加了鎖,但是并沒(méi)有對(duì) id > 2 這個(gè)范圍加鎖~

事務(wù)B陷入阻塞后,切回事務(wù)A執(zhí)行當(dāng)前讀操作時(shí),死鎖出現(xiàn)。因?yàn)槭聞?wù)B在 insert 的時(shí)候,會(huì)在新記錄(id=5)上加鎖,所以事務(wù)A再次執(zhí)行當(dāng)前讀,想獲取id> 2 的記錄,就需要在 id=3,4,5 這3條記錄上加鎖,但是 id = 5這條記錄已經(jīng)被事務(wù)B 鎖住了,于是事務(wù)A被事務(wù)B阻塞,同時(shí)事務(wù)B還在等待 事務(wù)A釋放 id = 3上的鎖,最終產(chǎn)生了死鎖。

因此,我們可以發(fā)現(xiàn),RC隔離級(jí)別下,加鎖的select, update, delete等語(yǔ)句,使用的是記錄鎖,其他事務(wù)的插入依然可以執(zhí)行,因此會(huì)存在幻讀~

RR 級(jí)別解決幻讀分析

因?yàn)镽R是解決幻讀問(wèn)題的,怎么解決的呢,分析一波吧~

假設(shè)account表有4條數(shù)據(jù),RR級(jí)別。

開(kāi)啟事務(wù)A,執(zhí)行當(dāng)前讀,查詢(xún)id>2的所有記錄。

再開(kāi)啟事務(wù)B,插入id=5的一條數(shù)據(jù)。

可以發(fā)現(xiàn),事務(wù)B執(zhí)行插入操作時(shí),阻塞了~因?yàn)槭聞?wù)A在執(zhí)行select ... lock in share mode的時(shí)候,不僅在 id = 3,4 這2條記錄上加了鎖,而且在id > 2 這個(gè)范圍上也加了間隙鎖。

因此,我們可以發(fā)現(xiàn),RR隔離級(jí)別下,加鎖的select, update, delete等語(yǔ)句,會(huì)使用間隙鎖+ 臨鍵鎖,鎖住索引記錄之間的范圍,避免范圍間插入記錄,以避免產(chǎn)生幻影行記錄。

原文標(biāo)題:一文徹底讀懂 MySQL 事務(wù)的四大隔離級(jí)別

文章出處:【微信公眾號(hào):數(shù)據(jù)分析與開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)庫(kù)
    +關(guān)注

    關(guān)注

    7

    文章

    3925

    瀏覽量

    66172
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    858

    瀏覽量

    27897

原文標(biāo)題:一文徹底讀懂 MySQL 事務(wù)的四大隔離級(jí)別

文章出處:【微信號(hào):DBDevs,微信公眾號(hào):數(shù)據(jù)分析與開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    MySQL的組成結(jié)構(gòu)與結(jié)構(gòu)化查詢(xún)語(yǔ)言詳解

    MySQL作為世界上最流行的開(kāi)源關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),采用了分層架構(gòu)設(shè)計(jì)
    的頭像 發(fā)表于 07-14 11:21 ?95次閱讀

    MySQL數(shù)據(jù)庫(kù)是什么

    MySQL數(shù)據(jù)庫(kù)是一種 開(kāi)源的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)(RDBMS) ,由瑞典MySQL AB公司開(kāi)發(fā),后被Oracle公司收購(gòu)。它通過(guò)結(jié)構(gòu)化查詢(xún)語(yǔ)言(SQL)進(jìn)行數(shù)據(jù)存儲(chǔ)、管理和操作,廣泛應(yīng)用于Web
    的頭像 發(fā)表于 05-23 09:18 ?433次閱讀

    使用插件將Excel連接到MySQL/MariaDB

    使用插件將 Excel 連接到 MySQL/MariaDB 適用于 MySQL 的 Devart Excel 插件允許您將 Microsoft Excel 連接到 MySQL 或 MariaDB
    的頭像 發(fā)表于 01-20 12:38 ?613次閱讀
    使用插件將Excel連接到<b class='flag-5'>MySQL</b>/MariaDB

    MySQL數(shù)據(jù)庫(kù)的安裝

    MySQL數(shù)據(jù)庫(kù)的安裝 【一】各種數(shù)據(jù)庫(kù)的端口 MySQL :3306 Redis :6379 MongoDB :27017 Django :8000 flask :5000 【二】MySQL 介紹
    的頭像 發(fā)表于 01-14 11:25 ?557次閱讀
    <b class='flag-5'>MySQL</b>數(shù)據(jù)庫(kù)的安裝

    UVLED固化機(jī)結(jié)構(gòu)的四大模塊

    UVLED固化機(jī)作為一種高效、節(jié)能的固化設(shè)備,在多個(gè)行業(yè)中發(fā)揮著重要作用。其結(jié)構(gòu)設(shè)計(jì)的合理性直接決定了設(shè)備的性能和使用效果。UVLED固化機(jī)的四大模塊主要包括光源系統(tǒng)、控制系統(tǒng)、散熱系統(tǒng)和傳送系統(tǒng)
    的頭像 發(fā)表于 11-25 16:10 ?872次閱讀
    UVLED固化機(jī)結(jié)構(gòu)的<b class='flag-5'>四大</b>模塊

    MySQL還能跟上PostgreSQL的步伐嗎

    Percona 的老板 Peter Zaitsev最近發(fā)表一篇博客,討論了MySQL是否還能跟上PostgreSQL的腳步。Percona 作為MySQL 生態(tài)扛旗者,Percona 開(kāi)發(fā)了知名
    的頭像 發(fā)表于 11-18 10:16 ?561次閱讀
    <b class='flag-5'>MySQL</b>還能跟上PostgreSQL的步伐嗎

    三、、六、八缸發(fā)動(dòng)機(jī)NVH特性詳解

    三、、六、八缸發(fā)動(dòng)機(jī)NVH特性詳解。 ?
    的頭像 發(fā)表于 11-16 11:45 ?678次閱讀
    三、<b class='flag-5'>四</b>、六、八缸發(fā)動(dòng)機(jī)NVH特性<b class='flag-5'>詳解</b>

    詳解MySQL多實(shí)例部署

    詳解MySQL多實(shí)例部署
    的頭像 發(fā)表于 11-11 11:10 ?629次閱讀

    MySQL編碼機(jī)制原理

    前言 一位讀者在本地部署 MySQL 測(cè)試環(huán)境時(shí)碰到一個(gè)問(wèn)題,我覺(jué)得挺有代表性的,所以寫(xiě)篇文章介紹一下,看完相信你會(huì)對(duì) MySQL 的編碼機(jī)制有最本質(zhì)的了解,本文的目錄結(jié)構(gòu)如下 讀者問(wèn)題簡(jiǎn)介
    的頭像 發(fā)表于 11-09 11:01 ?577次閱讀

    Spring事務(wù)實(shí)現(xiàn)原理

    作者:京東零售 范錫軍 1、引言 spring的spring-tx模塊提供了對(duì)事務(wù)管理支持,使用spring事務(wù)可以讓我們從復(fù)雜的事務(wù)處理中得到解脫,無(wú)需要去處理獲得連接、關(guān)閉連接、事務(wù)
    的頭像 發(fā)表于 11-08 10:10 ?1154次閱讀
    Spring<b class='flag-5'>事務(wù)</b>實(shí)現(xiàn)原理

    適用于MySQL的dbForge架構(gòu)比較

    dbForge Schema Compare for MySQL 是一種工具,用于輕松有效地比較和部署 MySQL 數(shù)據(jù)庫(kù)結(jié)構(gòu)和腳本文件夾差異。該工具提供了 MySQL 數(shù)據(jù)庫(kù)架構(gòu)中所有差異的全面視圖。
    的頭像 發(fā)表于 10-28 09:41 ?567次閱讀
    適用于<b class='flag-5'>MySQL</b>的dbForge架構(gòu)比較

    華納云:InnoDB 具有哪四大特性

    InnoDB 是 MySQL 數(shù)據(jù)庫(kù)中的一種存儲(chǔ)引擎,它具有許多特性,但通常被認(rèn)為有以下幾個(gè)主要特點(diǎn): 行級(jí)鎖定:InnoDB 支持行級(jí)鎖定,這意味著它在處理并發(fā)事務(wù)時(shí),只鎖定那些需要修改的行,而
    的頭像 發(fā)表于 08-14 16:02 ?498次閱讀

    MySQL知識(shí)點(diǎn)匯總

    大家好,這部分被稱(chēng)為DQL部分,是每個(gè)學(xué)習(xí)MySQL必須要學(xué)會(huì)的部分,下面就讓我來(lái)介紹MySQL中的其他部分。
    的頭像 發(fā)表于 08-05 15:27 ?651次閱讀
    <b class='flag-5'>MySQL</b>知識(shí)點(diǎn)匯總

    探秘四大主流芯片架構(gòu):誰(shuí)將主宰未來(lái)科技?

    在科技日新月異的今天,芯片作為現(xiàn)代電子設(shè)備的心臟,其架構(gòu)的選擇與設(shè)計(jì)顯得尤為重要。目前市場(chǎng)上主流的芯片架構(gòu)有種:X86、ARM、RISC-V和MIPS。它們各具特色,廣泛應(yīng)用于各種電子設(shè)備中。本文將詳細(xì)剖析這四大主流芯片架構(gòu)的特點(diǎn)、優(yōu)勢(shì)及應(yīng)用領(lǐng)域。
    的頭像 發(fā)表于 07-31 11:15 ?4799次閱讀
    探秘<b class='flag-5'>四大</b>主流芯片架構(gòu):誰(shuí)將主宰未來(lái)科技?

    華納云:如何修改MySQL的默認(rèn)端口

    MySQL是世界上最流行的開(kāi)源關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)之一。在某些情況下,由于安全性、網(wǎng)絡(luò)策略或端口沖突的原因,數(shù)據(jù)庫(kù)管理員可能需要更改MySQL服務(wù)的默認(rèn)監(jiān)聽(tīng)端口。本文將指導(dǎo)您如何在不同的操作系統(tǒng)上
    的頭像 發(fā)表于 07-22 14:56 ?534次閱讀
    華納云:如何修改<b class='flag-5'>MySQL</b>的默認(rèn)端口