mysql 索引间隙锁_关于mysql innodb间隙锁的一些思考
innodb鎖機(jī)制
在mysql當(dāng)中,關(guān)于innodb的鎖類型總共可以分為四種,包含了行鎖和表鎖,分別是基本鎖(Shared Locks:S鎖和排它鎖(Exclusive Locks:X鎖))、意向鎖(intention lock,分為意向共享鎖(IS鎖)和意向排他鎖(IX鎖))、行鎖(record Locks、gap locks、next-key locks、Insert Intention Locks)、自增鎖(auto-inc locks)
共享鎖、意向鎖、排他鎖的兼容矩陣如下:
行鎖的兼容矩陣如下:
本文主要想介紹下間隙鎖的一些相關(guān)知識(shí),以及出現(xiàn)問題的一些解決方案
間隙鎖官方解釋
間隙鎖是在索引記錄之間的間隙上的鎖定,或在最后一個(gè)索引記錄之前或之后的間隙上的鎖定。
間隙可能跨越單個(gè)索引值,多個(gè)索引值,甚至為空。
間隙鎖是性能和并發(fā)性之間權(quán)衡的一部分,并且在一些事務(wù)隔離級(jí)別中使用。
使用唯一索引鎖定行來搜索唯一行的語句不需要間隙鎖定。(這不包括搜索條件包含多列唯一索引的某些列的情況;在這種情況下,會(huì)發(fā)生間隙鎖定)。例如,如果id列具有唯一索引,則以下語句會(huì)使用id值為100?的行的索引記錄鎖定,其他會(huì)話是否在前面的間隙中插入行無關(guān)緊要:
SELECT*FROMchildWHEREid=100;
如果id沒有索引或具有非唯一索引,則該語句會(huì)鎖定上述間隙。
事務(wù)A可以在間隙上持有一個(gè)共享間隙鎖(gap S-lock),與此同時(shí),事務(wù)B在同一間隙上持有一個(gè)排他間隙鎖(gap X-lock)。允許兩個(gè)間隙鎖的原因是,如果從索引中清除記錄,則必須合并不同事務(wù)對(duì)記錄持有的間隙鎖。
間隙鎖在InnoDB中只是一種“?純粹的抑制?",它們只是阻止其他事務(wù)插入到當(dāng)前間隙當(dāng)中,但是它們不會(huì)阻止不同的事務(wù)在同一間隙上獲得間隙鎖。因此,共享間隙鎖具有與排他間隙鎖相同的效果。
間隙鎖可以被明確禁用。就是將事務(wù)隔離級(jí)別更改為READ COMMITTED或啟用?innodb_locks_unsafe_for_binlog?系統(tǒng)變量(現(xiàn)在已被棄用)。在這種情況下,針對(duì)搜索和索引掃描禁用間隙鎖,僅用于外鍵約束檢查和重復(fù)鍵檢查。
還有使用READ COMMITTED隔離級(jí)別或啟用WHERE條件進(jìn)行評(píng)估后,將釋放非匹配行的記錄鎖。對(duì)于?UPDATE語句,InnoDB?做一個(gè)“?半一致?”的讀取,將最新的提交版本返回給MySQL,以便MySQL可以確定該行是否符合UPDATE的where條件。
可能會(huì)出現(xiàn)的問題
當(dāng)高并發(fā)訪問時(shí)執(zhí)行delete操作和insert操作時(shí),如
delete from user u where u.id = 2;?insert into user u values(2);
delete from user u where u.id = 3;?insert into user u values(3);
執(zhí)行過程中可能會(huì)出現(xiàn)亂序,所以當(dāng)執(zhí)行delete from user u where u.id = 2和delete from user u where u.id = 3時(shí)就會(huì)拋出死鎖。這時(shí)數(shù)據(jù)庫會(huì)掃描索引,數(shù)據(jù)庫會(huì)向左掃描掃到第一個(gè)比給定參數(shù)小的值, 向右掃描掃描到第一個(gè)比給定參數(shù)大的值, 然后以此為界,構(gòu)建一個(gè)區(qū)間,最終會(huì)鎖住整個(gè)區(qū)間內(nèi)的數(shù)據(jù),這就是間隙鎖的死鎖,這種情況就得修改代碼邏輯, 存在才刪除,盡量不去刪除不存在的記錄。
解決方案
mysql默認(rèn)采用的是可重復(fù)讀(即repeatable read)隔離級(jí)別,并且會(huì)以Next-Key Lock的方式(即record lock和gap lock的結(jié)合)對(duì)數(shù)據(jù)行進(jìn)行加鎖,這樣可以有效防止幻讀的發(fā)生,但是在平時(shí)開發(fā)過程中,一些不正確的行為可能會(huì)導(dǎo)致間隙鎖鎖定范圍變得很大,線程間互相等待從而導(dǎo)致死鎖,下面將提供一些解決思路供大家參考:
1.將事務(wù)隔離級(jí)別改為讀提交(即read committed),binlog格式改為row或mixed,但可能會(huì)產(chǎn)生臟讀
2.避免刪除不存在的數(shù)據(jù),這樣會(huì)導(dǎo)致間隙鎖鎖定范圍變得很大,可能會(huì)導(dǎo)致死鎖,一般采用的方法是先讀取數(shù)據(jù),如果存在,再進(jìn)行刪除
3.對(duì)于select from where for update操作,一定要保證where條件字段的值一定存在,不然可能會(huì)導(dǎo)致間隙鎖鎖定的范圍變得很大,阻塞別的事務(wù),也有可能會(huì)導(dǎo)致死鎖
4.將一個(gè)較長的事務(wù)分解成幾個(gè)較小的事務(wù)
5.采用序列化(即serializable)隔離級(jí)別
6.避免在同一個(gè)事務(wù)中對(duì)同一張表的數(shù)據(jù)同時(shí)進(jìn)行快照讀和當(dāng)前讀
總結(jié)
在日常的開發(fā)過程中,雖然很少會(huì)遇到像mysql表死鎖的一些問題,但是了解常用的存儲(chǔ)引擎的一些特性和組成結(jié)構(gòu)還是非常有必要的,便于在遇到問題的時(shí)候能夠更快的查出問題并制定出解決方案。另外,在開發(fā)的過程中如果遇到一些長事務(wù)或者是多個(gè)數(shù)據(jù)插入、更新或刪除操作的時(shí)候多考慮一下,采取正確的做法也許就可以避免一些不必要線上問題的發(fā)生。
補(bǔ)充點(diǎn)(供排查問題的時(shí)候使用)
1. 查看事務(wù)隔離級(jí)別
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
2. 設(shè)置隔離級(jí)別
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
例如:set session transaction isolation level read uncommitted;
3. 查看表狀態(tài)
show table status like '表名';
4. 查看SQL性能
set profiling=1;
show profiles
或
show profile for query 1;(show profiles查詢出來的id)
5. 查看當(dāng)前最新事務(wù)ID
每開啟一個(gè)新事務(wù),記錄當(dāng)前最新事務(wù)的id,可用于后續(xù)死鎖分析。
show engine innodb status;
6. 查看事務(wù)鎖等待狀態(tài)情況
select?*?from information_schema.innodb_locks;
select? *from information_schema.innodb_lock_waits;
select * from information_schema.innodb_trx;
7. 查看innodb狀態(tài)(包含最近的死鎖日志)
show engine innodb status;
本文來自網(wǎng)易實(shí)踐者社區(qū),經(jīng)作者姚成授權(quán)發(fā)布。
總結(jié)
以上是生活随笔為你收集整理的mysql 索引间隙锁_关于mysql innodb间隙锁的一些思考的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不要随便点这个网站,你偷偷下载的小电影,
- 下一篇: MYSQL间隙锁详解