InnoDB的机制和Oracle有不少相近的地方,但是两者的实现却是截然不同的。总的来说,Oracle锁定数据主要是通过在须锁定的某行记录所在的物理block上的事务槽上面添加锁定信息,而InnoDB的锁定则是通过在指向数据激励的第一个索引键之前和最后一个索引键之后的空域空间标记锁定信息实现的。InnoDB的这种锁定实现方式被称为“next-key locking”(间隙锁),因为Qusery在执行过程中通过范围查找,它会锁定整个范围内所有的索引键值,即使整个键值并不存在。
间隙锁有一个比较致命的弱点,就是在锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,这会造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下,这可能会对性能造成很大的危害。而InnoDB给出的解释是为了阻止幻读的出现,所以他们选择了间隙锁。
除了间隙锁给InnoDB带来性能的负面影响,通过索引实现锁定的方式还存在其他几个较大的性能隐患:
- 当Query无法利用索引的时候,InnoDB会放弃使用行级锁定而改用表级别的锁定,造成并发性能的降低;
- 当Query使用的索引并不包含所有过滤条件时,数据检索使用到的索引键中的数据可能有部分并不属于该Query的结果集行列,但是也会被锁定,因为间隙锁锁定的是一个范围,而不是具体的索引键;
- 当Query在使用索引定为数据的时候,如果使用的索引键一样但访问的数据行不同(索引指示过滤条件的一部分),它们一样会被锁定。
在InnoDB的事务管理和锁定机制中,有专门检测死锁的机制会在系统中产生死锁之后的很短时间内就检测到该死锁的存在。当InnoDB检测到系统中产生了死锁,InnoDB会通过相应的判断来选择产生死锁的两个事务中较小的那个来回滚,让另外一个较大的事务成功完成。实际上,当InnoDB发现死锁时,会计算出两个事务各自插入、更新或删除的数据量,从而判断两个事务的大小。但是有一点须要注意的就是,当产生死锁的场景中不止涉及InnoDB存储引擎的时候,InnoDB是没有办法检测到该死锁的,这时候只能通过锁定超时限制来解决该死锁。