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锁。

mysql 排他锁 加锁情况 mysql的间隙锁与排他锁_mysql 排他锁 加锁情况

B连接,试图获取X锁时,被阻塞

mysql 排他锁 加锁情况 mysql的间隙锁与排他锁_存储引擎_02


直到A连接commit后,B连接才能获取X锁。

排他锁(x):又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。可以自行演示,非常简单。

意向锁

InnoDB支持多种粒度锁定,允许行锁和表锁并存。意向锁是表级锁,指示事务稍后对表中的行需要哪种类型的锁(共享锁或排他锁)。有两种类型的意图锁:

  • 意图共享锁(IS)指示一个事务打算设置一个共享 上各个行锁定在表中。
  • 意图独占锁(IX)指示一个事务打算设定各行的排他锁在表中。

意向锁定协议如下:

  • 在事务可以获取表中某行的共享锁之前,它必须首先获取表中的IS锁或更高级别的锁。
  • 在事务可以获取表中某行的排它锁之前,它必须首先获取该表中的IX锁。

表级锁类型的兼容性汇总在以下矩阵中。

mysql 排他锁 加锁情况 mysql的间隙锁与排他锁_mysql_03

记录锁

记录锁定是对索引记录的锁定。例如, 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的锁机制和一致性读策略不同。