日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

從事務(wù)的隔離級(jí)別談起

眾所周知,事務(wù)有四大特性,簡(jiǎn)稱ACID:原子性、一致性、隔離性、持久性。

對(duì)于隔離性,簡(jiǎn)單來說就是多個(gè)事務(wù)之間是彼此隔離的,互不影響。但想要做到完全的互不影響是很難的,因?yàn)閿?shù)據(jù)的強(qiáng)一致性,很多時(shí)候需要犧牲性能去達(dá)成。比如如果我們能接受事務(wù)的串行執(zhí)行,那一定是互不影響的。然而現(xiàn)實(shí)是,MySQL作為一個(gè)數(shù)據(jù)庫(kù),必然是要支持一定程度的并行執(zhí)行的,也就是多個(gè)事務(wù)同時(shí)去執(zhí)行。

?

凡并行程序,往往是在性能和數(shù)據(jù)一致性上做取舍。較好的解決方案要么是最終一致,要么是盡量縮小串行執(zhí)行的范圍。

?

如果多個(gè)事務(wù)同時(shí)并行執(zhí)行,在沒有隔離的情況,可能會(huì)發(fā)生臟讀、不可重復(fù)讀、幻讀的問題。

案例數(shù)據(jù)(demo表):

id(主鍵) c(普通索引) d(無索引) 5 5 5 10 10 10 15 15 15 20 20 20 25 25 25

「臟讀」

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

InnoDB的行鎖,原來為你做了這么多

 

臟讀

「不可重復(fù)讀」

一個(gè)事務(wù)讀取同一行數(shù)據(jù),多次讀取結(jié)果不同。

InnoDB的行鎖,原來為你做了這么多

 

不可重復(fù)讀

「幻讀」

一個(gè)事務(wù)讀取到了別的事務(wù)插入的數(shù)據(jù)。

InnoDB的行鎖,原來為你做了這么多

 

幻讀

但I(xiàn)nnoDB因?yàn)槭褂昧薓VCC,讀取的是“快照”版本,有一些不同,但如果不上鎖,同樣可能會(huì)有幻讀問題。

InnoDB的行鎖,原來為你做了這么多

 

InnoDB的幻讀

事務(wù)用了四種不同的隔離級(jí)別用來解決這些問題。

  • Read uncommitted(未提交讀)
  • Read Committed(已提交讀,簡(jiǎn)稱RC)
  • Repeatable Reads(可重復(fù)讀,簡(jiǎn)稱RR)
  • Serializable(串行化)

隔離級(jí)別越高,解決的問題越多,但并發(fā)性能也會(huì)越差。它們之間的關(guān)系如下表:

隔離級(jí)別 臟讀 不可重復(fù)讀 幻讀 Read uncommitted 是 是 是 Read Committed 是 是 Repeatable Reads 是 Serializable

?

但I(xiàn)nnoDB有些許不同,InnoDB默認(rèn)的隔離級(jí)別是RR,但是通過MVCC和間隙鎖來一定程度上的解決了幻讀的問題。這也是我們今天這篇文章后面會(huì)詳細(xì)介紹的。

?

無鎖思想:MVCC

MVCC即“多版本并發(fā)控制”,但是它在很多情況下避免了加鎖操作,因此開銷更低。

主流的關(guān)系型數(shù)據(jù)庫(kù)都實(shí)現(xiàn)了MVCC,但實(shí)現(xiàn)機(jī)制各有不同。實(shí)際上MVCC也沒有一個(gè)統(tǒng)一的標(biāo)準(zhǔn)。但大都實(shí)現(xiàn)了非阻塞的讀操作,寫操作也只是鎖定必要的行。本文以下內(nèi)容所說的MVCC都指的是InnoDB實(shí)現(xiàn)的MVCC。

在Mysql的InnoDB引擎,是通過給每行記錄后面保存兩個(gè)隱藏的列來實(shí)現(xiàn)的。一個(gè)是保存行的創(chuàng)建時(shí)間,另一個(gè)保存了行的過期時(shí)間(或刪除時(shí)間)。

?

實(shí)際上存儲(chǔ)的并不是實(shí)際的一個(gè)時(shí)間戳,而是“系統(tǒng)版本號(hào)”。

?

每次開啟一個(gè)事務(wù),系統(tǒng)版本號(hào)都會(huì)遞增。事務(wù)開始時(shí),系統(tǒng)版本號(hào)會(huì)作為事務(wù)的版本號(hào),用來和查詢到的行的版本號(hào)進(jìn)行比較。

MVCC只在REPEATABLE READ和READ COMMITTED兩個(gè)隔離級(jí)別下工作,其它兩個(gè)隔離級(jí)別不能工作。因?yàn)镽EAD UNCOMMITTED總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行。而SERIALIZABLE則會(huì)對(duì)所有讀取的行都加鎖。

在MySQL中,正常的SELECT語句,后面不加FOR UPDATE和LOCK IN SHARE MODE的,就是用的MVCC去讀。

?

MVCC和我們?cè)趹?yīng)用層面去實(shí)現(xiàn)的“樂觀鎖”有一樣的思想:用版本號(hào),在盡量無鎖的情況下實(shí)現(xiàn)一定程度的一致性。

?

InnoDB行鎖的概念

InnoDB的行鎖(也稱為臨鍵鎖) Next-Key Locks,「是MySQL對(duì)外暴露的鎖的基本單位,它會(huì)智能選擇記錄鎖或間隙鎖,鎖住一行或多行或一個(gè)間隙」。而記錄鎖又分為共享鎖和排他鎖,間隙鎖的概念下面有一個(gè)插入意向鎖。這些鎖的關(guān)系大概是這樣:

InnoDB的行鎖,原來為你做了這么多

 

鎖的關(guān)系

記錄鎖

所謂記錄鎖 Record Locks,就是鎖住確定的一行行記錄。它分為共享鎖和排它鎖。分別對(duì)應(yīng)不同的SQL寫法。

共享鎖

共享鎖 Shared Locks ,簡(jiǎn)稱S鎖。使用以下SQL可能觸發(fā):

SELECT ... LOCK IN SHARE MODE

之所以說“可能”觸發(fā),是因?yàn)樗榈搅藬?shù)據(jù)庫(kù)有確定的記錄才會(huì)鎖住這些記錄,否則會(huì)變成間隙鎖。這個(gè)其實(shí)很好理解,找到了數(shù)據(jù),才鎖它。如果沒找到數(shù)據(jù),就鎖這個(gè)間隙。

排他鎖

排他鎖 Exclusive locks ,簡(jiǎn)稱X鎖。使用一下SQL可能觸發(fā):

SELECT ... FOR UPDATE

這里的“可能”含義與上面同理,不贅述。

間隙鎖

間隙鎖 Gap - Lock,顧名思義,鎖住一個(gè)間隙。上文我們提到過,InnoDB默認(rèn)的隔離級(jí)別是RR,但是通過間隙鎖來一定程度上的解決了幻讀的問題。它是怎么解決的呢?就是通過間隙鎖來解決的。

上面兩種SQL,如果沒有查找到確定的記錄,就會(huì)根據(jù)條件去鎖住一個(gè)間隙。間隙鎖是根據(jù)已有數(shù)據(jù)的一個(gè)左開右閉的區(qū)間。

還是這個(gè)案例數(shù)據(jù)(假設(shè)數(shù)據(jù)都是從1開始):

id(主鍵) c(普通索引) d(無索引) 5 5 5 10 10 10 15 15 15 20 20 20 25 25 25

對(duì)于下面這些區(qū)間的操作,會(huì)有對(duì)應(yīng)的間隙鎖:(0, 5], (5, 10], (10, 15], (15, 20], (20, 25], (25, 正無窮)。

什么意思呢?假如你的SQL查詢的范圍不同,那它鎖住的區(qū)間就不同。比如:

-- 鎖住(0, 5]
SELECT * FROM demo where id = 3;
-- 鎖住(10, 15]
SELECT * FROM demo where id = 11;

間隙鎖其實(shí)是“共享”的。也就是說,多個(gè)事務(wù)可以獲取同一個(gè)區(qū)間的間隙鎖。

InnoDB的行鎖,原來為你做了這么多

 

間隙鎖不互相阻塞

插入意向鎖

插入意向鎖 Insert Intention Locks,代表當(dāng)前事務(wù)準(zhǔn)備插入一行數(shù)據(jù)。使用INSERT/UPDATE/DELETE等語句會(huì)獲得插入意向鎖。

「插入意向鎖和插入意向鎖之間是兼容的,只要插入的鍵值不同,就不會(huì)相互阻塞」。比如以下兩個(gè)SQL,在不同的事務(wù)中,哪怕它們?cè)谕粋€(gè)間隙,只要沒有間隙鎖,就不會(huì)阻塞:

InnoDB的行鎖,原來為你做了這么多

 

插入兩條不同的記錄不阻塞

但如果兩個(gè)事務(wù)插入同一個(gè)key,那就會(huì)阻塞。

InnoDB的行鎖,原來為你做了這么多

 

兩個(gè)插入意向鎖阻塞的情況

插入意向鎖可以保證兩個(gè)事務(wù)插入key不同的數(shù)據(jù)的時(shí)候不沖突,提升并發(fā)性。

「但是間隙鎖會(huì)阻塞插入意向鎖」!這也可以理解,因?yàn)镮nnoDB想在RR隔離級(jí)別就解決幻讀問題。所以A事務(wù)用SELECT語句獲取了一個(gè)間隙鎖,自然不希望B事務(wù)在這個(gè)期間往這個(gè)間隙插入一條新的記錄。

InnoDB的行鎖,原來為你做了這么多

 

間隙鎖阻塞插入意向鎖

與索引的關(guān)系

不管哪種行級(jí)鎖,「行級(jí)鎖的其實(shí)都是索引」。所以在上面的demo中,如果對(duì)id(主鍵)或者column c(普通索引)操作,都會(huì)觸發(fā)相應(yīng)的行級(jí)鎖,但如果對(duì)column d(無索引)做同樣的操作,InnoDB就會(huì)對(duì)表中所有數(shù)據(jù)加鎖,實(shí)際效果跟表級(jí)鎖一樣。

所以一定要注意,如果要上鎖,需要注意是否走了索引,不要弄成了表級(jí)鎖造成安全事故。

一個(gè)死鎖案例

最后給一個(gè)關(guān)于間隙鎖和插入意向鎖的死鎖案例吧,也是之前在項(xiàng)目上遇到過的真實(shí)案例。

過程

  • 事務(wù)A select ... for update 查找一個(gè)不存在的數(shù)據(jù),獲得間隙鎖;
  • 事務(wù)B select ... for update 查找一個(gè)不存在的數(shù)據(jù),獲得相同位置的間隙鎖;
  • 事務(wù)A insert into ... 到這個(gè)間隙,嘗試獲得插入意向鎖,但被事務(wù)B持有的間隙鎖阻塞。
  • 事務(wù)B insert into ... 到這個(gè)間隙,嘗試獲得插入意向鎖,但被事務(wù)A持有的間隙鎖阻塞,MySQL監(jiān)測(cè)到死鎖,回滾事務(wù)B;
  • 因?yàn)槭聞?wù)B回滾,所以事務(wù)B持有的間隙鎖被釋放,所以之前事務(wù)A的插入語句不再阻塞,順利執(zhí)行插入操作。

?

報(bào)錯(cuò)信息:Deadlock found when trying to get lock; try restarting transaction

?

用圖把這個(gè)過程描述出來:

InnoDB的行鎖,原來為你做了這么多

 

死鎖過程

最終結(jié)果

事務(wù)A插入成功,事務(wù)B因?yàn)樗梨i被回滾。

總結(jié)

間隙鎖主要還是用于防止幻讀的情況,所以多個(gè)事務(wù)能夠同時(shí)獲取同一段間隙鎖本身并沒有問題,間隙鎖能夠阻塞插入意向鎖也并沒有問題。

而插入意向鎖可以看成是一種特殊的間隙鎖,是用于在同一個(gè)間隙,插入不同的數(shù)據(jù),不會(huì)互相阻塞。它比普通間隙鎖的數(shù)據(jù)一致性更低,但并發(fā)性能更好;

解決思路

普通間隙鎖還是主要用于讀操作防止幻讀。所以我們?cè)谙脒M(jìn)行插入操作的時(shí)候,其實(shí)沒有必要對(duì)即將插入的間隙使用普通間隙鎖,直接使用insert語句產(chǎn)生的插入意向鎖就好了。

如果要保證數(shù)據(jù)的一致性,可以使用插入意向鎖配合主鍵、唯一鍵等約束。

附錄

那些在文章中用過的SQL:

-- 建表
create table demo
(
 id int unsigned not null,
 c int unsigned not null,
 d int unsigned not null,
 constraint demo_pk
  primary key (id),
 constraint idx_c
  unique (c)
);
-- 插數(shù)據(jù)
INSERT INTO demo VALUES
(5, 5, 5),
(10, 10, 10),
(15, 15, 15),
(20, 20, 20),
(25, 25, 25);
-- 禁止自動(dòng)提交事務(wù)
SET AUTOCOMMIT = 0;
-- 開啟事務(wù)
BEGIN;
-- 提交事務(wù)
COMMIT;
-- 會(huì)獲取鎖的查詢
SELECT * FROM demo WHERE id < 5 FOR UPDATE ;

關(guān)于作者

我是Yasin,一個(gè)不斷精進(jìn)的菜雞。

微信公眾號(hào):編了個(gè)程

個(gè)人網(wǎng)站:https://yasinshaw.com

關(guān)注我的公眾號(hào),和我一起成長(zhǎng)~

分享到:
標(biāo)簽:InnoDB 行鎖
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定