InnoDB 6 锁
锁机制用于管理对共享资源的并发访问。InnoDB引擎会在行级别上对表数据上锁,也会在数据库内部其它多个地方使用锁,从而允许对多种不同资源提供并发访问。
例如,操作缓冲池的LRU列表,删除、添加、移动LRU列表中的元素,为了保证一致性,必须有锁的介入。
lock与latch:
latch一般称为闩锁(即轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。latch分为mutex互斥量和rwlock读写锁。用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。作用于线程。
lock的对象是事务,用来锁定数据库中的对象,如表、页、行。一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。lock有死锁机制。
InnoDB存储引擎中的锁:
锁的类型:
共享锁S:允许事务读一行数据。
排它锁X:允许事务删除或更新一行数据。
InnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行枷锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。
意向锁也分为:意向共享锁IS、意向排它锁IX。
一致性非锁定读:
一致性的非锁定读是指InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据。
读数据时不需要等待行上排它锁的释放,直接读取该行的快照数据。快照数据其实就是当前行数据之前的历史版本,每行记录可能有多个版本,
在事务隔离级别“读已提交”和“可重复读”下,InnoDB存储引擎使用非锁定的一致性读。
一致性锁定读:
InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读操作:
SELECT.....FOR UPDATE
SELECT.....LOCK IN SHARE MODE
这里的FOR UPDATE指为读取的行加上一个X锁。LOCK IN SHARE MODE加上一个S锁。
对于一致性非锁定读,即使读取的行已被执行了SELECT...FOR UPDATE,也是可以进行读取的。
自增长与锁:
使用auto_increment依靠自增长计数器值加1赋予自增长列。通过AUTO-INC Locking实现。这种锁是一种特殊的表锁机制,为了提高插入的性能,锁不是在一个事务完成后才释放,而是在完成对自增长值插入的SQL语句后立即释放。
外键和锁:
外键主要用于引用完整性的约束检查。InnoDB引擎中如果没有对外键列进行索引,则会自动加上一个索引,因为这样可以避免表锁。
对于外键值的插入或更新,首先需要查询父表中的记录,即SELECT父表。
锁的算法:
行锁的3种算法:
Record Lock:单个行记录上的锁
Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。
Phantom Problem:
在默认的事务隔离级别下,即REPEATABLE READ下,InnoDB引擎采用Next-Key Lock机制来避免Phantom Problem(幻象问题)。
该问题是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行。
锁问题:
脏读:dirty read。是指事务对缓冲池中行记录的修改,并且还没有被提交。
如果读到了脏数据,即一个事务读到另一个事务中未提交的数据,则显然违反了数据库的隔离性。
不可重复读:是指在一个事务内多次读取同一数据库集合。在这个事务还没有结束时,另一个事务也访问该同一数据集合,并做了一些DML操作。
脏读是读到未提交的数据;不可重复读是读到已提交的数据,但一个事务可能有多条语句,另一个事务读到该事务前一个SQL语句提交的内容,对该事务后续语句来说就违反了隔离性。
丢失更新:一个事务的更新操作会被另一个事务的更新操作所覆盖,从而导致数据的不一致。
阻塞:
由于锁的兼容关系,一个事务中的锁可能需要等待另一个事务中的锁释放它占用的资源,这就是阻塞。
死锁:
是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。
解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
除了超时机制,当前数据库都普遍采用wait-for graph(等待图)的方式来进行死锁检测。这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息:
锁的信息链表;事务等待链表。
通过上述等待链表可以构造出一张图,而在这个图中若存在回路,就代表存在死锁,因此资源间相互发生等待。
在wait-for graph中,事务为图中的节点。而在图中,事务T1指向T2边的定义为:
事务T1等待事务T2所占用的资源;
事务T1最终等待T2所占用的资源,也就是事务之间在等待相同的资源,而事务T1发生在事务T2的后面。
锁升级:
是指将当前锁的粒度降低。如将页锁升级为表锁。