详细介绍MySQL/MariaDB的锁

官方手册:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-transaction-model.html

1.事务提交的方式

在MariaDB/MySQL中有3种事务提交的方式。

1.显式开启和提交。

使用begin或者start transaction来显式开启一个事务,显式开启的事务必须使用commit或者rollback显式提交或回滚。几种特殊的情况除外:行版本隔离级别下的更新冲突和死锁会自动回滚。

在存储过程中开启事务时必须使用start transaction,因为begin会被存储过程解析为begin...end结构块。

另外,MariaDB/MySQL中的DDL语句会自动提交前面所有的事务(包括显示开启的事务),而在SQL Server中DDL语句还是需要显式提交的,也就是说在SQL Server中DDL语句也是可以回滚的。

2.自动提交。(MySQL默认的提交方式)

不需要显式begin或者start transaction来显式开启事务,也不需要显式提交或回滚事务,每次执行DML和DDL语句都会在执行语句前自动开启一个事务,执行语句结束后自动提交或回滚事务。

3.隐式提交事务

隐式提交事务是指执行某些语句会自动提交事务,包括已经显式开启的事务。

会隐式提交事务的语句主要有:

(1).DDL语句(其中有truncate table)。

(2).隐式修改mysql数据库架构的操作:create user,drop user,grant,rename user,revoke,set password。

(3).管理语句:analyze table、cache index、check table、load index into cache、optimize table、repair table。

通过设置 auto_commit 变量值为1或0来设置是否自动提交,为1表示自动提交,0表示关闭自动提交,即必须显式提交。但是不管设置为0还是1,显式开启的事务必须显式提交,而且隐式提交的事务不受任何人为控制。

2.MariaDB/MySQL中的锁

锁和事务的实现是存储引擎内的组件管理的,而MariaDB/MySQL是插件式的存储引擎实现方式,所以不同的存储引擎可以支持不同级别的锁和事务。

2.1 不同存储引擎支持的锁级别

MariaDB/MySQL相比其他数据产品来说,支持的锁比较简单。

1.MyISAM、Aria(MariaDB中对myisam的改进版本)和memory存储引擎只支持表级别的锁。

2.innodb支持行级别的锁和表级别的锁,默认情况下在允许使用行级别锁的时候都会使用行级别的锁。

3.DBD存储引擎支持页级别和表级别的锁。

2.2 锁类型

在MariaDB/MySQL中只有简单的几种锁类型:

1.共享锁(S):即读锁,不涉及修改数据,在检索数据时才申请的锁。

2.独占锁(X):增、删、改等涉及修改操作的时候,都会申请独占锁。

以上是支持表锁的存储引擎都会有的锁类型。以下两种是支持行锁或页锁才会有的锁类型,也就是说myisam没有下面的锁,而innodb有。

3.意向共享锁(IS):获取低级别共享锁的同时,在高级别上也获取特殊的共享锁,这种特殊的共享锁是意向共享锁。

4.意向独占锁(IX):获取低级别独占锁的同时,在高级别上也获取特殊的独占锁,这种特殊的独占锁是意向独占锁。

低级别锁表示的是行锁或页锁,意向锁可能是多条记录组成的范围锁,也可能直接就是表意向锁。

2.3 锁兼容性

如下表:

独占锁和所有的锁都冲突,意向共享锁和共享锁兼容(这是肯定的),还和意向独占锁兼容。所以加了意向共享锁的时候,可以修改行级非共享锁的记录。同理,加了意向独占锁的时候,可以检索这些加了独占锁的记录。

3.MyISAM的表级锁(lock tables和unlock语句)

MariaDB/MySQL中myisam和innodb都支持表级锁。表级锁分为两种:读锁(read lock)和写锁(write lock)。本节所述均为myisam支持的,同样innodb也一样支持。

可以通过语句来实现表级锁的锁定和解锁,这些语句的操作环境是当前客户端会话(即作用范围是会话)。锁表的时候可以一次性锁定多张表,并使用不同的锁,而解锁的时候只能一次性解锁当前客户端会话的所有表。

LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias] lock_type] ...
lock_type:READ [LOCAL]| [LOW_PRIORITY] WRITEUNLOCK TABLES

lock tables命令可以锁表或锁视图,锁视图的时候会自动将视图内的基表加上对应类型的锁。由于MariaDB/MySQL中触发器是基于表的,所以lock tables锁定表的时候,触发器内使用的表也都会被锁定。

例如:table1上有一个如下触发器:

CREATE TRIGGER trigger1 AFTER INSERT ON table1 FOR EACH ROW
BEGININSERT INTO table2 VALUES (1);UPDATE table3 SET writes = writes+1WHERE id = NEW.id AND EXISTS (SELECT id FROM table4);
END;

如果为table1加上写锁,则table2、table3都会加上写锁,而table4会加上读锁。

lock tables命令会隐式释放当前客户端会话中之前的所有锁。

现在创建3张表作为测试表。

DROP TABLE IF EXISTS t1,t2,t3;
CREATE TABLE t1(a INT,b CHAR(5))ENGINE=MYISAM;
CREATE TABLE t2(a INT,b CHAR(5))ENGINE=MYISAM;
CREATE TABLE t3(a INT,b CHAR(5))ENGINE=MYISAM;
INSERT INTO t1 VALUES(1,'a');
INSERT INTO t2 VALUES(1,'a');
INSERT INTO t3 VALUES(1,'a');

给t1加上读锁。

LOCK TABLES t1 READ;

此时当前会话将无法操作t1以外的任何表,连查询也不允许,因为只有t1表加了锁。而其他会话则可以进行查询,但不能进行更新。

当再次使用lock tables命令的时候,会先释放当前会话之前所有的锁,再对lock tables命令中的表申请锁。

例如,上面会话1锁了表t1,此时无法操作t2表。现在对t2表lock table。

lock tables t2 read;

此时就可以操作t2表而不能操作t1表了,因为对t1表的锁已经释放了。

使用lock tables给表加读锁的时候,还有一个选项local,该选项表示对当前现有的记录加上锁,不影响其他会话的插入记录语句。但是否真的能插入,由变量concurrent_insert决定,该变量默认值为auto。关于并发插入,见我翻译的官方手册:https://mariadb.com/kb/zh-cn/concurrent-inserts/。

如果设置为2,那么对myisam表的并发插入有一定提升。

现在测试默认的情况,即 concurrent_insert=auto 的情况。

insert into t1 values(2,'c'),(3,'d'),(4,'e'),(5,'f');show variables like "%concurrent_insert%";
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| concurrent_insert | AUTO  |
+-------------------+-------+
1 row in setlock tables t1 read local;

在另一个会话中插入一条记录,这是允许的操作。当然,在锁表的会话中肯定是不能插入的。

insert into t1 values(8,'h');

解锁,并删除中间的两条记录,形成空洞。然后再锁定表。

mysql> unlock tables;
mysql> delete from t1 where a=3 or a=4;
mysql> lock tables t1 read local;

在其他会话中插入记录。会发现被阻塞。当表解锁后立即成功插入。

insert into t1 values(3,'h'),(9,'i'),(8,'g');

将concurrent_insert设置为2,即always,此时不管是否有空洞都允许向myisam表尾部插入。

delete from t1 where a=3 or a=8 or a=9;
set @@global.concurrent_insert=2;
lock tables t1 read local;
insert into t1 values(3,'d'),(8,'g'),(9,'i');

此时发现能够正常插入,且查询t1表发现,这些记录都插入在表的尾部。

默认情况下,使用表级锁的存储引擎中(所以innodb不支持),写锁的优先级高于读锁。这意味着,当表上已经有一个写锁的时候,后续的写操作、读操作都会队列化,且队列中的写操作总是在读操作之前执行,即使写操作比读操作后到达MySQL/MariaDB服务器。可以改变这种优先级。详细内容见:http://www.cnblogs.com/f-ck-need-u/p/8907252.html

4.innodb中的锁

innodb支持行级锁,也是在允许的情况下默认申请的锁。

SQL Server中的锁是一种稀有资源,且会在需要的时候锁升级,所以锁越多性能越差。而MariaDB/MySQL中的锁不是稀有资源,不会进行锁升级,因此锁的多少不会影响性能,1个锁和1000000个锁性能是一样的(不考虑锁占用的内存),锁的多少只会影响并发性。

4.1 查看锁信息的几种方法

现在人为造成一个锁等待。

会话1执行:

begin;
update tt set b='h' where a=1;

会话2执行:

begin;
update tt set b= 'x' where a=1;

此时会话2被阻塞,进入锁等待状态。

要查看锁信息。有几种方法:

1.通过show engine innodb status来查看,其中的transactions片段可以看到事务,其中包括锁等待。

以下是没有激活任何事务的信息:

mysql> show engine innodb status;
------------
TRANSACTIONS
------------
Trx id counter 2856
Purge done for trx's n:o < 2856 undo n:o < 0 state: running
History list length 25
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421383739060216, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421383739059200, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421383739057168, not started
0 lock struct(s), heap size 1136, 0 row lock(s)

三个"---TRANSACTION"表示当前开启了3个mysql会话,但这3个会话都没有任何事务。

以下是某会话开启一个事务,但没有任何锁等待的事务信息:

mysql> show engine innodb status;
------------
TRANSACTIONS
------------
Trx id counter 2857
Purge done for trx's n:o < 2856 undo n:o < 0 state: running
History list length 25
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421383739060216, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421383739057168, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 2856, ACTIVE 10 sec
1 lock struct(s), heap size 1136, 0 row lock(s), undo log entries 1
MySQL thread id 39, OS thread handle 139909209945856, query id 1814112 localhost root Reset for next command

不难看出,这个事务是一个需要写日志的DML事务。

以下是有锁等待的事务信息:

mysql> show engine innodb status;
------------
TRANSACTIONS
------------
Trx id counter 14915
Purge done for trx's n:o < 14912 undo n:o < 0 state: running but idle
History list length 896
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 14909, not started
MySQL thread id 36, OS thread handle 0x7f5d57e4b700, query id 961 localhost root init
show engine innodb status
---TRANSACTION 14914, ACTIVE 465 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1184, 1 row lock(s)
MySQL thread id 34, OS thread handle 0x7f5d57e8c700, query id 959 localhost root updating
update tt set b= 'x' where a=1
------- TRX HAS BEEN WAITING 13 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 184 page no 3 n bits 80 index `GEN_CLUST_INDEX` of table `test`.`tt` trx id 14914 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 00: len 6; hex 000000000601; asc       ;;1: len 6; hex 000000003a41; asc     :A;;2: len 7; hex 2f000001580feb; asc /   X  ;;3: len 4; hex 80000001; asc     ;;4: len 5; hex 6820202020; asc h    ;;------------------
---TRANSACTION 14913, ACTIVE 490 sec
2 lock struct(s), heap size 360, 6 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f5d57f4f700, query id 900 localhost root

从上面的结果可以看到锁等待的信息。

"TRX HAS BEEN WAITING 13 SEC FOR THIS LOCK TO BE GRANTED"表示该事务申请锁已经等待了13秒。

"RECORD LOCKS space id 184 page no 3 n bits 80 index `GEN_CLUST_INDEX` of table `test`.`tt` trx id 14914 lock_mode X waiting"表示test.tt表上的记录要申请的行锁(recode lock)是独占锁并且正在waiting,并且标明了该行记录所在表数据文件中的物理位置:表空间id为184,页码为3。

关于这些信息的详细解释,后文会逐渐说明。

2.使用show processlist查看。

show full processlist;

从上面的结果可以看出,update语句一直处于updating状态。所以,该方法查出来的并不一定是锁等待,有可能是更新的记录太多或者其他问题,总之这里看出来的是该语句还没有执行完成。

3.查看information_schema中的数据字典

在information_schema架构下,有3个表记录了事务和锁相关的信息。分别是INNODB_TRX,INNODB_LOCKS,INNODB_LOCK_WAITS

这三个表可能相对复杂,以下分别说明这3张表的各列。

根据上面实验过程中的锁查看该表的部分结果如下:

mysql> select * from information_schema.INNODB_TRX\G
*************************** 1. row ***************************trx_id: 14914trx_state: LOCK WAITtrx_started: 2017-03-30 06:07:51trx_requested_lock_id: 14914:184:3:2trx_wait_started: 2017-03-30 06:39:25trx_weight: 2trx_mysql_thread_id: 34trx_query: update tt set b= 'x' where a=1
*************************** 2. row ***************************trx_id: 14913trx_state: RUNNINGtrx_started: 2017-03-30 06:07:26trx_requested_lock_id: NULLtrx_wait_started: NULLtrx_weight: 3trx_mysql_thread_id: 1trx_query: NULL

从结果中可以看出id为14914的事务正处于锁等待状态,该事务中要申请锁的语句是update语句,也就是说是因为该语句而导致的锁等待。

从innodb_trx表中只能查看到事务的信息,而不能看到锁相关的信息。要看锁的信息,需要查看表innodb_locks。

mysql> select * from information_schema.INNODB_LOCKS\G
*************************** 1. row ***************************lock_id: 14914:184:3:2
lock_trx_id: 14914lock_mode: Xlock_type: RECORDlock_table: `test`.`tt`lock_index: GEN_CLUST_INDEXlock_space: 184lock_page: 3lock_rec: 2lock_data: 0x000000000601
*************************** 2. row ***************************lock_id: 14913:184:3:2
lock_trx_id: 14913lock_mode: Xlock_type: RECORDlock_table: `test`.`tt`lock_index: GEN_CLUST_INDEXlock_space: 184lock_page: 3lock_rec: 2lock_data: 0x000000000601
2 rows in set (0.00 sec)

从上面的结果中看出,锁所在的事务ID为14914,并且锁模式为独占锁,类型为record即行锁,申请锁的表为tt表,而且锁定的页数为3页,锁定的行有2行,锁定行的主键值为0x000000000601。也许会奇怪,在前面实验过程中根本就没有建立主键,这里为什么会有主键值,这是因为MySQL在加锁的时候判断是否有索引,没有索引的时候会自动隐式的添加索引(聚集索引),从上面锁的索引为"GEN_CLUST_INDEX"可以看出。

所以我们可以知道,MariaDB/MySQL中的行锁是通过键锁(Key)来实现的(在SQL Server中有堆表的概念,SQL Server对于没有索引的表,其行锁通过rid锁来实现)。

并且从上面的两段结果也可以看到,它们的申请锁资源所处位置是相同的,正因为位置相同,所以才有了锁等待。

现在在会话1上创建索引,然后人为造成锁等待再来查看innodb_locks表。

在会话1和会话2执行:

rollback;

在会话1执行:

create index idx_tt on tt(a);
begin;
update tt set b='h' where a=1;

在会话2执行:

begin;
update tt set b='x' where a=1;

查看innodb_locks表。

mysql> select * from information_schema.INNODB_LOCKS\G
*************************** 1. row ***************************lock_id: 14925:184:4:2
lock_trx_id: 14925lock_mode: Xlock_type: RECORDlock_table: `test`.`tt`lock_index: ind_ttlock_space: 184lock_page: 4lock_rec: 2lock_data: 1, 0x000000000601
*************************** 2. row ***************************lock_id: 14924:184:4:2
lock_trx_id: 14924lock_mode: Xlock_type: RECORDlock_table: `test`.`tt`lock_index: ind_ttlock_space: 184lock_page: 4lock_rec: 2lock_data: 1, 0x000000000601
2 rows in set (0.00 sec)

此处发现,锁的索引类型为ind_tt,而锁住行的主键值已经变为1个1了。

查出了锁的信息后,就可以人为的判断出锁等待信息。但是当事务比较大的时候,锁的信息非常繁杂,这时候通过上面的两张表无法轻易判断相关锁信息。由此要借助第三张表 innodb_lock_waits ,该表只有4列,且意义直观明了。

还是上面试验过程中造成的锁等待,查看那innodb_lock_waits表结果如下:

mysql> select * from information_schema.INNODB_LOCK_WAITS\G
*************************** 1. row ***************************
requesting_trx_id: 14914
requested_lock_id: 14914:184:3:2blocking_trx_id: 14913blocking_lock_id: 14913:184:3:2
1 row in set (0.00 sec)

可以看到,申请锁的事务ID为14914,阻塞在前方的事务ID为14913。

有了这3张表,还可以将它们联接起来更直观的显示想要的结果。如下:

SELECTr.trx_id AS waiting_trx_id,r.trx_mysql_thread_id AS waiting_thread,r.trx_query AS waiting_query,b.trx_id AS blocking_trx_id,b.trx_mysql_thread_id AS blocking_thread,b.trx_query AS blocking_query
FROMinformation_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id\G
*************************** 1. row ***************************waiting_trx_id: 14925waiting_thread: 34waiting_query: update tt set b='x' where a=1
blocking_trx_id: 14924
blocking_thread: 1blocking_query: NULL

现在可以直观的看到14925事务被阻,语句为update,阻塞它的事务为14924。

还可以从以下联接语句中查看锁和事务的相关信息。

SELECTtrx_id,trx_state,lock_id,lock_mode,lock_type,lock_table,trx_mysql_thread_id,trx_query
FROMinformation_schema.innodb_trx t
JOIN information_schema.innodb_locks l ON l.lock_trx_id = t.trx_id;

4.2 innodb表的外键和锁

在innodb表中,创建外键的时候若外键列上没有索引,则会在创建过程中自动在外键列上隐式地创建索引。

存在这样一种情况,当向子表中插入数据的时候,会向父表查询该表中是否存在对应的值以判断将要插入的记录是否满足外键约束,也就是说会对父表中对应的记录加上依赖性的共享锁,并在表上加意向共享锁。如果此时父表上对应的记录正好有独占锁,那么插入就会失败。同理,从子表中删除或更新记录也是一样的。

现在创建父表parent和子表child,并不要在外键列(pid)上显式创建索引。

create table parent(pid int primary key);
create table child(cid int primary key,pid int,foreign key(pid) references parent(pid));
show create table child\G
*************************** 1. row ***************************Table: child
Create Table: CREATE TABLE `child` (`cid` int(11) NOT NULL,`pid` int(11) DEFAULT NULL,PRIMARY KEY (`cid`),KEY `pid` (`pid`),CONSTRAINT `child_ibfk_1` FOREIGN KEY (`pid`) REFERENCES `parent` (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

从show的结果中可以发现,已经自动添加了索引列pid。

插入一些测试记录。

insert into parent values(1),(2),(3);

在会话1中执行:

begin;
delete from parent where pid=3;

在会话2中执行:

begin;
insert into child select 3,3;

这时会发现会话2被阻塞了。通过innodb_trx和innodb_locks表的联合,得到如下结果:

SELECTtrx_id,trx_state,lock_id,lock_mode,lock_type,lock_table,trx_mysql_thread_id,trx_query
FROMinformation_schema.innodb_trx t
JOIN information_schema.innodb_locks l ON l.lock_trx_id = t.trx_id\G
*************************** 1. row ***************************trx_id: 14951trx_state: LOCK WAITlock_id: 14951:185:3:4lock_mode: Slock_type: RECORDlock_table: `test`.`parent`
trx_mysql_thread_id: 34trx_query: insert into child select 3,3
*************************** 2. row ***************************trx_id: 14946trx_state: RUNNINGlock_id: 14946:185:3:4lock_mode: Xlock_type: RECORDlock_table: `test`.`parent`
trx_mysql_thread_id: 1trx_query: NULL

不难看出,insert语句想要在父表parent上的资源"14951:185:3:4"加共享锁,但是此时父表上该资源已经有了独占锁,所以被阻塞了。

并且也可以判断出,通过外键读取父表时的模式是lock in share mode,而不是基于快照的行版本读(什么是lock in share mode和行版本快照读见事务隔离级别内容),假如是基于行版本的快照读,那么就可以查出存在pid=3的记录而导致子表插入成功,这样也可能导致父表和子表不满足外键约束。

4.3 innodb锁算法

innodb支持行级锁,但是它还支持范围锁。即对范围内的行记录加行锁。

有三种锁算法:

  • 1.record lock:即行锁
  • 2.gap lock:范围锁,但是不锁定行记录本身
  • 3.next-key lock:范围锁加行锁,即范围锁并锁定记录本身,gap lock + record lock。

record lock是行锁,但是它的行锁锁定的是key,即基于唯一性索引键列来锁定(SQL Server还有基于堆表的rid类型行锁)。如果没有唯一性索引键列,则会自动在隐式列上创建索引并完成锁定。

next-key lock是行锁和范围锁的结合,innodb对行的锁申请默认都是这种算法。如果有索引,则只锁定指定范围内的索引键值,如果没有索引,则自动创建索引并对整个表进行范围锁定。之所以锁定了表还称为范围锁定,是因为它实际上锁的不是表,而是把所有可能的区间都锁定了,从主键值的负无穷到正无穷的所有区间都锁定,等价于锁定了表。

以下示例过程将演示范围锁的情况。

1.有索引的情况

首先创建一个有索引的表t。然后插入几个被分隔的记录。

create table t(id int);
create unique index idx_t on t(id);
insert into t values(1),(2),(3),(4),(7),(8),(12),(15);

在会话1执行:无需知道lock in share mode是什么意思,只需知道它的作用是在读取的时候加上共享锁并且不释放,具体内容在事务章节中会说明。

begin;
select * from t where id<5 lock in share mode;

在会话2执行:

insert into t values(9);
insert into t values(6);

这时发现第一条插入语句是正常插入的,而第二条语句被阻塞。 show engine innodb status 看结果。

mysql> show engine innodb status;
------------
TRANSACTIONS
------------
Trx id counter 14992
Purge done for trx's n:o < 14987 undo n:o < 0 state: running but idle
History list length 914
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started
MySQL thread id 50, OS thread handle 0x7f5d57e0a700, query id 1495 localhost root init
show engine innodb status
---TRANSACTION 14991, ACTIVE 17 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1
MySQL thread id 49, OS thread handle 0x7f5d57d88700, query id 1491 localhost root update
insert into t values(6)
------- TRX HAS BEEN WAITING 17 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 187 page no 4 n bits 80 index `idx_t` of table `test`.`t` trx id 14991 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 00: len 4; hex 80000007; asc     ;;1: len 6; hex 00000000060c; asc       ;;------------------
---TRANSACTION 14989, ACTIVE 32 sec
2 lock struct(s), heap size 360, 5 row lock(s)
MySQL thread id 43, OS thread handle 0x7f5d57f0e700, query id 1489 localhost root

其中"locks gap"就表示阻塞insert语句的锁是gap锁,即范围锁。锁定的范围包括(-∞,4],(4,7](锁到操作行的下一个key,此处插入id=6,由于存在id=7的key,所以锁到7为止,这就是next-key的意思)。当测试插入或修改-1,0,5,6等小于7的值都会被阻塞,而插入或修改大于7的值就不会被阻塞。

如何判断锁定的范围大小?可以通过下面的查询语句:

mysql> select * from information_schema.INNODB_LOCKS\G
*************************** 1. row ***************************lock_id: 2856:109:4:6
lock_trx_id: 2856lock_mode: X,GAPlock_type: RECORDlock_table: `test`.`t`lock_index: idx_tlock_space: 109lock_page: 4lock_rec: 6lock_data: 7
*************************** 2. row ***************************lock_id: 421383739058184:109:4:6
lock_trx_id: 421383739058184lock_mode: Slock_type: RECORDlock_table: `test`.`t`lock_index: idx_tlock_space: 109lock_page: 4lock_rec: 6lock_data: 7
2 rows in set (0.000 sec)

lock_mode为"X+GAP",表示next-key lock算法。其中lock_data值为7,表示锁定了值为7的记录,这是最大锁定范围边界。lock_rec的值为6,表示锁定了6行记录,其中1,2,3,4,7共5行记录是通过gap锁锁定的范围,加上待插入的id=6(该行为key锁锁定),共锁定6行记录。

而如果使用的是大于号,由于操作任何一条记录,它的下一个key都会被锁定,这等价于锁定了整个无穷区间,即实现了表锁的功能。如下:

在会话1上执行:

# 首先回滚
rollback;
begin;
select * from t where id>10 lock in share mode;

在会话2执行:

insert into t values(0);
insert into t values(5);
insert into t values(100);

会发现任何插入都是阻塞的。即锁定的范围为(-∞,+∞),等价于锁定了整张表。

但是如果使用的等于号,那么在查找索引的时候发现只需锁定一条记录和下一条记录中间的范围即可。

在会话1执行:

# 首先回滚
rollback;
begin;
select * from t where id=5 lock in share mode;

在会话2执行:

insert into t values(0);
insert into t values(10);

会发现上述插入都是允许的。

但如果插入id=6的记录,则阻塞,因为锁定的范围为[5,7]区间。

也就是说,在有索引的情况下,如果是非具体的行锁,那么就会将能扫描到的索引键值内的所有范围加锁。

下面测试没有索引的情况。

2.无索引的情况

首先创建没有索引的表,然后插入一些分隔的记录。

create table ttt(id  int);
insert into ttt values(1),(2),(3),(4),(7),(8),(12),(15);

在会话1上执行:

begin;
select * from ttt where id=4  lock in share mode;

在会话2上执行:

insert into ttt values(5);
insert into ttt values(100);
insert into ttt values(0);

会发现不管是插入哪些记录,都会被阻塞。因为没有索引键值的时候,自动隐式创建索引会锁定整个区间。查看下innodb的事务状态。

mysql> show engine innodb status;
------------
TRANSACTIONS
------------
Trx id counter 15102
Purge done for trx's n:o < 15096 undo n:o < 0 state: running but idle
History list length 944
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 15066, not started
MySQL thread id 53, OS thread handle 0x7f5d57d47700, query id 1615 localhost root
---TRANSACTION 15065, not started
MySQL thread id 52, OS thread handle 0x7f5d57dc9700, query id 1590 localhost root
---TRANSACTION 15097, not started
MySQL thread id 51, OS thread handle 0x7f5d57ecd700, query id 1637 localhost root
---TRANSACTION 0, not started
MySQL thread id 50, OS thread handle 0x7f5d57e0a700, query id 1642 localhost root init
show engine innodb status
---TRANSACTION 15101, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 49, OS thread handle 0x7f5d57d88700, query id 1641 localhost root update
insert into ttt values(0)
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 190 page no 3 n bits 80 index `GEN_CLUST_INDEX` of table `test`.`ttt` trx id 15101 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 00: len 8; hex 73757072656d756d; asc supremum;;------------------
---TRANSACTION 15087, ACTIVE 215 sec
2 lock struct(s), heap size 360, 9 row lock(s)
MySQL thread id 43, OS thread handle 0x7f5d57f0e700, query id 1631 localhost root

可以发现,这时的锁不是范围锁,因为没有了locks gap,但却仍然是行锁而不是表锁,只不过此时等价于表锁。如下

mysql> select * from information_schema.innodb_locks\G
*************************** 1. row ***************************lock_id: 15102:190:3:1
lock_trx_id: 15102lock_mode: Xlock_type: RECORDlock_table: `test`.`ttt`lock_index: GEN_CLUST_INDEXlock_space: 190lock_page: 3lock_rec: 1lock_data: supremum pseudo-record
*************************** 2. row ***************************lock_id: 15087:190:3:1
lock_trx_id: 15087lock_mode: Slock_type: RECORDlock_table: `test`.`ttt`lock_index: GEN_CLUST_INDEXlock_space: 190lock_page: 3lock_rec: 1lock_data: supremum pseudo-record

发现确实是行锁而非表锁。并且索引键值那里为"supermum pseudo-record",这表示锁定的是"最大上界伪记录",即锁定的是无穷值。

没索引的时候,哪怕查询具体的行记录都会锁定整个区间,更不用说锁定范围(例如:where id>5)。其实它们的结果都是一样的:锁定整个区间。

4.4 innodb中的锁等待超时

在innodb存储引擎中,当出现锁等待时,如果等待超时,将会结束事务,超时时长通过动态变量innodb_lock_wait_timeout值来决定,默认是等待50秒。关于锁等待超时,可以直接在语句中设置超时时间。可以设置锁等待超时时间的语句包括:wait n的n单位为秒,nowait表示永不超时。

ALTER TABLE tbl_name [WAIT n|NOWAIT] ...
CREATE ... INDEX ON tbl_name (index_col_name, ...) [WAIT n|NOWAIT] ...
DROP INDEX ... [WAIT n|NOWAIT]
DROP TABLE tbl_name [WAIT n|NOWAIT] ...
LOCK TABLE ... [WAIT n|NOWAIT]
OPTIMIZE TABLE tbl_name [WAIT n|NOWAIT]
RENAME TABLE tbl_name [WAIT n|NOWAIT] ...
SELECT ... FOR UPDATE [WAIT n|NOWAIT]
SELECT ... LOCK IN SHARE MODE [WAIT n|NOWAIT]
TRUNCATE TABLE tbl_name [WAIT n|NOWAIT]

超时后结束事务的方式有中断性结束回滚性结束两种方式,这也是通过变量来控制的,该变量为innodb_rollback_on_timeout,默认为off,即超时后不回滚,也即中断性结束

mysql> show variables like "innodb%timeout";
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| innodb_flush_log_at_timeout | 1     |
| innodb_lock_wait_timeout    | 50    |
| innodb_rollback_on_timeout  | OFF   |
+-----------------------------+-------+

转载于:https://www.cnblogs.com/f-ck-need-u/p/8995475.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/486152.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美国专利商标局发布人工智能专利扩散分析报告

以下文章来源&#xff1a;中科院知识产权信息&#xff0c;2020-11-23报告显示&#xff0c;从2002到2018年&#xff0c;美国人工智能专利的年申请量增长超过100%&#xff0c;从每年3万件增加到6万多件&#xff0c;含人工智能的专利申请所占份额从9%上升到近16%。同时&#xff0c…

java 开发环境的搭建

这里主要说的是在windows 环境下怎么配置环境。 1.首先安装JDK java的sdk简称JDK &#xff0c;去其官方网站下载最近的JDK即可。。http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html点击下载好的exe文件安装即可。 2.接下来我们需要配置环…

linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表

路由表的创建inet_init() -> ip_init() -> ip_fib_init() -> fib_net_init() -> ip_fib_net_init()[net\ipv4\fib_frontend.c]首先为路由表分配空间&#xff0c;这里的每个表项hlist_head实际都会链接一个单独的路由表&#xff0c;FIB_TABLE_HASHSZ表示了分配多少个…

2017级面向对象程序设计 作业二

以下均以扫描方式为例&#xff0c;即电梯只会在最底层和最高层选择掉头&#xff0c;路途中遇到路径方向相同的乘客将他带上电梯。 文字描述面向过程实现的步骤&#xff1a; 一. 定义有关电梯的变量&#xff0c;如&#xff1a;1.电梯当前所在楼层.&#xff0c;2. 电梯内的人数&a…

新型支架状电极允许人类思想操作计算机

Illustration: Synchron来源&#xff1a;IEEE电气电子工程师据悉&#xff0c;两名患有神经肌肉疾病的澳大利亚人在他们的大脑中植入了支架状的电极&#xff0c;使他们能够利用自己的思想操作电脑&#xff0c;从而恢复了一些个人独立性。据发明者介绍&#xff0c;这是这种被称为…

java中的foreach

foreach 并不是java中的关键字&#xff0c;是for语句的特殊简化版&#xff0c;在比那里数组&#xff0c;集合时&#xff0c;foreach更加简单快捷&#xff0c;从字面上的意思理解 foreach 也就是 “ for每一个 ”的意思&#xff0c;那么到底怎么使用 foreach语句呢&#xff1f; …

ACM数论-素数

ACM数论——素数 素数定义&#xff1a; 质数&#xff08;prime number&#xff09;又称素数&#xff0c;有无限个。质数定义为在大于1的自然数中&#xff0c;除了1和它本身以外不再有其他因数&#xff0c;这样的数称为质数。例 子&#xff1a;2、3、5、7、11、13、17、19。&am…

机器视觉中彩色成像必须考虑的十个问题

来源&#xff1a;Imagination Tech在为你的产品开发最适合的机器视觉系统时&#xff0c;需要考虑很多因素&#xff0c;以下列出开发过程中需要考虑的一些问题&#xff1a;颜色准确性/差异化首先要考虑的是应用程序所需的颜色精度和差异程度。在某些应用中&#xff0c;机器视觉相…

嫦娥“挖土”归来有多难?看看中国首颗返回式卫星的故事

本文转载自“科技日报&#xff08;kjrbwx&#xff09;”&#xff0c;原标题《嫦娥“挖土”归来有多难&#xff1f;看看中国首颗返回式卫星的故事》&#xff0c;作者 | 吕炳宏 付毅飞2020年11月30日&#xff0c;嫦娥五号探测器在环月轨道上&#xff0c;成功实施着陆器上升器组合…

重磅,2020年度第十届吴文俊人工智能科学技术奖获奖名单公示

来源&#xff1a;科奖圈根据《吴文俊人工智能科学技术奖励条例》和《吴文俊人工智能科学技术奖励实施细则》相关规定&#xff0c;经全国各地方人工智能学会、协会及联盟&#xff0c;各高校及科研&#xff08;院&#xff09;所&#xff0c;学会各专业委员会及工作委会&#xff0…

理解 %IOWAIT (%WIO)

%iowait 是 “sar -u” 等工具检查CPU使用率时显示的一个指标&#xff0c;在Linux上显示为 %iowait&#xff0c;在有的Unix版本上显示为 %wio&#xff0c;含义都是一样的。这个指标常常被误读&#xff0c;很多人把它当作I/O问题的征兆&#xff0c;我自己每隔一段时间就会遇到对…

自由意志不存在?神经科学能证明不?

来源&#xff1a; 神经现实本文经授权摘自《认知科学对当代哲学的挑战》作者&#xff1a;李恒威神经科学能说明自由意志不存在吗?里贝特是人类意识和自由意志的实验研究领域的先驱性神经科学家&#xff0c;但驱使他开展意识的实证研究的根本动因是回应意识科学研究中的本体论问…

MySQL数据库order by 奇慢无比

今天遇到个奇葩的问题&#xff0c; sql 数据量很大 有where 和order by&#xff0c;不加order by 速度很快&#xff0c;加了就很慢 一、首先我们对这条sql执行查询计划&#xff1a; explain select t.order_id from book_order t ORDER BY t.order_id desc explain select t.…

PNAS “深度学习的科学”论文合集导读

来源&#xff1a;混沌巡洋舰今天的科学家对于机器可以学习做什么的想法与我们10年前完全不同。在图像处理、语音和视频处理、机器视觉、自然语言处理和经典的双人游戏中&#xff0c;特别是在过去的十年中&#xff0c;随着在一系列公共组织的挑战问题&#xff08;例如围棋&#…

s3c6410 jpeg编码 linux,S3C6410 裸机硬件JPEG解码

主函数的部分代码/**************************************************************************************************************************函数 : static PIC_ERROR OpenPictureFile(const char *FileName,u8 *buff,u32 FileMaxSize)*功能 : 打开一张…

注解原理

学习spring时&#xff0c;大量使用了注解&#xff0c;但一直对其底层实现机制不得其解&#xff1a; ref&#xff1a;http://www.cnblogs.com/Johness/archive/2013/04/17/3026689.html ref&#xff1a;https://www.jianshu.com/p/28edf5352b63 ref&#xff1a;http://www.cnblo…

C语言中关于字符数组输入,scanf没执行

问题&#xff1a;有时C语言中关于字符数组输入&#xff0c;有的scanf语句没执行。 未执行scanf的代码&#xff1a; #include<stdio.h> int main() {int n;int t0;scanf("%d",&n);char arr[n];int i;for(i0;i<n;i){scanf("%c",&arr[i]);…

清华微电子副所长尹首一:中国AI芯片的技术路线最全面

大数据文摘出品整理&#xff1a;牛婉杨12月1日&#xff0c;GTIC 2020 AI芯片创新峰会在京举办&#xff0c;本次峰会聚集了AI芯片以及各个细分赛道的产、学、研精英人士&#xff0c;共议AI芯片在中国半导体黄金时代的创新与未来。2020年对于AI芯片来说&#xff0c;是充满坎坷的一…

linux socket资源耗尽,TCP的socket资源被耗尽的问题

一、 故障现象部分机顶盒用户出现大面积登录APP时&#xff0c;界面停留在登陆页面&#xff0c;无反应。二、现象初步分析本次问题出现时&#xff0c;所有AAA出现了异常流量波动&#xff0c;在AAA异常流量段期间接到用户故障报障。此时主要表现在LVS集群显示真实的EPG 服务器不停…

演讲实录丨CAAI名誉副理事长杨强教授:人工智能的金融实践

杨强CAAI 名誉副理事长、微众银行首席人工智能官AAAI/ACM/IEEE/CAAI/AAAS Fellow转自中国人工智能学会CAAI原创 丨 作者杨强教授以下是杨强教授的演讲实录&#xff1a;今天的报告主要讲两个内容&#xff0c;一个是人工智能发展&#xff1b;另一个是如何在金融领域落地。刚才已经…