按粒度
|
| 右侧是已加的锁(+ 代表兼容, -代表不兼容) | G | R | N | I |
|---|---|---|---|---|
| G | + | + | + | + |
| R | + | – | – | + |
| N | + | – | – | + |
| I | – | + | – | + |
S锁和S锁是完全兼容的,因此在判别兼容性时不需要对比精确模式。精确模式的检测,用在S、X和X、X之间。从这个矩阵可以看到几个特点:
只有正确通过索引条件检索数据(没有索引失效的情况),InnoDB才会使用行级锁,否则InnoDB对表中的所有记录加锁,也就是将锁住整个表。注意,这里说的是锁住整个表,但是Innodb并不是使用表锁来锁住表的,而是使用了下面介绍的Next-Key Lock来锁住整个表。网上很多的说法都是说用表锁,然而实际上并不是,我们可以通过下面的例子来看看。
假设我们有以下的数据(MySQL8):
mysql> select * from users; +----+------+-----+ | id | name | age | +----+------+-----+ | 1 | a | 1 | | 2 | a | 1 | | 3 | a | 1 | | 4 | a | 1 | | 5 | a | 1 | +----+------+-----+
方法一:
我们使用表锁锁表,并查看引擎的状态
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> lock tables users write; Query OK, 0 rows affected (0.00 sec) mysql> show engine innodb status\G ... ------------ TRANSACTIONS ------------ Trx id counter 4863 Purge done for trx's n:o < 4862 undo n:o < 0 state: running but idle History list length 911 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 281479760456232, not started mysql tables in use 1, locked 1 ###############注意这里 0 lock struct(s), heap size 1136, 0 row lock(s) ...
然后我们再通过非索引的字段查询来加锁,并查看引擎的状态
## 先解锁上次的表锁 mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from users where name = 'a' for update; mysql> show engine innodb status\G ... ------------ TRANSACTIONS ------------ Trx id counter 4864 Purge done for trx's n:o < 4862 undo n:o < 0 state: running but idle History list length 911 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 4863, ACTIVE 37 sec 2 lock struct(s), heap size 1136, 6 row lock(s) ###############注意这里 ...
然后我们再删除id为2,3,4的数据,然后在通过非索引的字段查询来加锁,并查看引擎的状态
mysql> delete from users where id in (2,3,4); Query OK, 3 rows affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from users where name = 'a' for update; mysql> show engine innodb status\G ... ------------ TRANSACTIONS ------------ Trx id counter 4870 Purge done for trx's n:o < 4869 undo n:o < 0 state: running but idle History list length 914 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 4869, ACTIVE 9 sec 2 lock struct(s), heap size 1136, 3 row lock(s) ###############注意这里 ...
可以看到这里使用了表锁和因为没法用索引锁定特定行而转而锁住整个表是不一样的。从第二次和第三次的操作来看,lock住的row也是不同的,这是因为两者间隙的个数不同,所以可以看到使用的并不是表锁,而是Next-Key Lock。第一次锁住了(-∞,1],(1,2],(2,3],(3,4],(4,5],(5,∞],第二次锁住了(-∞,1],(1,5],(5,∞]。
方法二:
也可以通过以下语句来查看锁的信息,也可以知道用的是行锁,且是锁住了区间(插入不了数据)和记录,所以是Next-Key Lock。
mysql> select ENGINE_TRANSACTION_ID,LOCK_TYPE,LOCK_MODE from performance_schema.data_locks where ENGINE_TRANSACTION_ID in (你的事务id); +-----------------------+-----------+-----------+ | ENGINE_TRANSACTION_ID | LOCK_TYPE | LOCK_MODE | +-----------------------+-----------+-----------+ | 4889 | TABLE | IX | | 4889 | RECORD | X | | 4889 | RECORD | X | | 4889 | RECORD | X | +-----------------------+-----------+-----------+ 10 rows in set (0.00 sec)
LOCK_TYPE:对于InnoDB,可选值为 RECORD(行锁), TABLE(表锁)
LOCK_MODE:对于InnoDB,可选值为S[,GAP], X[,GAP], IS[,GAP],IX[,GAP], AUTO_INC和UNKNOWN。除了AUTO_INC和UNKNOWN,其他锁定模式都包含了GAP锁(如果存在)。
具体可见 MySQL文档:https://dev.mysql.com/doc/ref...
直接对整个表加锁,影响表中所有记录,表读锁和表写锁的兼容性见上面的分析。
MySQL中除了表读锁和表写锁之外,还存在一种特殊的表锁:意向锁,这是为了解决不同粒度的锁的兼容性判断而存在的。
因为锁的粒度不同,表锁的范围覆盖了行锁的范围,所以表锁和行锁会产生冲突,例如事务A对表中某一行数据加了行锁,然后事务B想加表锁,正常来说是应该要冲突的。如果只有行锁的话,要判断是否冲突就得遍历每一行数据了,这样的效率实在不高,因此我们就有了意向表锁。
意向锁的主要目的是为了使得 行锁 和 表锁 共存,事务在申请行锁前,必须先申请表的意向锁,成功后再申请行锁。注意:申请意向锁的动作是数据库完成的,不需要开发者来申请。
意向锁是表级锁,但是却表示事务正在读或写某一行记录,而不是整个表, 所以意向锁之间不会产生冲突,真正的冲突在加行锁时检查。
意向锁分为意向读锁(IS)和意向写锁(IX)。
| 右侧是已加的锁(+ 代表兼容, -代表不兼容) | IS | IX | S | X |
|---|---|---|---|---|
| IS | + | + | + | – |
| IX | + | + | – | – |
| S | + | – | + | – |
| X | – | – | – | – |
以上就是MySQL 锁的相关知识总结的详细内容,更多关于MySQL 锁的资料请关注社区其它相关文章!