传送门: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隔离级别下 |