InnoDB引擎事務總結

什么是局部性原理?

數據預讀的思路是:磁盤讀寫并不是按需讀取,而是按頁預讀,一次會讀一頁的數據,每次加載更多的數據,以便未來減少磁盤IO

局部性原理:軟件設計要盡量遵循“數據讀取集中”與“使用到一個數據,大概率會使用其附近的數據”,這樣磁盤預讀能充分提高磁盤IO

B樹

B樹,如上圖,它的特點是:
(1)不再是二叉搜索,而是m叉搜索;
(2)葉子節點,非葉子節點,都存儲數據;
(3)中序遍歷,可以獲得所有節點;
非根節點包含的關鍵字個數j滿足,(┌m/2┐)-1 <= j <= m-1,節點分裂時要滿足這個條件

B+樹

(1)非葉子節點不再存儲數據,數據只存儲在同一層的葉子節點上;
(2)葉子之間,增加了鏈表,獲取所有節點,不再需要中序遍歷;
數據庫的索引最常用B+樹:
(1)很適合磁盤存儲,能夠充分利用局部性原理,磁盤預讀。
比如:每個節點是1頁的大小,4k,key占8個字節,j = 512, 根據(┌m/2┐)1 <= j <= m-1, m=j + 1,即513
(2)很低的樹高度,能夠存儲大量數據;
(3)索引本身占用的內存很小;
(4)能夠很好的支持單點查詢,范圍查詢,有序性查詢;

InnoDB的索引

InnoDB的索引有兩類索引,聚集索引(Clustered Index)與普通索引(Secondary Index)。
InnoDB的每一個表都會有聚集索引:
(1)如果表定義了PK,則PK就是聚集索引;
(2)如果表沒有定義PK,則第一個非空unique列是聚集索引;
(3)否則,InnoDB會創建一個隱藏的row-id作為聚集索引;
索引的結構是B+樹,這里不展開B+樹的細節,說幾個結論:
(1)在索引結構中,非葉子節點存儲key,葉子節點存儲value;
(2)聚集索引,葉子節點存儲行記錄(row);
(3)普通索引,葉子節點存儲了PK的值;
InnoDB的普通索引,實際上會掃描兩遍:
第一遍,由普通索引找到PK;
第二遍,由PK找到行記錄;

隔離性

事務ACID特性,其中I代表隔離性(Isolation)
隔離性是指多個并發事務之間要相互隔離。
并發事務之間相互干擾,可能導致事務出現讀臟,不可重復度,幻讀等問題

不隔離可能導致的問題

假設有InnoDB表:
t(id PK, name);
表中有三條記錄:
1, shenjian
2, zhangsan
3, lisi

case 1
事務A,先執行,處于未提交的狀態:
insert into t values(4, wangwu);

事務B,后執行,也未提交:
select * from t;

如果事務B能夠讀取到(4, wangwu)這條記錄,事務A就對事務B產生了影響,這個影響叫做“讀臟”,讀到了未提交事務操作的記錄。

case 2
事務A,先執行:
select * from t where id=1;

結果集為:
1, shenjian

事務B,后執行,并且提交:
update t set name=xxoo where id=1;
commit;

事務A,再次執行相同的查詢:
select * from t where id=1;

結果集為:
1, xxoo
這次是已提交事務B對事務A產生的影響,這個影響叫做“不可重復讀”,一個事務內相同的查詢,得到了不同的結果。

case 3
事務A,先執行:
select * from t where id>3;
結果集為:
NULL

事務B,后執行,并且提交:
insert into t values(4, wangwu);
commit;

事務A,首次查詢了id>3的結果為NULL,于是想插入一條為4的記錄:
insert into t values(4, xxoo);

結果集為:
Error : duplicate key!

事務A的內心OS是:你TM在逗我,查了id>3為空集,insert id=4告訴我PK沖突?

這次是已提交事務B對事務A產生的影響,這個影響叫做“幻讀”。
可以看到,并發的事務可能導致其他事務:

  • 讀臟

  • 不可重復讀

  • 幻讀

事務的隔離級別:

按照SQL92標準,InnoDB實現了四種不同事務的隔離級別:

  • 讀未提交(Read Uncommitted)
  • 讀提交(Read Committed, RC)
  • 可重復讀(Repeated Read, RR)
  • 串行化(Serializable)
    不同事務的隔離級別,實際上是一致性與并發性的一個權衡與折衷。

隔離級別實現

InnoDB使用不同的鎖策略(Locking Strategy)來實現不同的隔離級別。

(1)讀未提交:select不加鎖,可能出現讀臟;

(2)讀提交(RC):普通select快照讀,鎖select /update /delete 會使用記錄鎖,可能出現不可重復讀;

(3)可重復讀(RR):普通select快照讀,鎖select /update /delete 根據查詢條件情況,會選擇記錄鎖,或者間隙鎖/臨鍵鎖,以防止讀取到幻影記錄;

(4)串行化:select隱式轉化為select ... in share mode,會被update與delete互斥;

InnoDB默認的隔離級別是RR,用得最多的隔離級別是RC

mysql的鎖

默認的事務隔離級別為可重復讀(Repeated Read, RR)

記錄鎖(Record Locks)

記錄鎖,它封鎖索引記錄,例如:
select * from t where id=1 for update;
它會在id=1的索引記錄上加鎖,以阻止其他事務插入,更新,刪除id=1的這一行。
需要說明的是:
select * from t where id=1;
則是快照讀(SnapShot Read),它并不加鎖

間隙鎖(Gap Locks)

間隙鎖,它封鎖索引記錄中的間隔,或者第一條索引記錄之前的范圍,又或者最后一條索引記錄之后的范圍。

依然是上面的例子,InnoDB,RR:
t(id PK, name KEY, sex, flag);
表中有四條記錄:
1, shenjian, m, A
3, zhangsan, m, A
5, lisi, m, A
9, wangwu, f, B

這個SQL語句

select * from t 
    where id between 8 and 15 
    for update;

會封鎖區間,以阻止其他事務id=10的記錄插入。
為什么要阻止id=10的記錄插入?
如果能夠插入成功,頭一個事務執行相同的SQL語句,會發現結果集多出了一條記錄,即幻影數據。
間隙鎖的主要目的,就是為了防止其他事務在間隔中插入數據,以導致“不可重復讀”。
如果把事務的隔離級別降級為讀提交(Read Committed, RC),間隙鎖則會自動失效。

臨鍵鎖(Next-Key Locks)

臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖范圍,既包含索引記錄,又包含索引區間。
更具體的,臨鍵鎖會封鎖索引記錄本身,以及索引記錄之前的區間。
如果一個會話占有了索引記錄R的共享/排他鎖,其他會話不能立刻在R之前的區間插入新的索引記錄。
依然是上面的例子,InnoDB,RR:
t(id PK, name KEY, sex, flag);
表中有四條記錄:
1, shenjian, m, A
3, zhangsan, m, A
5, lisi, m, A
9, wangwu, f, B

PK上潛在的臨鍵鎖為:
(-infinity, 1]
(1, 3]
(3, 5]
(5, 9]
(9, +infinity]

臨鍵鎖的主要目的,也是為了避免幻讀(Phantom Read)。如果把事務的隔離級別降級為RC,臨鍵鎖則也會失效。

共享/排它鎖(Shared and Exclusive Locks)

(1)事務拿到某一行記錄的共享S鎖,才可以讀取這一行;
(2)事務拿到某一行記錄的排它X鎖,才可以修改或者刪除這一行;

意向鎖(Intention Locks)

InnoDB支持多粒度鎖(multiple granularity locking),它允許行級鎖與表級鎖共存,實際應用中,InnoDB使用的是意向鎖。
意向鎖是指,未來的某個時刻,事務可能要加共享/排它鎖了,先提前聲明一個意向。
意向鎖有這樣一些特點:
(1)首先,意向鎖,是一個表級別的鎖(table-level locking);
(2)意向鎖分為:
意向共享鎖(intention shared lock, IS),它預示著,事務有意向對表中的某些行加共享S鎖
意向排它鎖(intention exclusive lock, IX),它預示著,事務有意向對表中的某些行加排它X鎖
舉個例子:
select ... lock in share mode,要設置IS鎖;
select ... for update,要設置IX鎖;

(3)意向鎖協議(intention locking protocol)并不復雜:
事務要獲得某些行的S鎖,必須先獲得表的IS鎖
事務要獲得某些行的X鎖,必須先獲得表的IX鎖
(4)由于意向鎖僅僅表明意向,它其實是比較弱的鎖,意向鎖之間并不相互互斥,而是可以并行

插入意向鎖(Insert Intention Locks)

對已有數據行的修改與刪除,必須加強互斥鎖X鎖,那對于數據的插入,是否還需要加這么強的鎖,來實施互斥呢?插入意向鎖,孕育而生。
插入意向鎖,是間隙鎖(Gap Locks)的一種(所以,也是實施在索引上的),它是專門針對insert操作的。
多個事務,在同一個索引,同一個范圍區間插入記錄時,如果插入的位置不沖突,不會阻塞彼此
在MySQL,InnoDB,RR下:
t(id unique PK, name);

數據表中有數據:
10, shenjian
20, zhangsan
30, lisi

事務A先執行,在10與20兩條記錄中插入了一行,還未提交:
insert into t values(11, xxx);
事務B后執行,也在10與20兩條記錄中插入了一行:
insert into t values(12, ooo);
(1)會使用什么鎖?
(2)事務B會不會被阻塞呢?

回答:雖然事務隔離級別是RR,雖然是同一個索引,雖然是同一個區間,但插入的記錄并不沖突,故這里:
使用的是插入意向鎖
并不會阻塞事務B

自增鎖(Auto-inc Locks)

自增鎖是一種特殊的表級別鎖(table-level lock),專門針對事務插入AUTO_INCREMENT類型的列。最簡單的情況,如果一個事務正在往表中插入記錄,所有其他事務的插入必須等待,以便第一個事務插入的行,是連續的主鍵值。
與此同時,InnoDB提供了innodb_autoinc_lock_mode配置,可以調節與改變該鎖的模式與行為。
一,案例說明

MySQL,InnoDB,默認的隔離級別(RR),假設有數據表:

t(id AUTO_INCREMENT, name);

數據表中有數據:

1, shenjian

2, zhangsan

3, lisi

事務A先執行,還未提交:

insert into t(name) values(xxx);

事務B后執行:

insert into t(name) values(ooo);

問:事務B會不會被阻塞?

二,案例分析

InnoDB在RR隔離級別下,能解決幻讀問題,上面這個案例中:

(1)事務A先執行insert,會得到一條(4, xxx)的記錄,由于是自增列,故不用顯示指定id為4,InnoDB會自動增長,注意此時事務并未提交;

(2)事務B后執行insert,假設不會被阻塞,那會得到一條(5, ooo)的記錄;

此時,并未有什么不妥,但如果,

(3)事務A繼續insert:

insert into t(name) values(xxoo);

會得到一條(6, xxoo)的記錄。

(4)事務A再select:

select * from t where id>3;

得到的結果是:

4, xxx

6, xxoo

畫外音:不可能查詢到5的記錄,再RR的隔離級別下,不可能讀取到還未提交事務生成的數據。

這對于事務A來說,就很奇怪了,對于AUTO_INCREMENT的列,連續插入了兩條記錄,一條是4,接下來一條變成了6,就像莫名其妙的幻影。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容

  • 酒過三巡,燈火通明,伴著音樂,往事歷歷在目,看著眼前的人,突然覺得那么陌生,莫名的恐慌如龍卷風般席卷而來,傾襲你的...
    見素_藍色鳶尾閱讀 223評論 0 0
  • 啊啊啊……說到淘寶我不自覺地就想來回答這個問題。自從認識馬爸爸以后,我酷愛逛淘寶,特別是衣服類~就算不買看著那些漂...
    是張姑娘啊閱讀 9,093評論 4 83
  • 星期六下午,我和媽媽和阿姨一家一起去山上采楊梅。 今天,艷陽高照,太陽火辣辣的照射著大地。我們一行人沿著一條陡峭的...
    秋風靜美閱讀 349評論 0 1