InnoDB的锁机制
Table of Contents

锁的类型

InnoDB 引擎实现了两种行级锁:

意向锁(Intention Lock)

为了方便检测表级锁和行级锁之间的冲突,减少加锁时封锁检查的工作量,支持多粒度的锁定,基于两种基本的锁类型,可以派生出如下两种意向锁:

意向锁主要目的是为了在一个事务中揭示下一行将被请求的锁类型。如果没意向锁,需要表锁时,要一行行检查某个表是否发生了行锁,进而判断能否表锁成功,如果有了意向锁,不用一个个去检查,直接从表的层次就可判断。

注: 1. 意向锁为表级锁,但表示的是事务正在操作某一行记录。 2. 意向锁之间不会发生冲突,冲突检测是在加行锁时发生。

锁的兼容性

. X IX S IS
X 冲突 冲突 冲突 冲突
IX 冲突 兼容 冲突 兼容
S 冲突 冲突 兼容 兼容
IS 冲突 兼容 兼容 兼容

锁的粒度

InnoDB 行锁实现方式

InnoDB 行锁是通过给索引上的索引项加锁来实现的,这一点 MySQL 与 Oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁。

行锁的三种算法

例如,有索引列1,3,5三条记录:

注:

  1. 如果表中没有任何索引,此时InnoDB会自动创建一个隐式聚集索引,并用这个索引来加记录锁。
  2. InnoDB 对唯一索引加 Record Lock,其他情况加 Next-key Lock
  3. Gap Lock中存在一种插入意向锁(Insert Intention Lock),在insert操作时产生。在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。
  4. 在默认innodb是RR隔离级别,并且参数innodb_locks_unsafe_for_binlog被禁止情况下:

    1. 非唯一索引会产生Next-key Lock,即Record Lock + Gap Lock。可防止幻读;
    2. 如果操作(update、delete)的列没有索引,所有纪录加上Next-key Lock,即所有的行记录都被锁住,相当于全表锁,此时相关此表的任何操作都将被阻塞;
    3. 修改事务隔离级别为RC或启用innodb_locks_unsafe_for_binlog参数,InnoDB会触发semi-consistent read,降低锁范围,变成只锁定有影响的记录行。(风险:违反二阶段锁(2PL)原则,导致幻读,不可重复读)

死锁

死锁指两个或以上事务因争夺锁资源造成互相等待。解决方式:

  1. 超时机制:事务等待超时即回滚。
  2. wait-for graph(等待图)进行死锁检测,通过保存锁的信息链表和事务等待链表,然后构造等待图。在每个事务请求锁并发等待的时候都会判断是否存在回路,若存在死锁,InnoDB会回滚undo量最小的事务。

阻塞

阻塞即一个事务的锁需要等待其他事务的锁释放资源。

在InnoDB存储引擎中,参数innodb_lock_wait_timeout用来控制等待的时间(默认是50秒),innodb_rollback_on_timeout用来设定 是否在等待超时时对进行中的事务进行回滚操作(默认是OFF,代表不回滚)。参数innodb_lock_wait_timeout是动态的,可以在MySQ L数据库运行时进行调整。

在默认情况下InnoDB不会回滚超时引发的错误异常,因此用户必须判断是否进行提交或者回滚。但是如果检测到死锁发生,InnoDB会马上回滚事务。