MySQL/MariaDB的锁超详细讲解(2)

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表加了锁。而其他会话则可以进行查询,但不能进行更新。

MySQL/MariaDB的锁超详细讲解

当再次使用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/

MySQL/MariaDB的锁超详细讲解

如果设置为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 set

lock 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表发现,这些记录都插入在表的尾部。

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事务。

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/3810f55420a83dd159a316202f796b19.html