简识MySQL的InnoDB Locking锁的分类
( 参考官方网页: MySQL :: MySQL 5.7 Reference Manual :: 14.7.1 InnoDB Locking)
一、InnoDB Locking锁的分类:
锁的分类 | 英文 | 缩写 |
---|---|---|
共享锁 | Shared Locks | S |
排他锁 | Exclusive Locks | X |
意向共享锁 | Intention Shared Locks | IS |
意向排他锁 | Intention Exclusive Locks | IX |
记录锁 | Record Locks | - |
间隙锁 | Gap Locks | - |
下一键值锁(临键锁) | Next-Key Locks | - |
插入意向锁 | Insert Intention Locks | - |
自动插入锁(自增锁) | AUTO-INC Locks | - |
以下是对各类锁的具体说明:
- 共享锁(S锁):允许持有共享锁的事务读取一行数据,但不允许修改或删除。在MySQL的InnoDB引擎中,它是行级锁的一种。
- 排他锁(X锁):允许持有排他锁的事务更新或删除一行数据,同时阻止其他事务读取、修改或删除该行数据。在MySQL的InnoDB引擎中,它也是行级锁的一种。
- 意向共享锁(IS锁):表示一个事务打算在表的单独数据行上设置共享锁。它是表级锁,用于锁定表,以表示稍后将对表中的某些行加共享锁。
- 意向排他锁(IX锁):表示一个事务打算在表的单独数据行上设置排他锁。它也是表级锁,用于锁定表,以表示稍后将对表中的某些行加排他锁。
- 记录锁:索引记录上的一个锁,锁住单个行记录。在InnoDB中,即使表上没有定义索引,也会创建一个隐式簇索引,并用该索引进行记录锁定。
- 间隙锁:索引记录之间间隙上的锁,或第一个索引记录之前或最后一个索引记录之后间隙上的锁。间隙锁是“纯粹禁止的”,它们只是阻止其他事务往指定间隙插入数据,而不阻止不同事务在同一个间隙上获取间隙锁。
- 下一键值锁(临键锁):Record Lock和Gap Lock的结合,锁定一个范围,并且锁定记录本身。这种锁在InnoDB中用于防止幻读现象。
- 插入意向锁:间隙锁的一种,专门针对insert操作。当多个事务需要向同一个间隙中插入数据时,插入意向锁允许这些事务并行插入,而不会互相阻塞。
- 自动插入锁(自增锁):一种特殊的表级别锁,专门针对事务插入AUTO_INCREMENT类型的列。当一个事务正在向表中插入值时,其他事务必须等待,以便第一组事务插入的行接收到连续的主键值。
这些锁机制共同构成了InnoDB强大的并发控制功能,使得MySQL数据库能够在高并发环境下稳定运行。
二、InnoDB中部分锁的举例说明:
1. 共享锁(S锁)
共享锁允许多个事务同时读取同一行数据,但不能修改该数据。
示例:
- 事务1开始,并在
id = 1
的行上放置共享锁:SELECT * FROM your_table WHERE id = 1 LOCK IN SHARE MODE;
- 事务2开始,也可以在
id = 1
的行上放置共享锁并查询:SELECT * FROM your_table WHERE id = 1 LOCK IN SHARE MODE;
在这个例子中,两个事务都可以同时对id = 1
的行加共享锁并读取数据。
2. 排他锁(X锁)
排他锁允许事务独占一行数据,其他事务既不能读取也不能修改。
示例:
- 事务1开始,并在
id = 2
的行上放置排他锁:SELECT * FROM your_table WHERE id = 2 FOR UPDATE;
- 事务2开始,尝试在同一行查询(会被阻塞,因为有排他锁):
SELECT * FROM your_table WHERE id = 2;
在这个例子中,事务1在id = 2
的行上放置了排他锁,事务2尝试读取这一行时就会被阻塞,直到事务1提交或回滚。
3. 意向锁(IS锁和IX锁)
意向锁用于表示事务想要在更高级别上获取锁(如表级锁)。意向锁是表级别的锁,不与行级锁冲突。
示例(意向共享锁IS):
- 事务1开始,先在表上放置意向共享锁,表示想要获取表级共享锁:
LOCK TABLE your_table IN SHARE MODE;
- 事务2开始,尝试直接在表上进行独占操作(会被阻塞,因为有表级的意向共享锁):
LOCK TABLE your_table IN EXCLUSIVE MODE;
在这个例子中,事务1先在表上放置了意向共享锁,事务2尝试获取表的排他锁时就会被阻塞。
示例(意向排他锁IX):
- 事务1开始执行
SELECT ... FOR UPDATE
语句,此时会对所查询的记录加排他锁,并先对表加意向排他锁。
4. 记录锁
记录锁用于锁定单个行记录。
示例:
- 假设表
your_table
有字段id
和name
,事务1开始并更新id = 3
的行,会在该行上放置记录锁:UPDATE your_table SET name = 'new_name' WHERE id = 3;
- 事务2开始,尝试更新同一行(会被阻塞,因为有记录锁):
UPDATE your_table SET name = 'another_name' WHERE id = 3;
在这个例子中,事务1在id = 3
的行上放置了记录锁,事务2尝试更新同一行时就会被阻塞。
5. 间隙锁
间隙锁用于锁定索引记录之间的“间隙”,但不锁定索引记录本身。
示例:
- 假设表
your_table
的id
字段是索引,且有值1、3、5。事务1开始,查询id
在1到3之间(不包含1和3)的数据并加锁,会在这个间隙上加间隙锁:SELECT * FROM your_table WHERE id > 1 AND id < 3 FOR UPDATE;
- 事务2开始,尝试插入
id = 2
的数据(会被阻塞,因为有间隙锁):INSERT INTO your_table (id, name) VALUES (2, 'new_entry');
在这个例子中,事务1在1和3之间的间隙上加了间隙锁,事务2尝试插入id = 2
的数据时就会被阻塞。
6. 下一键值锁(临键锁)
临键锁是记录锁和间隙锁的组合,用于锁定一个索引记录及其前面的间隙。
示例:
- 假设表
your_table
的id
字段是索引,且有值1、3、5。事务1开始,查询id = 3
的数据并加锁,会在id = 3
的记录及其前面的间隙上加临键锁:SELECT * FROM your_table WHERE id = 3 FOR UPDATE;
- 事务2开始,尝试插入
id = 2
的数据(会被阻塞,因为有临键锁锁定了id = 3
前面的间隙):INSERT INTO your_table (id, name) VALUES (2, 'new_entry');
- 事务2尝试更新
id = 3
的数据(会被阻塞,因为有临键锁锁定了id = 3
的记录):UPDATE your_table SET name = 'updated_name' WHERE id = 3;
在这个例子中,事务1在id = 3
的记录及其前面的间隙上加了临键锁,事务2尝试插入或更新时都会被阻塞。
7. 插入意向锁
插入意向锁是间隙锁的一种,专门针对insert操作。多个事务在同一个索引、同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。
示例(假设与间隙锁示例中的表结构和数据相同):
- 事务1在1和3之间的间隙上尝试插入
id = 2
的数据(加插入意向锁)。 - 事务2也在1和3之间的间隙上尝试插入
id = 2.5
的数据(加插入意向锁,与事务1不冲突)。
在这个例子中,尽管事务1和事务2都在同一个间隙上尝试插入数据,但由于它们插入的位置不冲突(一个是2,一个是2.5),因此它们可以并行插入而不会互相阻塞。
8. 自动插入锁(自增锁)
自增锁是一种特殊的表级别锁,专门针对事务插入AUTO_INCREMENT类型的列。当一个事务正在向表中插入值时,其他事务必须等待,以便第一组事务插入的行接收到连续的主键值。
示例:
- 假设有数据表
t(id AUTO_INCREMENT, name)
,数据表中有数据:1, shenjian;2, zhangsan;3, lisi。 - 事务A先执行(还未提交):
insert into t(name) values('xxx');
会得到一条(4, xxx)
的记录。 - 事务B后执行:
insert into t(name) values('ooo');
此时需要等待事务A提交后才能继续执行,因为InnoDB在RR隔离级别下使用自增锁来解决幻读问题。
在这个例子中,事务A先执行插入操作并获得了自增锁,因此事务B必须等待事务A提交后才能继续执行插入操作,以确保插入的主键值是连续的。
这些示例展示了InnoDB中各种锁的使用场景和工作方式。在实际应用中,需要根据具体的业务需求和并发控制要求来选择合适的锁类型。
(望各位潘安、各位子健不吝赐教!多多指正!🙏)