MySQL InnoDB存储引擎(三):锁及事务模型

JerryXia 发表于 , 阅读 (0)
2016 年 09 月 10 日
mysql

对于开发人员而言,对于事务不会陌生。为了避免多线程同时读写共享数据发生竞争的问题,因此,是一种比较常用的方式。而事务的定义,主要体现在其基本特性:原子性一致性隔离性持久性之前的一篇文章有所提到过。但我相信很多开发人员对事务的理解是不够深入的,包括我自己,始终觉得好像很容易回答,又答不出所以然,平常也应该有人问某条SQL会加什么锁呢,为什么我在方法上加了@Transactional就发生死锁了呢?本文将阐述InnoDB存储引擎中的事务模型相关的细节,希望能帮助开发人员了解InnoDB如何有效处理并发事务等机制。

InnoDB锁(InnoDB Locking)

InnoDB中有多种类型的锁,分别适用于不同的场景,如共享/排它锁意向锁记录锁等,下面将逐一对其进行阐述。

共享/排它锁(Shared and Exclusive Locks)

InnoDB实现了两种类型的标准行级锁:共享锁(S)排它锁(X)。下面分别阐述其作用及区别:

共享锁(S):允许持有该锁的事务读取某一行记录。持有共享锁的事务允许其他事务读取该行记录,并且获取到该记录的共享锁,但不允许其他事务对该记录进行更新删除。如下图所示:
innodb-shared-lock-example.png

如图中所示,事务1事务2并发开启事务(操作1,2),并通过语句LOCK IN SHARE MODE对同一行记录请求了共享锁(操作3,4),此时,事务3开启(操作5),并尝试更新同一行记录(操作6),但此时会被事务1事务2持有的共享锁阻塞,直到事务1事务2均提交了事务(操作7,8),事务3的更新操作得以执行,并提交事务(操作9)。

排它锁(X):允许持有该锁的事务更新或删除某一行。持有排它锁的事务不允许其他事务对同一行记录加锁。根据事务隔离级别排它锁将可能阻塞其他对同一行记录进行更新的事务,也可能阻塞其他对同一行记录进行读取的事务。不过InnoDB默认的事务隔离级别REPEATABLE READ,该隔离级别允许较高的并发度,允许其他事务读取已经被持有了排它锁的记录,这就是一致性读。如下图所示:
innodb-exclusive-lock-example.png

如图中所示,事务1并发开启事务(操作1),并通过UPDATE语句请求到了行记录的排它锁(操作2);此时,事务2对同一行记录进行读操作(操作3),由于InnoDB的默认事务隔离级别为REPEATABLE READ,因此该操作得以执行;事务3开启事务(操作4),并尝试修改同一行记录(操作5),但该行记录的排它锁已被事务1获取,因此被阻塞,直到超时重新开启事务,此时,提交事务1,事务3的更新操作(操作6)方可执行。

意向锁(Intention Locks)

InnoDB支持多粒度锁,允许行锁表锁共存,为了支持这种多粒度锁的场景,InnoDB提出了另一种锁--意向锁意向锁属于表级锁,用来表明事务想要在表中的行上获取什么类型的锁(共享独占),不同的事务可以在同一个表上获取不同类型的意向锁,但是第一个事务获取表上的意向排它锁(IX)可以阻塞其他事务获取该表上的任何SX锁。相反,获取表上的意向共享锁(IS)的第一个事务将阻塞其他事务获取表上的任何X锁。两阶段过程允许按顺序解决锁定请求,而不阻塞锁定和对应的兼容操作。InnoDB这样定义了上述两种意向锁:

意向共享锁(IS):事务T尝试在表中某些行上设置共享锁(S),如使用 SELECT ... LOCK IN SHARE MODE
意向排它锁(IX):事务T尝试在表中某些行上设置排它锁(X),如使用SELECT ... FOR UPDATE

意向锁同时需要遵循以下协议:

在事务可以获取表t中的行的S锁之前,它必须首先在t上获取IS或更强的锁
在事务可以获取行上的X锁之前,它必须首先在t上获取IX锁