国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術(shù)|

服務(wù)器之家 - 數(shù)據(jù)庫 - Mysql - MySQL InnoDB 事務(wù)鎖源碼分析

MySQL InnoDB 事務(wù)鎖源碼分析

2021-11-17 17:24github Mysql

InnoDB 事務(wù)鎖,事務(wù)鎖實(shí)現(xiàn)起來的代碼卻是又臭又硬的好大一坨,各種細(xì)節(jié),今天小編在這里整理一個(gè)源碼閱讀筆記,把那一坨加鎖相關(guān)的代碼提煉出來,感興趣的小伙伴別忘記收藏奧

本文前提:

代碼mysql 8.0.13

只整理repeatable read當(dāng)前讀。read committed簡(jiǎn)單很多,另外快照讀是基于mvcc不用加鎖,所以不在本文討論范疇。

1. lock 與 latch

innodb 中的lock是事務(wù)中對(duì)訪問/修改的record加的鎖,它一般是在事務(wù)提交或回滾時(shí)釋放。latch是在btree上定位record的時(shí)候?qū)tree pages加的鎖,它一般是在對(duì)page中對(duì)應(yīng)record加上lock并且完成訪問/修改后就釋放,latch的鎖區(qū)間比lock小很多。在具體的實(shí)現(xiàn)中,一個(gè)大的transaction會(huì)被拆成若干小的mini transaction(mtr),如下圖所示:有一個(gè)transaction,依次做了insertselect…for updateupdate操作,這3個(gè)操作分別對(duì)應(yīng)3個(gè)mtr,每個(gè)mtr完成:

  • 在btree查找目標(biāo)record,加相關(guān)page latch
  • 加目標(biāo)record lock,修改對(duì)應(yīng)record
  • 釋放page latch

為什么要這么做呢?是為了并發(fā),事務(wù)中的每一個(gè)操作,在步驟二完成之后,相應(yīng)的record已經(jīng)加上了lock保護(hù)起來,確保其他并發(fā)事務(wù)無法修改,所以這時(shí)候沒必要還占著record所在的page latch,否則其他事務(wù) 訪問/修改 相同page的不同record時(shí),這本來是可以并行做的事情,在這里會(huì)被page latch會(huì)被卡住。

MySQL InnoDB 事務(wù)鎖源碼分析

lock是存在lock_sys->rec_hash中,每個(gè)record lockrec_hash中通過<space_id, page_no, heap_no>來標(biāo)識(shí)

latch是存在bufferpool對(duì)應(yīng)pageblock中,對(duì)應(yīng)block->lock

本文只關(guān)注lock相關(guān)的東西,latch后面單獨(dú)搞一篇整理

2. repeatable read

具體每個(gè)隔離級(jí)別就不展開說了,這里主要說下rr,從名字上也能看出來,rr支持可重復(fù)度,也就是在一個(gè)事務(wù)中,多次執(zhí)行相同的select…for update應(yīng)該看到相同的結(jié)果集(除本事務(wù)修改外),這個(gè)就要求select的區(qū)間里不能有其他事務(wù)插入新的record,所以select除了對(duì)滿足條件的record加lock之外,對(duì)相應(yīng)區(qū)間也要加lock來保護(hù)起來。在innodb的實(shí)現(xiàn)中,并沒有一個(gè)一下鎖住某個(gè)指定區(qū)間的鎖,而是把一個(gè)大的區(qū)間鎖拆分放在區(qū)間中已有的多個(gè)record上來完成。所以引入了gap lock和next-key lock的概念,它們加再一個(gè)具體的record上

  • gap lock 保護(hù)這個(gè)record與其前一個(gè)record之間的開區(qū)間
  • next-key lock 保護(hù)包含這個(gè)record與其前一個(gè)record之間的左開右閉區(qū)間

它們都是為了保護(hù)這個(gè)區(qū)間不能被別的事務(wù)插入新的record,實(shí)現(xiàn)rr。

接下來從源碼實(shí)現(xiàn)上來分別看下insert和select是如何加lock的,結(jié)合著看也就知道innodb的rr是如何實(shí)現(xiàn)的了。insert的加鎖分布在insert操作的過程中,遍布在多個(gè)相關(guān)的函數(shù)里,select的加鎖則比較集中,就在row_search_mvcc里。

3. insert加鎖流程

3.1 lock mode

lock的mode主要有share(s)和exclusive(x)【代碼中對(duì)應(yīng)lock_s和lock_x】

lock的gap mode主要有record lock, gap lock, next-key lock【代碼中對(duì)應(yīng)lock_rec_not_gap, lock_gap, lock_ordinary】

在具體使用中將 mode|gap_mode 之后就是一個(gè)lock的實(shí)際類型,record lock是作用在單個(gè)record上的記錄鎖,gap lock/next-key lock雖然也是加在某個(gè)具體record上,但作用是為了確保record前面的gap不要有其他并發(fā)事務(wù)插入,這個(gè)具體是怎么實(shí)現(xiàn)呢,innodb引入了一個(gè)插入意向鎖,他的實(shí)際類型是

(lock_x | lock_gap | lock_insert_intention)

gap lock/next-key lock互斥,如果要插入前檢測(cè)到插入位置的next record上有l(wèi)ock,則會(huì)嘗試對(duì)這個(gè)next record加一個(gè)插入意向鎖,代表本事務(wù)打算給這個(gè)gap里插一個(gè)新record,看行不行?如果已經(jīng)有別的事務(wù)給這里上了gap/next-key lock,代表它想保護(hù)這里,所以當(dāng)前插入意向鎖需要等待相關(guān)事務(wù)提交才行。這個(gè)檢測(cè)只是單向的,即插入意向鎖需等待gap/next-key lock釋放,而任何鎖不用等待插入意向鎖釋放,否則嚴(yán)重影響這個(gè)gap中不沖突的insert操作并發(fā)。

具體的鎖沖突檢測(cè)在lock_rec_has_to_wait函數(shù)中,大體原則就是:判斷兩個(gè)lock兼容還是不兼容,首先先做mode的沖突檢測(cè)

MySQL InnoDB 事務(wù)鎖源碼分析

如果不沖突,則代表鎖兼容,無需等待,如果沖突,則接著做gap mode的沖突例外檢測(cè),整理如下:

MySQL InnoDB 事務(wù)鎖源碼分析

如果gap mode不沖突,則作為例外情況可以認(rèn)為鎖兼容,無需等待。可以看到:

  • 插入意向鎖需要等待gap locknext-key lock
  • 任何鎖不用等待插入意向鎖
  • gap lock無需等待任何鎖
  • next-key lock需要等待其他next-key lock及record lock,反之亦然

了解了這些鎖兼容原則,接下來就可以看在實(shí)際insert流程中是如何使用它們的。

3.2 加鎖流程

insert的順序是先插入主鍵索引,再依次插入二級(jí)索引。以下是從代碼中整理出來的流程,插入某個(gè)entry的操作,

【對(duì)于主鍵索引】:

(1)先在查找btree,加相關(guān)page latch,定位到entry對(duì)應(yīng)插入位置的record (<= entry)

(2)如果要插入的entry已經(jīng)存在,即entry = record此時(shí)接著判斷:

  • 如果是insert on duplicate key update,則對(duì)recordx next-key lock
  • 如果是普通insert,則對(duì)records next-key lock

之后接著判斷record是否是deleted mark:

  • 如果不是delete mark,說明的確有duplicate,返回db_duplicate_key到上層,然后上層通過看是insert on duplicate key update還是普通insert來決定是轉(zhuǎn)成update操作繼續(xù)還是給用戶報(bào)錯(cuò)duplicate
  • 如果是deleted mark,則說明實(shí)際沒有duplicate record,接著往下走

(3)判斷record的下一個(gè)record上當(dāng)前有沒有鎖,如果有的話,則給其加插入意向鎖,確保要插入entry的區(qū)間沒有其他gap lock/next-key lock保護(hù)

(4)插入entry

(5)釋放page latch,此時(shí)依舊占有l(wèi)ock

【對(duì)于二級(jí)索引】

(1)先在查找btree,加相關(guān)page latch,定位到entry對(duì)應(yīng)插入位置的record (<= entry)

(2)如果要插入的entry已經(jīng)存在,即entry = record,并且當(dāng)前index是unique:

  • 如果是insert on duplicate key update,則對(duì)recordx next-key lock
  • 如果是普通insert,則對(duì)record2s next-key lock

判斷record與entry是否相等:

如果相等 并且 是普通insert,則接著判斷record是否是deleted mark:

  •  如果不是delete mark,說明的確有duplicate,返回db_duplicate_key到上層,然后上層通過看是insert on duplicate key update還是普通insert來決定是轉(zhuǎn)成update操作繼續(xù)還是給用戶報(bào)錯(cuò)duplicate
  • 如果是delete mark,則實(shí)際沒有duplicate,接著往下走

(3)如果是insert on duplicate key update 并且 當(dāng)前index是unique,則給其下一個(gè)record x gap lock,保護(hù)不會(huì)被其他事務(wù)插入相同的entry

(4)判斷record的下一個(gè)record上當(dāng)前有沒有鎖,如果有的話,則給其加插入意向鎖

(lock_x | lock_gap | lock_insert_intention)

確保要插入entry的區(qū)間沒有其他gap lock/next-key lock保護(hù)

(5)插入entry

(6)釋放page latch

:【二級(jí)索引】的步驟3似乎有些多余,因?yàn)榧词褂衅渌l(fā)事務(wù)使用insert on duplicate key update來插入相同record的話,和【主鍵索引】流程一樣,步驟1也只能串行進(jìn)入,第一個(gè)線程沒有找到與entry相同的record,走步驟4插入,直到步驟6結(jié)束釋放page latch之后,第二個(gè)線程才能進(jìn)到步驟1里,此時(shí)在步驟2中會(huì)中卡在加record的x next-key lock上,直到線程一事務(wù)提交之后才能接著進(jìn)行,所以看起來不會(huì)沖突?

上述流程在row_ins_index_entry函數(shù)中,具體入口如下:

?
1
2
3
4
mysql_parse->mysql_execute_command->sql_cmd_dml::execute->
sql_cmd_insert_values::execute_inner->write_record->handler::ha_write_row->
ha_innobase::write_row->row_insert_for_mysql->row_insert_for_mysql_using_ins_graph->
row_ins_step->row_ins->row_ins_index_entry_step->row_ins_index_entry

其中插入意向鎖是在lock_rec_insert_check_and_lock函數(shù)里加的,入口如下:

?
1
2
3
row_ins_index_entry->row_ins_clust_index_entry/row_ins_sec_index_entry->
btr_cur_optimistic_insert/btr_cur_pessimistic_insert->btr_cur_ins_lock_and_undo->
lock_rec_insert_check_and_lock

3.3 隱式鎖

另外要提的一點(diǎn)就是,insert操作不會(huì)顯式的加鎖,每一條insert的record上都默認(rèn)有一個(gè)隱式鎖,它是通過record的隱藏字段trx_id來檢測(cè)的,對(duì)于主鍵索引,如果要插入的record在btree中找到,那么只需要通過比較已有record的trx_id,如果這個(gè)trx_id對(duì)應(yīng)的事務(wù)還是活躍事務(wù),那么說明這個(gè)record的插入事務(wù)還未提交,隱式代表這個(gè)record上有鎖,那么此時(shí)就才會(huì)將其轉(zhuǎn)成顯式鎖放進(jìn)lock_sys中并wait,這樣做是為了提高性能,盡量減少對(duì)lock_sys的操作。對(duì)于二級(jí)索引的隱式鎖檢測(cè)就沒有主鍵索引這么容易了,因?yàn)槎?jí)索引record沒有記錄trx_id,只能首先通過其所在page上的max_trx_id與當(dāng)前活躍事務(wù)列表的最小trx_id來比較,小于它的話代表最后一次修改這個(gè)page的事務(wù)都已經(jīng)提交,所以record上沒有隱式鎖,如果大于或等于它的話,就需要回主鍵找到對(duì)應(yīng)的主鍵record并遍歷undo歷史版本來確認(rèn)是否有隱式鎖,具體實(shí)現(xiàn)在row_vers_impl_x_locked_low中,

4. select 加鎖流程

select做當(dāng)前讀的加鎖流程就在row_search_mvcc當(dāng)中,一條select語句會(huì)多次進(jìn)入這個(gè)函數(shù),第一次是通過index_read->row_search_mvcc進(jìn)來,一般是首次訪問index,取找where里的exact record,之后每次再通過general_fetch->row_search_mvcc進(jìn)來,根據(jù)具體條件遍歷prev/next record,直到把滿足whrer條件的record都取出來。具體的加鎖也就是在訪問和遍歷record的過程中進(jìn)行,row_search_mvcc代碼很長(zhǎng),這里我只提煉總結(jié)下加鎖相關(guān)的流程:

  • 在index上查找search_tuple對(duì)應(yīng)的record。(這里的record可能是上面說的index_read進(jìn)來首次通過index btree查找search_tuple對(duì)應(yīng)的record,也有可能是之后多次general_fetch進(jìn)來通過之前保存的cursor來恢復(fù)出來的上一次訪問位置,然后拿到的prev/next record)
  • 如果是index_read 并且 mode是page_cur_l 或著page_cur_le,給定位到的record的next record加 gap lock
  • 如果record是infimum,跳轉(zhuǎn)步驟9 next_rec,如果是supremum,加next-key lock,跳轉(zhuǎn)步驟9 next_rec
  • 如果是index_read,record與search_tuple不相等,給recordgap lock,返回 not found
  • 到這里說明record與search_tuple相等,給record加next-key lock,兩個(gè)例外,只加rec lock:
  1. 對(duì)于index_read,如果當(dāng)前index是主鍵索引 并且 modepage_cur_ge 并且 search_tuple的fields個(gè)數(shù)等于index的unique fields個(gè)數(shù)
  2. 看是否是unique_search,即search_tuple的fields個(gè)數(shù)等于當(dāng)前index的unique fields個(gè)數(shù) 并且 當(dāng)前index是主鍵索引或者(是二級(jí)索引且search_tuple不包含null字段)并且 record不是deleted mark
  • 到這里說明加鎖成功了,然后處理record是deleted mark的情況:
  1. 當(dāng)前index是主鍵索引 并且 是unique_search,返回 not found
  2. 否則,跳轉(zhuǎn)步驟9 next_rec
  • 如果當(dāng)前index是二級(jí)索引 并且 需要回查主鍵索引,去主鍵索引里找對(duì)應(yīng)的primary record并加 rec lock,如果primary record是deleted mark,則當(dāng)前二級(jí)索引接著跳轉(zhuǎn)步驟9 next_rec
  • 成功,返回db_success
  • next_rec: 根據(jù)mode來取對(duì)應(yīng)的prev/next record,跳轉(zhuǎn) 步驟3 繼續(xù)

重點(diǎn)說一下步驟3,這里一般record是infimum或者supremum的情況都是多次genera_fetch對(duì)某個(gè)page取prev/next record之后走到page邊緣,對(duì)于infimum,不會(huì)加任何lock,直接繼續(xù)訪問前一個(gè)prev record(即prev page的supremum),對(duì)于supremum的話,會(huì)加上gap lock,它保護(hù)當(dāng)前page最后一個(gè)user record和next page第一個(gè)user record之間的gap。

其他的流程也就沒什么了:

  1. 對(duì)于遍歷到的滿足條件的record,基本默認(rèn)都是加next-key lock
  2. 二級(jí)索引回表時(shí)只會(huì)對(duì)主鍵加rec lock
  3. 對(duì)于某些特殊的場(chǎng)景,會(huì)將某些next-key lock降級(jí)成rec lock(步驟5)
  4. 還有一些特殊場(chǎng)景,會(huì)只加gap lock(步驟2、4)

總結(jié):

以上基本就是innodb加事務(wù)鎖的相關(guān)流程,insertselect的加鎖流程配合著看,事務(wù)鎖的原則及實(shí)現(xiàn)基本也就出來了。

到此這篇關(guān)于mysql innodb 事務(wù)鎖源碼分析的文章就介紹到這了,更多相關(guān)mysql innodb 事務(wù)鎖源碼分析內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:http://kernelmaker.github.io/MySQL_Lock?utm_source=tuicool&utm_medium=referral

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产婷婷精品av在线 | 欧美一区二区三区视频 | 欧美国产日韩一区 | 国产精品日韩欧美一区二区三区 | 亚洲精品影院 | 日本一区二区高清视频 | 精品国产欧美一区二区 | 午夜国产精品成人 | 亚洲精品综合 | 五月综合激情 | 国产精品久久久久久久久久久久午夜片 | 国产精品毛片久久久久久久 | 成人免费在线小视频 | 91在线精品一区二区 | 免费的av网站| 一道本一区二区三区 | av在线一区二区三区 | 日韩成人在线播放 | 国产精品久久久久久久久久久久 | 91av导航| 午夜影院 | 国产一级二级毛片 | 亚洲精品影院 | 亚洲免费精品 | 国产中文视频 | 亚洲骚片 | 国产中文字幕一区 | 国产一区二区三区四区五区密私 | 一区二区亚洲 | 97精品国产97久久久久久免费 | 精品国产乱码久久久久久牛牛 | 国产在线网站 | 日韩有码av| 国产黄色免费网站 | 国产精品videosex极品 | 真实国产露脸乱 | 日韩一区中文字幕 | 91av精品视频 | 99热首页| 精品一区二区免费视频视频 | 中文字幕亚洲精品 |