MySQL中的锁
MySQL中不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking),InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。
InnoDB锁
本文主要探讨InnoDB存储引擎中的锁机制,大致分文如下几种锁类型:
- 共享锁和排它锁(Shared and Exclusive Locks)
- 意向锁(Intention Locks)
- 记录锁(Record Locks)
- 间隙锁(Gap Locks)
共享锁和排他锁
InnoDB实现标准的行级锁定,其中有两种类型的锁: 共享(S)锁和排他(X)锁。
共享锁(s):又称读锁,允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。若事务T对数据对象A加上S锁,则其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
A连接,开启事务,加S锁。
B连接,试图获取X锁时,被阻塞
直到A连接commit后,B连接才能获取X锁。
排他锁(x):又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。可以自行演示,非常简单。
意向锁
InnoDB支持多种粒度锁定,允许行锁和表锁并存。意向锁是表级锁,指示事务稍后对表中的行需要哪种类型的锁(共享锁或排他锁)。有两种类型的意图锁:
-
意图共享锁(IS)指示一个事务打算设置一个共享 上各个行锁定在表中。
-
意图独占锁(IX)指示一个事务打算设定各行的排他锁在表中。
意向锁定协议如下:
-
在事务可以获取表中某行的共享锁之前,它必须首先获取表中的IS锁或更高级别的锁。
-
在事务可以获取表中某行的排它锁之前,它必须首先获取该表中的IX锁。
表级锁类型的兼容性汇总在以下矩阵中。
记录锁
记录锁定是对索引记录的锁定。例如, select * from table_lock_demo where id = 1 for update; 可以防止任务其他事务插入,更新或删除id=1的行记录。
记录锁总是锁定索引记录,即使表没有定义索引。对于这种情况,InnoDB创建一个隐藏的聚集索引并使用这个索引来锁定记录。
间隙锁
间隙锁定是对索引记录之间的间隙的锁定,或者是对第一个或最后一个索引记录之前的间隙的锁定。
如果执行如下语句,age上有一个普通索引。
select age from table_lock_demo where age BETWEEN 1 and 15 FOR UPDATE;
那么可以防止其他事务对age为1到15之间的数据进行修改、删除、插入。
测试的发现如果查询的是 select * from…则间隙锁不起作用,会锁表。
注意事项
- InnoDB的行锁是基于索引实现的,如果不通过索引访问数据,InnoDB会使用表锁。
- 在不同的隔离级别下,InnoDB的锁机制和一致性读策略不同。