行锁也称为记录锁,顾名思义,就是锁住某一行(某条记录row)。需要的注意的事,MySQL服务器层并没有实行行锁机制,行级锁只在储存引擎实现。
优点:锁的力度小,发生锁冲突概率低,可以实现高并发。
缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁情况。
InnoDB与MySAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。
记录锁也就是仅仅把一条记录锁上,官方的类型名称为: LOCK_ REC NOT_ GAP。比如我们把id值为8的那条记录加一个记录锁的示意图如图所示。仅仅是锁住了id值为8的记录,对周围的数据没有影响。
●当一个事务获取了一条记录的S型记录锁后,其他事务也可以继续获取该记录的s型记录锁,但不可以继续获取x型记录锁;
●当一个事务获取了一条记录的x型记录锁后,其他事务既不可以继续获取该记录的s型记录锁,也不可以继续获取x型记录锁。
MySQL在可重复读 隔离级别下是可以解决幻读问题的,解决方案有两种,可以使用MVCC方案解决,也可以采用加锁方案解决。但是在使用加锁方案解决时有个大问题,就是事务在第一次执行读取操作时,那些幻影记录尚不存在,我们无法给这些幻影记录加上记录锁。InnoDB提出了一种称之为Gap Locks 的锁,官方的类型名称为: LOCK _GAP ,我们可以简称为gap锁。比如,把id值为8的那条记录加 一个gap锁的示意图如下。
图中id值为8的记录加了gap锁,意味着不允许别的事务在id值为8的记录前边的间隙插入新记录,其实就是id列的值(3, 8)这个区间的新记录是不允许立即插入的。比如,有另外一一个事务再想插入一条id值为4的新记录,它定位到该条新记录的下一条记录的id值为8, 而这条记录上又有一个gap锁,所以就会阻塞插入操作,直到拥有这个gap锁的事务提交了之后,id列的值在区间(3, 8)中的新记录才可以被插入。
gap锁的提出仅仅是为了防止插入幻影记录而提出的。虽然有共享gap锁和独占gap锁这样的说法,但是它们起到的作用是相同的。而且如果对一条记录加了gap锁(不论是共享gap锁还是独占gap锁),并不会限制其他事务对这条记录加记录锁或者继续加gap锁。
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环。死锁示例:
有时候我们既想锁住某条记录,又想阻止其他事务在该记录前边的间隙插入新记录,所以InnoDB就提出了一种称之为Next-Key Locks 的锁,官方的类型名称为: LOCK. ORDINARY ,我们也可以简称为next-key锁。Next-Key-Locks是在存储引擎innodb、事务级别在可重复读的情况下使用的数据库锁,innodb默认的锁就是Next-Key-locks。比如,我们把id值为8的那条记录加一个next-key锁的示意图如下:
next-key锁的本质就是一个记录锁和一个gap锁的合体,它既能保护该条记录,又能阻止别的事务将新记录插入被保护记录前边的间隙。
我们说一个事务在插入一条记录时需要判断- - 下插入位置是不是被别的事务加了gap锁( next-key锁也包含gap锁),如果有的话,插入操作需要等待,直到拥有gap锁的那个事务提交。但是** InnoDB规定事务在等待的时候也需要在内存中生成一个锁结构**, 表明有事务想在某个间隙中插入新记录,但是现在在等待。InnoDB就把这种类型的锁命名为Insert Intention Locks,官方的类型名称为: LOCK_ INSERT_ INTENTION,我们称为插入意向锁。插入意向锁是一种Gap锁,不是意向锁,在inser操作时产生。
插入意向锁是在插入一条记录行前,由INSERT操作产生的一种间隙锁。该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。假设存在两条值分别为4和7的记录,两个不同的事务分别试图插入值为5和6的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4, 7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间并不会产生冲突(阻塞等待)。
总结来说,插入意向锁的特性可以分成两部分:
(1)插入意向锁是一种特殊的间隙锁- --间隙锁可以锁定开区间内的部分记录。
(2)插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待。
注意,虽然插入意向锁中含有意向锁三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁是表锁而插入意向锁是行锁。
比如,把id值为8的那条记录加一一个插入意向锁的示意图如下:
从图中可以看到,由于T1持有gap锁,所以T2和T3需要生成一个插入意向锁的锁结构并且处于等待状态。当T1提交后会把它获取到的锁都释放掉,这样T2和T3就能获取到对应的插入意向锁了(本质上就是把插入意向锁对应锁结构的is. _waiting属性改为false) , T2和T3之间也并不会相互阻塞,它们可以同时获取到id值为8的插入意向锁,然后执行插入操作。事实上插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁。