InnoDB存储引擎实现了如下两种标准的行级锁:
- 共享锁(S Lock),允许事物度一行数据。
- 排他锁(X Lock),允许事物删除或更新一行数据。
如果一个事物T1已经获得了行 r 的共享锁,那么另外的事物T2可以立即获得r的共享锁,因为读取并没有改变行 r 的数据,称这种情况为锁的兼容(Lock Compatible)。但若有其他的事物T3想要获得行 r 的排他锁,则其必须等待事物 T1、T2释放行r上的共享锁----这种情况成为锁的不兼容
从表6-3可以发现X锁与任何的锁都不兼容,而S锁仅和S锁兼容。需要特别注意的是,S和X锁都是行锁,兼容是指对同一记录(row)锁的兼容性情况。
此外,InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention Lock)。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁,如图6-3所示。若将上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先需要对粗粒度的对象上锁。例如图6-3,如果需要对页上的记录r进行上X锁,那么分别需要对数据库A、表、页上意向锁IX,最后对记录r上X锁。若其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。举例来说,在对记录r加X锁之前,已经有事务对表1进行了S表锁,那么表1上已存在S锁,之后事务需要对记录r在表1上加上IX,由于不兼容,所以该事务需要等待表锁操作的完成。
InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:
1)意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁
2)意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁
由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。故表级意向锁与行级锁的兼容性如表6-4所示。一致性非锁定读
一致性的非锁定读(consistent nolocking read)是指InnoDB存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行DELETE或UPDATE操作,这是读取操作不会青瓷去等待行上锁的释放。相反的,InnoDB存储引擎会去读取行的一个快照数据
在事务隔离级别READ COMMITTED和REPEATABLE READ(InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。来看下面的一个例子,首先在当前MySQL数据库的连接会话A中执行如下SQL语句:
一致性锁定读
InnoDB存储引擎对于SELECT语句支持二中一直性的锁定读(Locking read)操作:
*SELECT ... FOR UPDATE
*SELECT ... LOCK IN SHARE MODE
SELECT…FOR UPDATE对读取的行记录加一个X锁,其他事务不能对已锁定的行加上任何锁。SELECT…LOCK IN SHARE MODE对读取的行记录加一个S锁,其他事务可以向被锁定的行加S锁,但是如果加X锁,则会被阻塞。
外键和锁
在InnoDB存储引擎中,对于一个外键列,如果没有显示的对这个列加索引,InnoDB存储引擎自动对其加一个索引---因为这样可以避免表锁--这比Oracle数据库做得好,Oracle数据库不会自动的添加索引,用户必须自己手动添加,这也导致了Oracle数据库中肯产生死锁。
参考文献:Mysql技术内幕:InnoDB存储引擎