傳送門:MySQL鎖:01.總覽
傳送門:MySQL鎖:02.InnoDB鎖
傳送門:MySQL鎖:03.InnoDB行鎖 html
如同用書佔座,只有當其餘人想坐過來的時候,幫佔座的人才會出面提出該座位已經被佔用(被鎖)mysql
select .. for share/ lock in share modesql
X | IX | S | IS | AutoInc | |
---|---|---|---|---|---|
X | × | × | × | × | × |
IX | × | √ | × | √ | √ |
S | × | × | √ | √ | × |
IS | × | √ | √ | √ | √ |
AutoInc | × | √ | × | √ | × |
InnoDB對行鎖有進一步的細粒度:數據庫
lock wait 表示等待鎖。session
lock_ordinary | next-key lock,普通鎖,LOCK_S record lock + gap lock ,next-key lock | 鎖定記錄自己和前面的gap,record lock + gap lock (也叫next-key lock) RR級別下,利用next-key lock來避免產生幻讀 當innodb_locks_unsafe_for_binlog=1時,lock_ordinary會降級爲lock_rec_not_gap,至關於降級到RC。 8.0版本取消了參數innodb_locks_unsafe_for_binlog,即再也不容許RR級別的幻讀情景。 |
---|---|---|
lock_gap | gap lock | 鎖定一個範圍,但不包含記錄自己。 只鎖住索引記錄之間、或第一條索引記錄(INFIMUM)以前、又或最後一條索引記錄(SUPEREMUM)以後的範圍,並不鎖住記錄自己 RR級別下,對非惟一索引記錄當前讀時,除了對命中的記錄加lock_ordinary鎖,還會對該記錄以後的gap加gap lock,這是爲了保證可重複讀的須要,避免其餘事務插入數據形成幻讀。 innodb有兩條虛擬記錄,最小記錄和最大記錄,用來構建B+tree。 若是條件是where <= n, 這時會從n開始到最小值(虛擬最小記錄)之間範圍加鎖 若是條件是where >= n, 這時會從n開始到最大值(虛擬最小記錄)之間範圍加鎖 |
lock_rec_not_gap | record lock,鎖定記錄,但不鎖gap。 | record lock,單個記錄上的鎖。 僅鎖住記錄自己,不鎖前面的gap RC下的行鎖大多數都是這個鎖類型 RR下的主鍵、惟一索引等值條件下加鎖也一般是這個類型鎖 RR下的非惟一索引加鎖時(lock_ordinary),也會同時回溯到主鍵上加lock_rec_not_gap鎖。 但惟一性約束檢測時,即便是在RC下,老是要先加lock_s\lock_ordinary鎖。 |
lock_insert_intention | 意向插入鎖 | 是一種特殊的gap lock。 當插入索引記錄的時候用來判斷是否有其餘事務的範圍鎖衝突,若是有就須要等待。 同一個GAP中,只要不是同一個位置就能夠有多個插入意向鎖並存。 例如5~10區間,同時插入六、8就不會相互衝突阻塞,而同時插入9就會引起衝突阻塞等待。 插入意向鎖和間隙鎖(gap lock)並不兼容,一個gap加了lock gap後,沒法再加insert_intention。 |
lock_conv_by_other 鎖時由其餘事務建立的(好比隱式鎖轉換)併發
MyISAM引擎有表鎖,InnoDB引擎也能夠加表鎖。函數
binlog_format=row時,能夠放心的設置innodb_autoinc_lock_mode=2,下降自增鎖的影響。高併發
5.1以後新增innodb_autoinc_lock_mode選項。5.1之前,全部自增鎖都是表級別鎖,5.1之後能夠有不一樣的選項。
一樣的,也是在5.1之後binlog format支持多種方式(row,statement,mixed)。優化
自旋鎖 保護共享資源而提出的鎖機制,和互斥鎖相似,在任什麼時候刻下都只能有一個持有者,控制事務併發時CPU時間片分配。線程
能夠利用自旋鎖的狀態來判斷InnoDB線程內部爭用嚴重與否。
另外一種描述方式:
查看spin lock wait
mysql> show engine innodb status\G … ---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 239413 OS WAIT ARRAY INFO: signal count 560637 RW-shared spins 0, rounds 1028345, OS waits 118311 RW-excl spins 0, rounds 3590208, OS waits 45541 RW-sx spins 805351, rounds 5406426, OS waits 61835 Spin rounds per wait: 1028345.00 RW-shared, 3590208.00 RW-excl, 6.71 RW-sx ------------ - RW-shared spins 0 自旋0次, rounds 1028345 循環1028345圈, OS waits 118311 請求不到便sleep,sleep次數。 - OS waits / rounds - 118311 / 1028345= 0.115 - 45541 / 3590208 = 0.0127 - 61835 / 5406426 = 0.0114
---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 596113 OS WAIT ARRAY INFO: signal count 846843 RW-shared spins 0, rounds 4277086, OS waits 137734 RW-excl spins 0, rounds 22496950, OS waits 218313 RW-sx spins 637341, rounds 11383745, OS waits 170045 Spin rounds per wait: 4277086.00 RW-shared, 22496950.00 RW-excl, 17.86 RW-sx 218313/22496950 = 0.0097 170045/11383745 = 0.0149
默認都是加lock_ordinary鎖
若是是惟一索引列上的等值查詢,則退化成lock_rec_not_gap
全部版本,非惟一索引列上的範圍查詢,遇到第一個不符合條件的記錄也會加上lock_ordinary。
8.0.18版本之前,主要指<=場景:惟一索引列上的範圍查詢,遇到第一個不符合條件的記錄也會加上lock_ordinary ,在RC下會釋放,RR下不會釋放。
8.0.18版本之前,非惟一索引列上的等值查詢,向右遍歷遇到第一個不符合條件的記錄時,先加上lock_ordinary,再退化成lock_gap。
mysql> show engine innodb status \G mysql> select * from performance_schema.data_lock_waits; mysql> select * from performance_schema.data_locks; mysql> select * from performance_schema.metadata_locks;
Innodb_row_lock_current_waits | 當前等待的行鎖數量 (這個可能不許確。當前即使沒有發生,可能也大於0 .使用 select count(*) from sys.innodb_lock_waits 來確認是否真有行鎖發生。) |
---|---|
Innodb_row_lock_time | 請求行鎖總耗時(ms) |
Innodb_row_lock_time_avg | 請求行鎖平均耗時(ms) |
Innodb_row_lock_time_max | 請求行鎖最大耗時(ms) |
Innodb_row_lock_waits | 行鎖發生次數 |
show processlist
show engine innodb status
sys var: innodb_status_output & innodb_status_output_locks
sys.innodb_lock_waits & sys.schema_table_lock_waits
pfs.data_locks , 老版本是 innodb_locks
pfs.data_lock_waits
pfs.metadata_locks
請求的鎖類型 | 請求的鎖類型 | 請求的鎖類型 | 請求的鎖類型 | ||
---|---|---|---|---|---|
lock_ordinary | lock_rec_not_gap | lock_gap | lock_insert_intention | ||
已得到的鎖類型 | lock_ordinary | X | X | O | X |
已得到的鎖類型 | lock_rec_not_gap | X | X | O | O |
已得到的鎖類型 | lock_gap | O | O | O | X |
已得到的鎖類型 | lock_insert_intention | O | O | O | O |
gap只和insert intention鎖衝突
insert intention和任何鎖都不衝突,除非也在相同位置作意向插入鎖
先得到意向插入鎖的,再嘗試上gap lock是能夠的
可是反過來 ,先得到gap lock的,再嘗試加上意向插入鎖便會阻塞,
緣由是:先得到意向插入鎖時,實際上插入已經成功,意向插入鎖會被轉變爲對具體記錄的ordinary 或 rec_not_gap ,此時兩者都與lock gap兼容。
快照讀和當前讀。
select ..lock in share mode
select ..for update /DML
那些狀況下會觸發整個實例均可能 不可讀寫 的全局鎖?
用xtrabackup備份全實例數據時,會形成鎖等待嗎? 若是是mysqldump呢?
會話1發起backup lock,會話2執行mysqldump/xtrabackup備份,會被阻塞嗎?
mysql1> begin; Query OK, 0 rows affected (0.00 sec) mysql1> flush table with read lock; Query OK, 0 rows affected (0.00 sec) mysql1> create database oo; ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock mysql2> create database oo; --hang mysql3> select * from metadata_locks; +-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+ | GLOBAL | NULL | NULL | NULL | 139619211751216 | SHARED | EXPLICIT | GRANTED | lock.cc:1035 | 63 | 43 | | COMMIT | NULL | NULL | NULL | 139619186354560 | SHARED | EXPLICIT | GRANTED | lock.cc:1110 | 63 | 43 | |*GLOBAL | NULL | NULL | NULL | 139618850829760 | INTENTION_EXCLUSIVE | STATEMENT |*PENDING | lock.cc:747 | 65 | 5 | | TABLE | performance_schema | metadata_locks | NULL | 139619054809168 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6052 | 64 | 261 | +-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+-------------------+-----------------+----------------+ 4 rows in set (0.01 sec)
換一個順序
mysql1> lock instance for backup; Query OK, 0 rows affected (0.00 sec) mysql3> select * from metadata_locks; +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ |*BACKUP LOCK | NULL | NULL | NULL | 139619211751216 | SHARED | EXPLICIT | GRANTED | sql_backup_lock.cc:101 | 63 | 46 | | TABLE | performance_schema | metadata_locks | NULL | 139619054809168 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6052 | 64 | 263 | +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ 2 rows in set (0.00 sec) mysql2> begin; Query OK, 0 rows affected (0.00 sec) mysql2> flush table with read lock; Query OK, 0 rows affected (0.00 sec) mysql3> select * from metadata_locks; +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ | BACKUP LOCK | NULL | NULL | NULL | 139619211751216 | SHARED | EXPLICIT | GRANTED | sql_backup_lock.cc:101 | 63 | 46 | |*GLOBAL | NULL | NULL | NULL | 139618851123296 | SHARED | EXPLICIT | GRANTED | lock.cc:1035 | 65 | 10 | |*COMMIT | NULL | NULL | NULL | 139618850764288 | SHARED | EXPLICIT | GRANTED | lock.cc:1110 | 65 | 10 | | TABLE | performance_schema | metadata_locks | NULL | 139619053138368 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6052 | 64 | 266 | +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ 4 rows in set (0.00 sec) mysql1> create database oo; --hang mysql3> select * from metadata_locks; +-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+------------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+------------------------+-----------------+----------------+ | BACKUP LOCK | NULL | NULL | NULL | 139619211751216 | SHARED | EXPLICIT | GRANTED | sql_backup_lock.cc:101 | 63 | 46 | | GLOBAL | NULL | NULL | NULL | 139618851123296 | SHARED | EXPLICIT | GRANTED | lock.cc:1035 | 65 | 10 | | COMMIT | NULL | NULL | NULL | 139618850764288 | SHARED | EXPLICIT | GRANTED | lock.cc:1110 | 65 | 10 | |*GLOBAL | NULL | NULL | NULL | 139619186354560 | INTENTION_EXCLUSIVE | STATEMENT | PENDING | lock.cc:747 | 63 | 47 | | TABLE | performance_schema | metadata_locks | NULL | 139619053138368 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6052 | 64 | 267 | +-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+------------------------+-----------------+----------------+ 5 rows in set (0.00 sec) mysql2> unlock tables; --release FTWRL Query OK, 0 rows affected (0.00 sec) mysql1> lock instance for backup; ----前面的備份鎖還沒釋放 Query OK, 0 rows affected (0.00 sec) mysql1> create database oo; ----阻塞的DDL事務恢復執行了。 ERROR 1007 (HY000): Can't create database 'oo'; database exists mysql1> create database ooo; ----再執行一個DDL,成功。此時備份鎖還在呢。 Query OK, 1 row affected (0.21 sec) mysql3> select * from metadata_locks; ---備份鎖還在噢。 +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ | OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ | BACKUP LOCK | NULL | NULL | NULL | 139619211751216 | SHARED | EXPLICIT | GRANTED | sql_backup_lock.cc:101 | 63 | 46 | | TABLE | performance_schema | metadata_locks | NULL | 139619053138368 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6052 | 64 | 269 | +-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+------------------------+-----------------+----------------+ 2 rows in set (0.00 sec)
事務T1須要等待事務T2,畫一條T1到T2的線
以此類推
圖中若是有迴路就表示有死鎖。
偶爾死鎖不可怕,頻繁死鎖才須要關注
程序中應有事務失敗檢測及自動重複提交機制
多用小事務,並及時顯式提交/回滾
調整事務隔離級別爲RC,以消除gap lock,下降死鎖發生機率
事務中涉及多個表,或者涉及多行記錄時,每一個事務的操做順序都要保持一致,下降死鎖機率,最好用存儲過程/存儲函數固化
經過索引優化SQL效率,下降死鎖機率
死鎖不是「鎖死」,死鎖會快速檢測到,快速回滾。而「鎖死」則是行時間鎖等待。
innodb_rollback_on_timeout = on 時,一旦sql超時,整個事務回滾。
select … from | 一致性非鎖定讀 若是是serializable級別:Lock_ordinary|S |
---|---|
lock in share mode | Lock_ordinary |
for update | Lock_ordinary |
update/delete | Lock_ordinary |
update t … where col in (select .. from s ..) | s表加Lock_ordinary |
普通 insert | Lock_insert_intention|X 寫入請求檢測到有重複值時,會加鎖Lock_ordinary|X,可能引起死鎖 |
insert… on duplicate key update | Lock_ordinary |
insert into t select … from s | t表加Lock_rec_not_gap | X s表加Lock_ordinary | S 隔離級別爲RC或啓用innodb_locks_unsafe_for_binlog時,s表上採用無鎖一致性讀, 即:RC不加鎖,RR加nextkey-lock |
create table … select | 同 insert.. select |
replace | 無衝突/重複值時,和insert同樣:Lock_insert_intention | X, 不然Lock_ordinary | X |
replace into t select .. from s where | s表加Lock_ordinary |
auto_increment列上寫新數據時 | 索引末尾加 record lock |
請求自增列計數器時,InnoDB使用一個auto-inc mutex, 但只對請求的那個SQL有影響(lock_mode = 1 時) | -------------------------------- |
有外鍵約束字段上進行insert/update/delete操做時 | 除了自身的鎖,還會在外表約束列上同時加Lock_rec_not_gap |
nextkey-lock 只發生在RR隔離級別下 |