溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

mysql鎖的相關(guān)知識(shí)點(diǎn)介紹

發(fā)布時(shí)間:2021-08-26 16:37:16 來(lái)源:億速云 閱讀:141 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“mysql鎖的相關(guān)知識(shí)點(diǎn)介紹”,在日常操作中,相信很多人在mysql鎖的相關(guān)知識(shí)點(diǎn)介紹問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”mysql鎖的相關(guān)知識(shí)點(diǎn)介紹”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

一、mysql的鎖類型

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

共享鎖和排他鎖是InnoDB引擎實(shí)現(xiàn)的標(biāo)準(zhǔn)行級(jí)別鎖。

拿共享鎖是為了讓當(dāng)前事務(wù)去讀一行數(shù)據(jù)。

拿排他鎖是為了讓當(dāng)前事務(wù)去修改或刪除某一行數(shù)據(jù)。。

設(shè)置共享鎖:select * from user where id = 1 LOCK IN SHARE MODE;

設(shè)置排他鎖:select * from user where id = 1 FOR UPDATE;

(2) 意向鎖(Intention Locks)

意向鎖存在的意義在于,使得行鎖和表鎖能夠共存。

意向鎖是表級(jí)別的鎖,用來(lái)說(shuō)明事務(wù)稍后會(huì)對(duì)表中的數(shù)據(jù)行加哪種類型的鎖(共享鎖或獨(dú)占鎖)。

當(dāng)一個(gè)事務(wù)對(duì)表加了意向排他鎖時(shí),另外一個(gè)事務(wù)在加鎖前就會(huì)通過(guò)該表的意向排他鎖知道前面已經(jīng)有事務(wù)在對(duì)該表進(jìn)行獨(dú)占操作,從而等待。

(3) 記錄鎖(Record Locks)

記錄鎖是索引記錄上的鎖,例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;會(huì)阻止其他事務(wù)對(duì)c1=10的數(shù)據(jù)行進(jìn)行插入、更新、刪除等操作。

記錄鎖總是鎖定索引記錄。如果一個(gè)表沒(méi)有定義索引,那么就會(huì)去鎖定隱式的“聚集索引”。

(4) 間隙鎖(Gap Locks)

間隙鎖是一個(gè)在索引記錄之間的間隙上的鎖。

一個(gè)間隙可能跨越單個(gè)索引值、多個(gè)索引值,甚至為空。

對(duì)于使用唯一索引 來(lái)搜索唯一行的語(yǔ)句,只加記錄鎖不加間隙鎖(這并不包括組合唯一索引)。

(5) 臨鍵鎖(Next-key Locks)

Next-Key Locks是行鎖與間隙鎖的組合。當(dāng)InnoDB掃描索引記錄的時(shí)候,會(huì)首先對(duì)選中的索引記錄加上記錄鎖(Record Lock),然后再對(duì)索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。

(6) 插入意向鎖(Insert Intention Locks)

插入意向鎖是在數(shù)據(jù)行插入之前通過(guò)插入操作設(shè)置的間隙鎖定類型。

如果多個(gè)事務(wù)插入到相同的索引間隙中,如果它們不在間隙中的相同位置插入,則無(wú)需等待其他事務(wù)。例如:在4和7的索引間隙之間兩個(gè)事務(wù)分別插入5和6,則兩個(gè)事務(wù)不會(huì)發(fā)沖突阻塞。 

(7) 自增鎖(Auto-inc Locks)

自增鎖是事務(wù)插入到有自增列的表中而獲得的一種特殊的表級(jí)鎖。如果一個(gè)事務(wù)正在向表中插入值,那么任何其他事務(wù)都必須等待,保證第一個(gè)事務(wù)插入的行是連續(xù)的自增值。

二、鎖的實(shí)現(xiàn)方式

InnoDB行鎖是通過(guò)給索引加鎖來(lái)實(shí)現(xiàn)的,如果沒(méi)有索引,InnoDB會(huì)通過(guò)隱藏的聚簇索引來(lái)對(duì)記錄進(jìn)行加鎖(全表掃描,也就是表鎖)。

但是,為了效率考量,MySQL做了優(yōu)化,對(duì)于不滿足條件的記錄,會(huì)放鎖,最終持有的,是滿足條件的記錄上的鎖。但是不滿足條件的記錄上的加鎖/放鎖動(dòng)作是不會(huì)省略的。所以在沒(méi)有索引時(shí),不滿足條件的數(shù)據(jù)行會(huì)有加鎖又放鎖的耗時(shí)過(guò)程。

索引分為主鍵索引和非主鍵索引兩種。如果一條sql語(yǔ)句操作了主鍵索引,MySQL就會(huì)鎖定對(duì)應(yīng)主鍵索引;如果一條語(yǔ)句操作了非主鍵索引,MySQL會(huì)先鎖定非主鍵索引,再鎖定對(duì)應(yīng)的主鍵索引。

三、mysql鎖在4種事務(wù)隔離級(jí)別里的應(yīng)用

事務(wù)的四種隔離級(jí)別有:

  •  讀未提交(Read Uncommitted)

此時(shí)select語(yǔ)句不加任何鎖。此時(shí)并發(fā)最高,但會(huì)產(chǎn)生臟讀。

  •  讀提交(Read Committed, RC)

普通select語(yǔ)句是快照讀

update語(yǔ)句、delete語(yǔ)句、顯示加鎖的select語(yǔ)句(select … in share mode 或者 select … for update) 等,除了在外鍵約束檢查和重復(fù)鍵檢查時(shí)會(huì)封鎖區(qū)間,其他情況都只使用記錄鎖;

  •  可重復(fù)讀(Repeated Read, RR)

普通select語(yǔ)句也是快照讀

update語(yǔ)句、delete語(yǔ)句、顯示加鎖的select語(yǔ)句(select … in share mode 或者 select … for update)則要分情況:

在唯一索引上使用唯一的查詢條件,則使用記錄鎖。如: select * from user where id = 1;其中id建立了唯一索引。

在唯一索引上使用 范圍查詢條件,則使用間隙鎖與臨鍵鎖。如: select * from user where id >20;

  •  串行化(Serializable)

此時(shí)所有select語(yǔ)句都會(huì)被隱式加鎖:select … in share mode.

四、快照讀、當(dāng)前讀

要理解前面四種隔離級(jí)別的加鎖方式,對(duì)于MVCC、快照讀、當(dāng)前讀 都是必須要理解的。

MVCC并發(fā)控制中,讀操作可以分成兩類:快照讀 (snapshot read)與當(dāng)前讀 (current read)。

快照讀,讀取的是記錄的可見(jiàn)版本 (有可能是歷史版本),不用加鎖。

當(dāng)前讀,讀取的是記錄的最新版本,并且,當(dāng)前讀返回的記錄,都會(huì)加上鎖,保證其他事務(wù)不會(huì)再并發(fā)修改這條記錄。

什么是多版本并發(fā)控制(MVCC:multi-version concurrency control )

1.MVCC定義:多版并發(fā)控制系統(tǒng)??烧J(rèn)為是行級(jí)鎖的一個(gè)變種,它能夠避免更多情況下的加鎖操作。

2.作用:避免一些加鎖操作,提升并發(fā)性能。

3.實(shí)現(xiàn):通過(guò)在每行記錄的后面保存行的創(chuàng)建時(shí)間和過(guò)期時(shí)間或刪除時(shí)間(它們是隱藏的),這兩個(gè)時(shí)間實(shí)際都是系統(tǒng)的版本號(hào)。每開(kāi)始一個(gè)新的事務(wù),版本號(hào)都會(huì)自動(dòng)增加。

4.具體原理

4.1) select:innoBD查詢時(shí)會(huì)檢查以下兩個(gè)條件:一個(gè)是數(shù)據(jù)行的版本號(hào)早于當(dāng)前事務(wù)的版本號(hào);另一個(gè)是行的刪除版本號(hào),要么沒(méi)有,要么大于當(dāng)前事務(wù)的版本號(hào)。

4.2)insert/delete:innoDB將當(dāng)前的系統(tǒng)版本號(hào)作為新插入(刪除)的數(shù)據(jù)行的版本號(hào)。

4.3)update:先新插入一行數(shù)據(jù),并將當(dāng)前系統(tǒng)版本號(hào)作為行的版本號(hào),同時(shí)將當(dāng)前系統(tǒng)版本號(hào)作為原來(lái)行的刪除版本號(hào)。更新主鍵時(shí),聚集索引和普通索引都會(huì)產(chǎn)生兩個(gè)版本;而更新非主鍵時(shí),只要普通索引會(huì)產(chǎn)生兩個(gè)版本。

注意:MVCC只在read committed和repeatable read兩個(gè)隔離級(jí)別下工作。

[參考:《高性能mysql》]

快 照 讀 是 哪 些

一個(gè)正常的select…語(yǔ)句就是快照讀。

快照讀,使得在RR(repeatable read)級(jí)別下一個(gè)普通select...語(yǔ)句也能做到可重復(fù)讀。即前面MVCC里提到的利用可見(jiàn)版本來(lái)保證數(shù)據(jù)的一致性。

當(dāng) 前 讀 是 哪 些

insert語(yǔ)句、update語(yǔ)句、delete語(yǔ)句、顯示加鎖的select語(yǔ)句(select… LOCK IN SHARE MODE、select… FOR UPDATE)是當(dāng)前讀。

為什么insert、update、delete語(yǔ)句都屬于當(dāng)前讀?

這是因?yàn)檫@些語(yǔ)句在執(zhí)行時(shí),都會(huì)執(zhí)行一個(gè)讀取當(dāng)前數(shù)據(jù)最新版本的過(guò)程。

當(dāng)前讀的SQL語(yǔ)句,InnoDB是逐條與MySQL Server交互的。即先對(duì)一條滿足條件的記錄加鎖后,再返回給MySQL Server,當(dāng)MySQL Server做完DML操作后,再對(duì)下一條數(shù)據(jù)加鎖并處理。

五、查看行級(jí)鎖爭(zhēng)用情況

執(zhí)行SQL:mysql> show status like 'InnoDB_row_lock%';


mysql> show status like 'InnoDB_row_lock%';+-------------------------------+-------+| Variable_name                 | Value |+-------------------------------+-------+| InnoDB_row_lock_current_waits | 0     || InnoDB_row_lock_time          | 0     || InnoDB_row_lock_time_avg      | 0     || InnoDB_row_lock_time_max      | 0     || InnoDB_row_lock_waits         | 0     |+-------------------------------+-------+

如果發(fā)現(xiàn)鎖爭(zhēng)用比較嚴(yán)重,還可以通過(guò)設(shè)置InnoDB Monitors 來(lái)進(jìn)一步觀察發(fā)生鎖沖突的表、數(shù)據(jù)行等,并分析鎖爭(zhēng)用的原因。如:

設(shè)置監(jiān)視器:mysql> create table InnoDB_monitor(a INT) engine=InnoDB;

查看:mysql> show engine InnoDB status;

停止查看:mysql> drop table InnoDB_monitor;

具體參考:InnoDB Monitor

六、死鎖

什么是死鎖:你等我釋放鎖,我等你釋放鎖就會(huì)形成死鎖。

如何發(fā)現(xiàn)死鎖:在InnoDB的事務(wù)管理和鎖定機(jī)制中,有專門(mén)檢測(cè)死鎖的機(jī)制,會(huì)在系統(tǒng)中產(chǎn)生死鎖之后的很短時(shí)間內(nèi)就檢測(cè)到該死鎖的存在

解決辦法:回滾較小的那個(gè)事務(wù)

在REPEATABLE-READ隔離級(jí)別下,如果兩個(gè)線程同時(shí)對(duì)相同條件記錄用SELECT…FOR UPDATE加排他鎖,在沒(méi)有符合該條件記錄情況下,兩個(gè)線程都會(huì)加鎖成功。程序發(fā)現(xiàn)記錄尚不存在,就試圖插入一條新記錄,如果兩個(gè)線程都這么做,就會(huì)出現(xiàn)死鎖。這種情況下,將隔離級(jí)別改成READ COMMITTED,就可避免問(wèn)題。

如何判斷事務(wù)大?。菏聞?wù)各自插入、更新或者刪除的數(shù)據(jù)量

注意:

當(dāng)產(chǎn)生死鎖的場(chǎng)景中涉及到不止InnoDB存儲(chǔ)引擎的時(shí)候,InnoDB是沒(méi)辦法檢測(cè)到該死鎖的,這時(shí)候就只能通過(guò)鎖定超時(shí)限制參數(shù)InnoDB_lock_wait_timeout來(lái)解決。

七、優(yōu)化行級(jí)鎖定

(1)要想合理利用InnoDB的行級(jí)鎖定,做到揚(yáng)長(zhǎng)避短,我們必須做好以下工作: 

a)盡可能讓所有的數(shù)據(jù)檢索都通過(guò)索引來(lái)完成,從而避免InnoDB因?yàn)闊o(wú)法通過(guò)索引鍵加鎖而升級(jí)為表級(jí)鎖定; 

b)合理設(shè)計(jì)索引,讓InnoDB在索引鍵上面加鎖的時(shí)候盡可能準(zhǔn)確,盡可能的縮小鎖定范圍,避免造成不必要的鎖定而影響其他Query的執(zhí)行; 

c)盡可能減少基于范圍的數(shù)據(jù)檢索過(guò)濾條件,避免因?yàn)殚g隙鎖帶來(lái)的負(fù)面影響而鎖定了不該鎖定的記錄; 

d)盡量控制事務(wù)的大小,減少鎖定的資源量和鎖定時(shí)間長(zhǎng)度; 

e)在業(yè)務(wù)環(huán)境允許的情況下,盡量使用較低級(jí)別的事務(wù)隔離,以減少M(fèi)ySQL因?yàn)閷?shí)現(xiàn)事務(wù)隔離級(jí)別所帶來(lái)的附加成本。

(2)由于InnoDB的行級(jí)鎖定和事務(wù)性,所以肯定會(huì)產(chǎn)生死鎖,下面是一些比較常用的減少死鎖產(chǎn)生概率的小建議:

a)類似業(yè)務(wù)模塊中,盡可能按照相同的訪問(wèn)順序來(lái)訪問(wèn),防止產(chǎn)生死鎖; 

b)在同一個(gè)事務(wù)中,盡可能做到一次鎖定所需要的所有資源,減少死鎖產(chǎn)生概率; 

c)對(duì)于非常容易產(chǎn)生死鎖的業(yè)務(wù)部分,可以嘗試使用升級(jí)鎖定顆粒度,通過(guò)表級(jí)鎖定來(lái)減少死鎖產(chǎn)生的概率。

查看SQL語(yǔ)句的鎖信息

查看sql句的鎖信息前,需要做如下幾件事:

查看事務(wù)的隔離級(jí)別:

通過(guò)show global variables like “tx_isolation”; 命令查看。

可通過(guò)執(zhí)行set session transaction isolation level repeatable read;更改成我們想要隔離級(jí)別,隔離級(jí)別取值如下:

read uncommitted、read committed、repeatable read、serializable

保證事務(wù)為手動(dòng)提交:

通過(guò)show global variables like “autocommit”;查看。

如果為ON,則通過(guò)執(zhí)行set session autocommit=0;改為手動(dòng)提交。

保證間隙鎖開(kāi)啟:

通過(guò)show global variables like “innodb_locks%”;查看

OFF時(shí)表示開(kāi)啟。默認(rèn)是OFF

到此,關(guān)于“mysql鎖的相關(guān)知識(shí)點(diǎn)介紹”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI