mysql lock_MySQL-锁总结
鎖
鎖機(jī)制用于管理對共享資源的并發(fā)訪問。
lock和latch
在數(shù)據(jù)庫中,lock和Latch都稱為鎖,但是兩者意義不同。
latch稱為閂鎖(shuang suo),其要求鎖定的時(shí)間必須非常短。若持續(xù)的時(shí)間長,則應(yīng)用的性能會非常差。在InnoDB存儲引擎中,latch又分為mutex互斥鎖 和 rwLock讀寫鎖。其目的是為了保證并發(fā)線程操作臨界資源的正確性。通常沒有死鎖的檢測機(jī)制。
lock的對象是事務(wù),用來鎖定的是數(shù)據(jù)庫中的對象,如表、頁、行。并且一般lock的對象僅在事務(wù)commit或者rollback后進(jìn)行釋放。有死鎖檢測機(jī)制。
通過show engine innodb mutex可以查看InnoDB存儲引擎的中l(wèi)atch,具體字段詳情如下表:
鎖的類型
有幾個(gè)索引,需要分別向索引加鎖。
共享鎖、排他鎖
InnoDB存儲引擎實(shí)現(xiàn)了如下兩種標(biāo)準(zhǔn)的行級鎖:
共享鎖(S Lock):允許事務(wù)讀一行數(shù)據(jù)
排他鎖(X Lock):允許事務(wù)刪除 或 更新一行數(shù)據(jù)
如果一個(gè)事務(wù)T1已經(jīng)獲取了行r的共享鎖,那么另外的事務(wù)T2可以立即獲得行r的共享鎖。因?yàn)樽x取并不會改變行的數(shù)據(jù),所以可以多個(gè)事務(wù)同時(shí)獲取共享鎖,稱這種情況為鎖兼容。但若有其他的事務(wù)T3想獲得行R的排他鎖,則其必須等待事務(wù)T1、T2釋放行r上面的共享鎖,稱這種情況為鎖不兼容。下面顯示了共享鎖和排他鎖的兼容性:
從表6-3可以看出X鎖與任何鎖都不兼容,而S鎖僅和S鎖兼容。S鎖和X鎖都是行鎖,兼容是指對同一行記錄鎖的兼容情況。
普通 select 語句默認(rèn)不加鎖,而CUD操作默認(rèn)加排他鎖。
記錄鎖
Record Lock,僅鎖定一行記錄(如共享鎖、排他鎖)
記錄鎖總是會去鎖定索引記錄,如果表在建立的時(shí)候,沒有設(shè)置任何一個(gè)索引,那么InnoDB會使用隱式的主鍵來進(jìn)行鎖定。
查詢條件的列是唯一索引的情況下,臨建鎖退化為記錄鎖
間隙鎖
Gap Lock,鎖定一個(gè)范圍,但不包含記錄本身。
關(guān)閉間隙鎖的2種方式:
(1)將事務(wù)隔離級別變?yōu)閞ead committed
(2)將參數(shù)innodb_locks_unsafe_for_binlog設(shè)置為1
在上述配置下,除了外鍵和唯一性檢查依然需要間隙鎖,其余情況僅適用行鎖進(jìn)行鎖定。
臨鍵鎖
Next-Key Lock,等于記錄鎖 + 臨鍵鎖,鎖定一個(gè)范圍,并且鎖定記錄本身。主要是阻止多個(gè)事務(wù)將記錄插入到同一個(gè)范圍內(nèi),從而避免幻讀。
假如一個(gè)索引有10、11、13、20這四個(gè)值,那么該索引可能被鎖定的區(qū)間為:
若事務(wù)T1已經(jīng)通過臨鍵鎖鎖定了如下范圍:
當(dāng)插入新的記錄12時(shí),則鎖定的范圍變成:
當(dāng)查詢的索引是唯一索引的時(shí)候,InnoDB會將臨鍵鎖優(yōu)化成記錄鎖,從而提高并發(fā)。這時(shí)候,將不再由間隙鎖避免幻讀的問題,但是試驗(yàn)了下,即使優(yōu)化成記錄鎖,也不會有幻讀的問題,其實(shí)是因?yàn)镸VCC,在可重復(fù)讀的情況下,SELECT操作只會查找行版本號小于當(dāng)前事務(wù)版本號的記錄,其他事務(wù)(事務(wù)開啟時(shí)間比當(dāng)前事務(wù)晚)新插入的記錄版本號不滿足條件,就不會查出來。
對于輔助索引,當(dāng)執(zhí)行類似select * from z where b = 3 for update;加鎖語句時(shí),會加上臨鍵鎖,并且下一個(gè)鍵值的范圍也會加上間隙鎖。
值得注意的是,對于唯一鍵值的鎖定,由臨鍵鎖優(yōu)化為記錄鎖,僅存在于查詢所有的唯一索引。若唯一索引由多列組成,而查詢僅是查找多個(gè)唯一索引中的一個(gè),那么查詢其實(shí)是range類型查詢,而不是point類型查詢,故InnoDB存儲引擎還是繼續(xù)使用臨鍵鎖。
在InnoDB存儲引擎中,通過使用臨鍵鎖來避免不可重復(fù)讀的問題(即幻讀)。在使用臨鍵鎖的情況下,對于索引的掃描,不僅僅鎖住掃描的到索引,而且還鎖住這些索引覆蓋的范圍。因此,在這些范圍內(nèi)插入都是不允許的。這樣子就避免了其他事務(wù)在這些范圍內(nèi)插入數(shù)據(jù)導(dǎo)致不可重復(fù)讀的問題。
意向鎖
概念:未來的某個(gè)時(shí)刻,事務(wù)可能要加共享/排它鎖了,先提前聲明一個(gè)意向
意向鎖有這樣一些特點(diǎn):
(1)意向鎖是表級別的鎖
(2)意向鎖分為:
意向共享鎖(intention shared lock, IS),它預(yù)示著,事務(wù)有意向?qū)Ρ碇械哪承┬屑庸蚕鞸鎖
意向排它鎖(intention exclusive lock, IX),它預(yù)示著,事務(wù)有意向?qū)Ρ碇械哪承┬屑优潘黊鎖
(3)意向鎖協(xié)議:
事務(wù)要獲得某些行的共享鎖,必須先獲得表的意向共享鎖IS
事務(wù)要獲取某些行的排他鎖,必須先獲得表的意向排他鎖IX
(4)由于意向鎖僅僅表明意向,它其實(shí)是比較弱的鎖,意向鎖之間并不相互互斥,而是可以并行,其兼容互斥表如下:
? IS IX
IS 兼容 兼容
IX 兼容 兼容
(5)既然意向鎖之間都相互兼容,那其意義在哪里呢?它會與共享鎖/排它鎖互斥,其兼容互斥表如下:
? S X
IS 兼容 互斥
IX 互斥 互斥
(排它鎖是很強(qiáng)的鎖,不與其他類型的鎖兼容。這也很好理解,修改和刪除某一行的時(shí)候,必須獲得強(qiáng)鎖,禁止這一行上的其他并發(fā),以保障數(shù)據(jù)的一致性。)
InnoDB支持多粒度鎖定,這種鎖定允許事務(wù)在行級上的鎖和表級上的鎖同時(shí)存在。為了支持不同粒度上進(jìn)行加鎖操作,InnoDB存儲引擎支持一種額外的鎖方式,稱之為意向鎖。意向鎖是將鎖定的對象分為多個(gè)層次,意向鎖意味著事務(wù)希望在更細(xì)的粒度上進(jìn)行加鎖。如圖6-3所示:
若將上鎖的對象看成一棵樹,那么對最下層的對象上鎖,也就是對最細(xì)粒度的對象上鎖,那么首先需要對粗粒度的對象進(jìn)行上鎖。如上圖,如果需要對頁上的記錄上X鎖,那么需要分別對數(shù)據(jù)庫A、表、頁 上意向鎖IX,最后對記錄r上排他鎖X。
若其中任何一部分導(dǎo)致等待,那么該操作需要等待粗粒度鎖的完成。舉例來說,事務(wù)T1在對記錄r加X鎖之前,已有事務(wù)T2對表1進(jìn)行了S表鎖,那么表1上面已經(jīng)存在S鎖,之后事務(wù)T1試圖在表1上加IX鎖(獲取記錄r的X鎖必須先獲取表1的IX鎖),由于不兼容,所以事務(wù)T1需要等待事務(wù)T2釋放表鎖。
插入意向鎖
對已有數(shù)據(jù)行的修改與刪除,必須加強(qiáng)互斥鎖X鎖,那對于數(shù)據(jù)的插入,是否還需要加這么強(qiáng)的鎖,來實(shí)施互斥呢?插入意向鎖,孕育而生。
插入意向鎖,是間隙鎖(Gap Locks)的一種(所以,也是實(shí)施在索引上的),它是專門針對insert操作的。
它的用處是:多個(gè)事務(wù),在同一個(gè)索引,同一個(gè)范圍區(qū)間插入記錄時(shí),如果插入的位置不沖突,不會阻塞彼此。
示例
在MySQL,InnoDB,RR下:
t(id unique PK, name);
數(shù)據(jù)表中有數(shù)據(jù):
10, shenjian
20, zhangsan
30, lisi
事務(wù)A先執(zhí)行,在10與20兩條記錄中插入了一行,還未提交:
insert into t values(11, xxx);
事務(wù)B后執(zhí)行,也在10與20兩條記錄中插入了一行:
insert into t values(12, ooo);
(1)會使用什么鎖?
(2)事務(wù)B會不會被阻塞呢?
回答:雖然事務(wù)隔離級別是RR,雖然是同一個(gè)索引,雖然是同一個(gè)區(qū)間,但插入的記錄并不沖突,故這里:
使用的是插入意向鎖
并不會阻塞事務(wù)B
自增鎖
自增鎖是MySQL一種特殊的鎖,如果表中存在自增字段,MySQL便會自動維護(hù)一個(gè)自增鎖。
在InnoDB存儲引擎的內(nèi)存結(jié)構(gòu)中,對每個(gè)含有自增長值的表都有一個(gè)自增長計(jì)數(shù)器。當(dāng)對含有自增長計(jì)數(shù)器的表進(jìn)行插入操作時(shí),這個(gè)這個(gè)計(jì)數(shù)器會被初始化,執(zhí)行如下操作來得到計(jì)數(shù)器的值:
select max(auto_inc_col) from t for update
插入操作會依據(jù)這個(gè)自增長的計(jì)數(shù)器值加1賦予自增長列。這個(gè)實(shí)現(xiàn)方式成為Auto-Inc Locking。這種鎖其實(shí)是采用一種表鎖的機(jī)制,為了提高插入的性能,鎖不是在一個(gè)事務(wù)完成以后才釋放,而是在完成對自增長值插入的SQL語句后立即釋放。
雖然Auto-Inc Locking從一定程度上提高了并發(fā)插入的效率,但還是存在一些性能上的問題。對于有自增長值的列的并發(fā)插入性能較差,事務(wù)必須等待前一個(gè)插入的完成(雖然不用等待事務(wù)的完成)。
從MySQL5.12版本開始,InnoDB存儲引擎提供了一種輕量級互斥量的自增長實(shí)現(xiàn)方式。這種方式大大提高了自增長值插入的性能。并且從該版本開始,InnoDB存儲引起提供了一個(gè)參數(shù)innodb_innodb_autoinc_lock_mode來控制自增長模式,該參數(shù)的默認(rèn)值為1。首先看下自增長的插入分類,如下圖:
下圖展示了innodb_innodb_autoinc_lock_mode的不同值對自增的影響:(值為1、2的時(shí)候,看不懂。。)
InnoDB存儲引擎中自增長的實(shí)現(xiàn)和MyISAM不同。MyISAM存儲引擎是表鎖設(shè)計(jì),自增長不用考慮并發(fā)插入的問題。在InnoDB存儲引擎中,自增長值的列必須是索引,同時(shí)必須是索引的第一個(gè)列,如果不是第一個(gè)列,則MySQL會拋出異常。MyISAM存儲引擎沒有這個(gè)問題。
外鍵與鎖
如果沒有為外鍵顯示添加索引,InnoDB自動為外鍵創(chuàng)建索引,這樣子避免表鎖。
對于外鍵值的插入或更新,首先需要查詢父表中的記錄,即select父表。但是不是使用一致性非鎖定讀,因?yàn)檫@樣子會發(fā)生數(shù)據(jù)不一致的問題。因此這時(shí)使用的是select…lock in share mode,即主動對父表加一個(gè)共享鎖。如果這時(shí)父表已經(jīng)加了X鎖,子表上面的操作將會被阻塞,如下圖:
MVCC多版本
又稱為一致性非鎖定讀。指InnoDB通過行多版本控制的方式來讀取當(dāng)前執(zhí)行時(shí)間數(shù)據(jù)庫中行的數(shù)據(jù)。如果讀取的行正在執(zhí)行delete或者update操作,這時(shí)讀操作不會因此去等待行上鎖的釋放。相反的,InnoDB存儲引擎會去讀取行的一個(gè)快照數(shù)據(jù)。
在默認(rèn)配置下,即事務(wù)的隔離界別為REPEATABLE READ(可重復(fù)讀)模式下,InnoDB存儲引擎的SELECT操作使用一致性非鎖定讀。
快照數(shù)據(jù)是指該行的之前版本的數(shù)據(jù),該實(shí)現(xiàn)是通過undo段來完成。而undo用來在事務(wù)中回滾數(shù)據(jù),因此快照數(shù)據(jù)本身是沒有額外的開銷。此外讀取快照數(shù)據(jù)是不需要上鎖的,因?yàn)闆]有事務(wù)需要對歷史的數(shù)據(jù)進(jìn)行修改操作。
非鎖定度機(jī)制極大的提高了數(shù)據(jù)庫的并發(fā)性。這是InnoDB默認(rèn)的讀取方式,即讀取不會占用表上的鎖。但是在不同事務(wù)隔離界別下,讀取的方式不同,并不是在每個(gè)事務(wù)隔離界別下都是采用非鎖定的一致性讀。此外,即使都是使用非鎖定的一致性讀,但是對于快照數(shù)據(jù)的定義也是各不相同。
快照數(shù)據(jù)其實(shí)就是當(dāng)前行數(shù)據(jù)之前的歷史版本,每行記錄可能有多個(gè)版本。一個(gè)行記錄可能有不止一個(gè)快照數(shù)據(jù),一般稱這種技術(shù)為行多版本技術(shù),由此帶來的并發(fā)控制,稱之為多版本并發(fā)控制 MVCC。
在事務(wù)隔離界別read committed 和 repeatable read(InnoDB默認(rèn)的事務(wù)隔離界別)下,InnoDB使用非鎖定一致性讀。然而,對于快照數(shù)據(jù)的定義卻不相同。對于快照數(shù)據(jù),非一致性讀總是讀取被鎖定行的最新一份快照數(shù)據(jù)(如果沒有被鎖定,則讀取行的最新數(shù)據(jù);如果行鎖定了,則讀取該行的最新一個(gè)快照)。而在repeatable read事務(wù)隔離級別下,對于快照數(shù)據(jù),非一致性讀總是讀取事務(wù)開始時(shí)的行數(shù)據(jù)版本。
MVCC的優(yōu)缺點(diǎn)
MVCC在大多數(shù)情況下代替了行鎖,實(shí)現(xiàn)了對讀的非阻塞,讀不加鎖,讀寫不沖突。缺點(diǎn)是每行記錄都需要額外的存儲空間,需要做更多的行維護(hù)和檢查工作。注意寫寫不能并行。
MVCC的實(shí)現(xiàn)原理
undo log
undo log是為回滾而用,具體內(nèi)容就是copy事務(wù)前的數(shù)據(jù)庫內(nèi)容(行)到undo buffer,在適合的時(shí)間把undo buffer中的內(nèi)容刷新到磁盤。undo buffer與redo buffer一樣,也是環(huán)形緩沖,但當(dāng)緩沖滿的時(shí)候,undo buffer中的內(nèi)容會也會被刷新到磁盤;與redo log不同的是,磁盤上不存在單獨(dú)的undo log文件,所有的undo log均存放在主ibd數(shù)據(jù)文件中(表空間),即使客戶端設(shè)置了每表一個(gè)數(shù)據(jù)文件也是如此。在Innodb中,undo log被劃分為多個(gè)段,具體某行的undo log就保存在某個(gè)段中,稱為回滾段。可以認(rèn)為undo log和回滾段是同一意思。
為了便于理解MVCC的實(shí)現(xiàn)原理,這里簡單介紹一下undo log的工作過程
在不考慮redo log 的情況下利用undo log工作的簡化過程為:
序號 動作
1 開始事務(wù)
2 記錄數(shù)據(jù)行數(shù)據(jù)備份到undo log
3 更新數(shù)據(jù)
4 將undo log寫到磁盤
5 將數(shù)據(jù)寫到磁盤
6 提交事務(wù)
(1)為了保證數(shù)據(jù)的持久性,數(shù)據(jù)要在事務(wù)提交之前持久化
(2)undo log的持久化必須在在數(shù)據(jù)持久化之前,這樣才能保證系統(tǒng)崩潰時(shí),可以用undo log來回滾事務(wù)
(3)Innodb通過undo log保存了已更改行的舊版本的快照。
redo log
redo log就是保存執(zhí)行的SQL語句到一個(gè)指定的Log文件,當(dāng)MySQL執(zhí)行recovery(修復(fù))時(shí)重新執(zhí)行redo log記錄的SQL操作即可。當(dāng)客戶端執(zhí)行每條SQL(更新語句)時(shí),redo log會被首先寫入log buffer;當(dāng)客戶端執(zhí)行COMMIT命令時(shí),log buffer中的內(nèi)容會被視情況刷新到磁盤。redo log在磁盤上作為一個(gè)獨(dú)立的文件存在,即Innodb的log文件。
Innodb中的隱藏列
InnoDB的內(nèi)部實(shí)現(xiàn)中為每一行數(shù)據(jù)增加了三個(gè)隱藏列用于實(shí)現(xiàn)MVCC。
列名
長度(字節(jié))
作用
DB_TRX_ID
6
插入或更新行的最后一個(gè)事務(wù)的事務(wù)標(biāo)識符。(刪除視為更新,將其標(biāo)記為已刪除)
DB_ROLL_PTR
7
寫入回滾段的撤消日志記錄(若行已更新,則撤消日志記錄包含在更新行之前重建行內(nèi)容所需的信息)
DB_ROW_ID
6
行標(biāo)識(隱藏單調(diào)自增id)
一行記錄的結(jié)構(gòu)如下:
數(shù)據(jù)列
..
DB_ROW_ID
DB_TRX_ID
DB_ROLL_PTR
MVCC工作過程
MVCC只在READ COMMITED 和 REPEATABLE READ 兩個(gè)隔離級別下工作。READ UNCOMMITTED總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行。而SERIALIZABLE 則會對所有讀取的行都加鎖。另外事務(wù)的版本號是遞增的。
SELECT
InnoDB 會根據(jù)兩個(gè)條件來檢查每行記錄:
InnoDB只查找版本(DB_TRX_ID)早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(行的系統(tǒng)版本號<=事務(wù)的系統(tǒng)版本號,這樣可以確保數(shù)據(jù)行要么是在開始之前已經(jīng)存在了,要么是事務(wù)自身插入或修改過的)
行的刪除版本號(DB_ROLL_PTR)要么未定義(未更新過),要么大于當(dāng)前事務(wù)版本號(在當(dāng)前事務(wù)開始之后更新的)。這樣可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除。
INSERT
InnoDB為新插入的每一行保存當(dāng)前系統(tǒng)版本號作為行版本號
DELETE
InnoDB為刪除的每一行保存當(dāng)前的系統(tǒng)版本號作為行刪除標(biāo)識
UPDATE
innodb為插入一行新紀(jì)錄,保存當(dāng)前系統(tǒng)版本號作為行版本號,同時(shí)保存當(dāng)前系統(tǒng)版本號到原來的行作為行刪除標(biāo)示。
MVCC插入示例
F1~F6是字段名稱,1~6是對應(yīng)的數(shù)據(jù)。后面3個(gè)隱藏字段分別對應(yīng)行ID、事務(wù)ID、回滾指針。
初始狀態(tài)
假如有一條新增的數(shù)據(jù),可以認(rèn)為行ID為1,其他兩個(gè)字段為空。
事務(wù)1更改該行的值
當(dāng)事務(wù)1更改該行的值時(shí),會進(jìn)行如下操作:
用排他鎖鎖定該行
記錄redo log
把該行修改前的值復(fù)制到undo log,即上圖中下面的行
修改當(dāng)前的行的值,填寫事務(wù)編號,使回滾指針指向undo log中修改的行
釋放鎖
事務(wù)2更改該行的值
與事務(wù)1相同,此時(shí)undo log中有2條記錄,并且通過回滾指針連在一起。
因此,如果undo log一直不刪除,則可以通過當(dāng)前記錄的回滾指針回溯到該行創(chuàng)建時(shí)的初始內(nèi)容,所幸的是在InnoDB中存在清理線程,它會查詢比現(xiàn)在最老的事務(wù)還早的undo log,并刪除它們,從而保證undo log文件不會無限增長。
事務(wù)提交
當(dāng)事務(wù)正常提交時(shí),InnoDB只需要更改事務(wù)狀態(tài)為COMMIT即可,不需要做其他額外的工作,而回滾則復(fù)雜一點(diǎn),需要根據(jù)回滾指針找出事務(wù)修改前的版本,并且恢復(fù)。如果事務(wù)影響的行非常多,回滾則可能變得效率不高。
一致性非鎖定讀(見共享鎖、排他鎖)
在某些情況下,用戶需要顯式的對數(shù)據(jù)庫讀取操作進(jìn)行加鎖以保證數(shù)據(jù)邏輯的一致性。而這要求數(shù)據(jù)庫支持加鎖語句。InnoDB存儲引擎對于SELECT語句支持兩種一致性的鎖定度操作:
select … for update
共享鎖(S鎖, share locks)。其他事務(wù)可以讀取數(shù)據(jù),但不能對該數(shù)據(jù)進(jìn)行修改,直到所有的共享鎖被釋放。
? 如果事務(wù)對某行數(shù)據(jù)加上共享鎖之后,可進(jìn)行讀寫操作;其他事務(wù)可以對該數(shù)據(jù)加共享鎖,但不能加排他鎖,且只能讀數(shù)據(jù),不能修改數(shù)據(jù)。
select … lock in share mode
? 如果事務(wù)對數(shù)據(jù)加上排他鎖之后,則其他事務(wù)不能對該數(shù)據(jù)加任何的鎖。獲取排他鎖的事務(wù)既能讀取數(shù)據(jù),也能修改數(shù)據(jù)。
select…for update對讀取的行記錄加一個(gè)X鎖,其他事務(wù)不能對已鎖定的行加上任何鎖。select…lock in share mode對讀取的行記錄加一個(gè)S鎖,其他事務(wù)可以向被鎖定的行加S鎖,但是如果加X鎖,則會被阻塞。
對于一致性非鎖定讀,即時(shí)讀取的行已經(jīng)被執(zhí)行了select..for update,也是可以進(jìn)行讀取的。
如果不加篩選條件(或者篩選條件不走索引),會升級為表鎖
索引數(shù)據(jù)重復(fù)率太高會導(dǎo)致全表掃描:當(dāng)表中索引字段數(shù)據(jù)重復(fù)率太高,則MySQL可能會忽略索引,進(jìn)行全表掃描,此時(shí)使用表鎖。可使用 force index 強(qiáng)制使用索引。
鎖問題
臟讀
臟數(shù)據(jù):指的是事務(wù)對緩沖池中行記錄的修改,并且還沒有提交。即事務(wù)未提交的數(shù)據(jù)。
臟讀:指當(dāng)前事務(wù)可以讀到其他事務(wù)的未提交的數(shù)據(jù)。如果讀到了臟數(shù)據(jù),即一個(gè)事務(wù)可以讀到另外一個(gè)事務(wù)中未提交的數(shù)據(jù),顯然違反了事務(wù)的隔離性。
臟讀的條件:需要事務(wù)的隔離級別為讀未提交。
示例:
不可重復(fù)讀
不可重復(fù)讀:指在在一個(gè)事務(wù)內(nèi)多次讀取同一個(gè)數(shù)據(jù)集合,在這個(gè)事務(wù)還沒有結(jié)束時(shí),另外一個(gè)事務(wù)也訪問了同一個(gè)數(shù)據(jù)集合,并且做了一些DML操作。因此,在第一個(gè)事務(wù)的兩次讀數(shù)據(jù)之間,由于第二個(gè)事務(wù)的修改,第一個(gè)事務(wù)兩次讀取到的數(shù)據(jù)可能是不一樣的(具體看隔離級別)。這種稱為不可重復(fù)讀。
示例:
丟失更新
丟失更新:指一個(gè)事務(wù)的更新操作被另外一個(gè)事務(wù)的更新操作所覆蓋,從而導(dǎo)致數(shù)據(jù)的不一致。
丟失更新的實(shí)例:
解決辦法:對用戶讀取的記錄加上一個(gè)排他鎖,這樣子其他事務(wù)就必須等待前一個(gè)事務(wù)的完成。從而避免并發(fā)問題。
解決辦法的示例:
阻塞
阻塞:事務(wù)因?yàn)榈却渌聞?wù)釋放鎖而等待
超時(shí):等待其他事務(wù)釋放鎖,超過超時(shí)時(shí)間,就認(rèn)為是超時(shí)。
innodb_lock_wait_timeout:用來控制超時(shí)時(shí)間,默認(rèn)是50秒。可以在MYSQL運(yùn)行時(shí)進(jìn)行設(shè)置。
innodb_rollback_on_timeout:用來設(shè)定是否在等待超時(shí)時(shí)對進(jìn)行中的事務(wù)進(jìn)行回滾操作。默認(rèn)是OFF,不回滾。不可以在MySQL啟動時(shí)進(jìn)行修改。用戶在超時(shí)的情況下,必須判斷是是否需要commit或者rollback,之后再進(jìn)行下一步的操作。
死鎖
概念:死鎖是指兩個(gè)或者兩個(gè)以上的事務(wù),因爭奪資源而造成的一種互相等待的現(xiàn)象。若無外力作用,所有事務(wù)都將無法推進(jìn)下去。
解決數(shù)據(jù)庫死鎖最簡單的方法:設(shè)置超時(shí)時(shí)間。即當(dāng)兩個(gè)事務(wù)互相等待時(shí),當(dāng)一個(gè)等待時(shí)間超過設(shè)置的閾值時(shí),其中一個(gè)事務(wù)進(jìn)行回滾,另外一個(gè)等待的事務(wù)就能繼續(xù)執(zhí)行。
超時(shí)機(jī)制雖然簡單,但是其使用FIFO的方式來選擇超時(shí)回滾的事務(wù),假如第一個(gè)超時(shí)的事務(wù) 更新了很多行,遠(yuǎn)比第二個(gè)事務(wù)多,因此占用了更多的undo log,這時(shí)FIFO的方式,就顯得不適用了,因?yàn)榈谝粋€(gè)事務(wù)回滾時(shí)間明顯比第二個(gè)事務(wù)回滾時(shí)間長很多。
等待圖
因?yàn)镕IFO處理死鎖可能不適用,所以數(shù)據(jù)庫普遍采用了wait-for graph(等待圖)的方式來進(jìn)行死鎖檢測。和超時(shí)機(jī)制比較,這是一種更為主動的死鎖檢測方式,InnoDB也采用了這種方式。
等待圖要求數(shù)據(jù)庫保存以下兩種信息:
(1)鎖的信息鏈表(見圖6-5)
(2)事務(wù)等待鏈表(見圖6-5)
通過上述鏈表可以構(gòu)造出一張圖,而在這個(gè)圖中存在回路,則代表存在死鎖。在等待圖中,事務(wù)為圖中的節(jié)點(diǎn)。在圖中,事務(wù)T1指向事務(wù)T2邊的定義為:
(1)事務(wù)T1等待事務(wù)T2所占用的資源
(2)事務(wù)之間在等待相同的資源,而事務(wù)T1在事務(wù)T2之后
發(fā)現(xiàn)死鎖后,InnoDB會馬上回滾一個(gè)事務(wù)。
鎖升級
概念:將當(dāng)前鎖的粒度降低,比如說把行鎖升級為表鎖,那樣子會導(dǎo)致并發(fā)性能降低。
InnoDB不是根據(jù)每個(gè)記錄來產(chǎn)生行鎖的,而是根據(jù)每個(gè)事務(wù)訪問的每個(gè)頁對鎖進(jìn)行管理的,采用的是位圖的方式,因此不管一個(gè)事務(wù)鎖住頁中一條還是多條記錄,都是用一個(gè)鎖,其開銷通常是一致的。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的mysql lock_MySQL-锁总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: db2 sql 判断select是否为空
- 下一篇: Java面向对象基础接口和抽象的理解