独占锁和所有的锁都冲突,意向共享锁和共享锁兼容(这是肯定的),还和意向独占锁兼容。所以加了意向共享锁的时候,可以修改行级非共享锁的记录。同理,加了意向独占锁的时候,可以检索这些加了独占锁的记录。
MariaDB/MySQL中myisam和innodb都支持表级锁。表级锁分为两种:读锁(read lock)和写锁(write lock)。本节所述均为myisam支持的,同样innodb也一样支持。
可以通过语句来实现表级锁的锁定和解锁,这些语句的操作环境是当前客户端会话(即作用范围是会话)。锁表的时候可以一次性锁定多张表,并使用不同的锁,而解锁的时候只能一次性解锁当前客户端会话的所有表。
lock tables命令可以锁表或锁视图,锁视图的时候会自动将视图内的基表加上对应类型的锁。由于MariaDB/MySQL中触发器是基于表的,所以lock tables锁定表的时候,触发器内使用的表也都会被锁定。
例如:table1上有一个如下触发器:
如果为table1加上写锁,则table2、table3都会加上写锁,而table4会加上读锁。
lock tables命令会隐式释放当前客户端会话中之前的所有锁。
现在创建3张表作为测试表。
给t1加上读锁。
此时当前会话将无法操作t1以外的任何表,连查询也不允许,因为只有t1表加了锁。而其他会话则可以进行查询,但不能进行更新。
当再次使用lock tables命令的时候,会先释放当前会话之前所有的锁,再对lock tables命令中的表申请锁。
例如,上面会话1锁了表t1,此时无法操作t2表。现在对t2表lock table。
此时就可以操作t2表而不能操作t1表了,因为对t1表的锁已经释放了。
使用lock tables给表加读锁的时候,还有一个选项local,该选项表示对当前现有的记录加上锁,不影响其他会话的插入记录语句。但是否真的能插入,由变量concurrent_insert决定,该变量默认值为auto。关于并发插入,见我翻译的官方手册:https://mariadb.com/kb/zh-cn/concurrent-inserts/。
如果设置为2,那么对myisam表的并发插入有一定提升。
现在测试默认的情况,即 concurrent_insert=auto
在另一个会话中插入一条记录,这是允许的操作。当然,在锁表的会话中肯定是不能插入的。
解锁,并删除中间的两条记录,形成空洞。然后再锁定表。
在其他会话中插入记录。会发现被阻塞。当表解锁后立即成功插入。
将concurrent_insert设置为2,即always,此时不管是否有空洞都允许向myisam表尾部插入。
此时发现能够正常插入,且查询t1表发现,这些记录都插入在表的尾部。
默认情况下,使用表级锁的存储引擎中(所以innodb不支持),写锁的优先级高于读锁。这意味着,当表上已经有一个写锁的时候,后续的写操作、读操作都会队列化,且队列中的写操作总是在读操作之前执行,即使写操作比读操作后到达MySQL/MariaDB服务器。可以改变这种优先级。详细内容见:。
innodb支持行级锁,也是在允许的情况下默认申请的锁。
SQL Server中的锁是一种稀有资源,且会在需要的时候锁升级,所以锁越多性能越差。而MariaDB/MySQL中的锁不是稀有资源,不会进行锁升级,因此锁的多少不会影响性能,1个锁和1000000个锁性能是一样的(不考虑锁占用的内存),锁的多少只会影响并发性。
现在人为造成一个锁等待。
会话1执行:
会话2执行:
此时会话2被阻塞,进入锁等待状态。
要查看锁信息。有几种方法:
1.通过show engine innodb status来查看,其中的transactions片段可以看到事务,其中包括锁等待。
以下是没有激活任何事务的信息:
三个"---TRANSACTION"表示当前开启了3个mysql会话,但这3个会话都没有任何事务。
以下是某会话开启一个事务,但没有任何锁等待的事务信息:
不难看出,这个事务是一个需要写日志的DML事务。
以下是有锁等待的事务信息:
从上面的结果可以看到锁等待的信息。
"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" />