MySQL InnoDB引擎的锁机制是怎么一回事?

前言

在开发多用户、数据库驱动的引用时,最大的难点是:一方面要最大程度地利用数据库的并发访问,另一方面还要确保每个用户能以一致的方式读取和修改数据。为此就出现了“锁”机制,同时这也是数据库区别于文件系统的一个关键特性。锁机制用于管理对共享资源的并发访问。InnoDB存储引擎会在行级别上对表数据上锁。不过InnoDB存储殷勤也会在数据库内部其他多个地方使用锁,从而允许对多钟不同资源提供并发访问。例如,操作缓存池中的LRU列表,删除、添加、移动LRU列表中的元素,为了保持一致性,必须有锁的介入。

本文主要深入MySQL的锁机制,用最简洁明了的语言阐述MySQL的锁机制。

正题

什么是InnoDB的锁?

InnoDB锁的对象是什么?

InnoDB锁的对象是事务,作用范围为整个事务。它用来锁定的是数据库中的对象,例如表、页、行。并且一般锁的对象仅在事务commit或者rollback后进行释放(不同事务隔离级别释放的时间可能不同)。

InnoDB锁存放于哪里?

InnoDB锁存放于Lock Manager的哈希表中。

InnoDB死锁什么时候发生?怎么解决?

死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象;InnoDB的死锁通过waits-for graph、time out等方案来解决的。

InnoDB锁有哪几种类型?

InnoDB存储引擎有两种标准的行级锁:

  • 共享锁(S Lock),允许事务读一行数据。
  • 排他锁(X Lock),允许事务删除或更新一行数据。

什么是事务?

因为在InnoDB中锁的对象是事务,下面就先简单了解一下事务的概念以及什么是事务的隔离级别。

事务简单来说:一个Session中进行所有的操作,要么同时成功,要么同时失败。银行转账就是一个事务,要么同时成功,要么同时失败。 数据库事务正确执行的四个基本要素——俗称ACID:

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

一个支持事务(Transaction)中的数据库系统,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易。

什么是隔离级别?

在SQL标准中,定义了四种隔离级别。每一种级别都规定了,在一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离可以执行更高级别的并发,性能好,但是会出现脏读和幻读的现象。首先,我们从两个基础的概念说起。

脏读

事务A修改了某条数据,但是还未提交。此时事务B查询该条已经被修改的数据,那么事务B读取到的就是一条脏读数据。事务B的读取数据依赖事务A,此时如果事务A回滚,则事务B读取到的就是一条错误的脏读数据。(脏读——针对未提交的数据)

不可重复读

如果一个事务在读取某些数据一段时间后,再次读取这些数据的时候,发现读取出来的数据内容已经发生了改变,这种现象称为不可重复读。(不可重复读——读取数据本身的对比)

幻读

一个事务按相同的查询条件查询之前检索过的数据,却发现读取出来的结果集条数发生了改变(增多或减少),原因是因为其他事务对数据的插入或者删除,因此这种现象称为——幻读。(幻读——读取结果集条数的对比)

小结:幻读和不可重复读很容易混淆,在这里幻读针对的是结果集条数的变化,而不可重复读则针对的是单条数据


了解了脏读、幻读和不可重复读之后,就可以谈一下SQL中定义的四种标准隔离界别

  1. READ UNCOMMITTED(未提交读):隔离级别:0.可以读取未提交的记录。会出现脏读。
  2. READ COMMITTED (提交读) :隔离级别:1.事务中只能看到已提交的修改。不可重复读,会出现幻读。(在InnoDB中,会加行锁,但是不会加间隙锁)该隔离级别是大多数数据库系统的默认隔离级别,但是MySQL的则是RR。
  3. REPEATABLE READ (可重复读) :隔离级别:2.在InnoDB中是这样的:RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),因此不存在幻读现象。但是标准的RR只能保证在同一事务中多次读取同样记录的结果是一致的,而无法解决幻读问题。InnoDB的幻读解决是依靠MVCC的实现机制做到的。
  4. SERIALIZABLE (可串行化):隔离级别:3.该隔离级别会在读取的每一行数据上都加上锁,退化为基于锁的并发控制,即LBCC——锁的并发控制(Lock-Based Concurrent Control)

在事务隔离级别READ COMMITTED和REPEATABLE READ下,InnoDB存储引擎使用非锁定的一致性读(MVCC)。

image

MySQL中,查看当前隔离级别使用命令:

select @@tx_isolation;

设置隔离级别,使用命令:

set session transaction isolation level "隔离级别名"

提问:那么对于RC,MySQL是如何解决不可重复读、幻读的呢?对于RR,又是如何解决幻读的呢?

答:对于RR,InnoDB通过一种称为next-key locking的策略来避免幻读现象的产生。

什么是一致性非锁定读和一致性锁定读?

一致性非锁定读

一致性非锁定读是指InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据。对于一致性非锁定读,如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB存储引擎会去读取一个快照数据。如图:
image
对于其他事务正在操作时,被X锁锁住了,由于一致性非锁定读的机制,不会等X锁释放,而是直接去读取一个快照数据——Snapshot data,这样就避免了其他事务被阻塞了。由此可以看到,非锁定读机制极大地提高了数据库的并发性。

在InnoDB存储引擎的默认设置下(REPEATABLE READ),一致性非锁定读是默认的读取方式,即读取不会占用和等待表上的锁。但是,在不同事务隔离级别下,读取的方式是不同的,并不是在每个事务隔离界别下都采用一致性非锁定读。此外,如果该隔离级别下使用的是一致性非锁定读,但是对于快照数据的定义也是不同的。下面简单总结一下在能够使用一致性非锁定读的两种事务隔离级别下的快照数据定义:

  1. REPEATABLE READ:总是读取事务开始时的行数据版本。
  2. READ COMMITED:总是读取被锁定行的最新一份快照数据。

另外啰嗦一句,READ UNCOMMITTED是因为总是读取最新的未提交的数据(脏数据)所以没有快照的概念;而SERIALIZABLE,需要加锁来控制多个事务的发生顺序,所以不符合一致性非锁定读的概念。

对于一行记录可能有不止一个快照数据,一般这种技术被称为行多版本技术。由此带来的并发控制,称之为多版本并发控制(MVCC)。

什么是MVCC?

什么是MVCC?MVCC全称为(Multi-Version Concurrency Control)多版本并发控制。一般情况下,事务性存储引擎(如InnoDB)不是只使用表锁、行锁来处理数据,而还会结合MVCC机制来处理更多的并发问题,因为MVCC处理高并发的效率更高。

给数据库加锁访问,有什么坏处?

要实现数据库的并发访问控制,最简单的就是加锁访问,即读的时候不能写(允许多个现场同时读,共享锁——S锁),写的时候不能读(一次最多只能有一个线程对同一份数据进行写操作,排他锁——X锁)。这样的加锁访问并不是真正的并发,这样的效果虽然起到了并发,但是最终的实现却是读写串行化,可想而知这样大大降低了数据库的读写性能。这种对数据库读写操作加锁的机制,称之为——LBCC,即基于锁的并发控制(Lock-Based Concurrent Control),这是上面所说的四种隔离级别中最高的Serialize隔离级别。因此,为了避免LBCC的低效率,MVCC便应运而生。

几乎所有的RDBMS都支持MVCC。为什么MVCC比LBCC效率更高呢?因为MVCC最大的好处就是——读不加锁、读写不冲突

在InnoDB存储引擎中,undo log的主要作用除了实现MVCC和数据回滚,它是一种逻辑日志,作用只是将数据库逻辑地回滚到原来的样子,所有的修改都被逻辑地取消,但是数据结构和数据页本身在回滚之后与之前可能已经发生的变化,这样做的目的是因为数据库可能会同时存在多个并发事务,他们可能同时修改一个页上的其它数据行,如果因为一个事务的回滚而将数据页回滚到该事务开始时的状态,则会影响其它正在执行的事务。

undo log存在于undo log segments中,undo log segments位在于rollback segments中,而rollback segments则可能存在于系统系统表空间(system tablespace)、临时表空间(temporary tablespace)、撤销表空间(undo tablespaces)中。

小结:简单来说InnoDB的一致性读主要依赖MVCC实现,而MVCC的核心则是依赖undo log来保存事务快照,使得InnoDB在不使用锁的前提下依然能保证事务中数据的一致性,减少了锁的开销,大大提高了查询性能。

详细请参考:https://cloud.tencent.com/developer/news/204224

一致性锁定读

在InnoDB存储引擎中,select操作使用的是一致性非锁定读。但是在某些情况下,用户需要显示地对数据库读操作进行加锁以保证数据逻辑的一直性。而这要求数据库支持加锁语句,即使是对于SELECT的只读操作。InnoDB存储引擎中,对于SELECT语句支持两种一致性的锁定读操作:

  • SELECT…FOR UPDATE
  • SELECT…LOCK IN SHARE MODE

SELECT…FOR UPDATE对读取的行记录加一个X锁,其他事务不能对已锁定的行加上任何锁。SELECT…LOCK IN SHARE MODE对读取的行记录加上一个S锁,其他事务可以向被锁定的行加S锁,但是如果加X锁,则会被阻塞。同样的,SELECT…FOR UPDATE,SELECT…LOCK IN SHARE MODE必须在一个事务中,如果事务提交了,锁也就被释放了。

在MySQL中什么是乐观锁?什么是悲观锁?

乐观锁(Optimistic Lock)和悲观锁(Pessimistic Lock)是两种常见的资源并发锁设计思路,是并发编程中一个非常基础的概念,不仅仅在MySQL数据库中存在。

乐观锁

乐观锁,顾名思义,对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,"乐观"的认为加锁一定会成功的,在最后一步更新数据的时候在进行加锁,乐观锁的实现方式一般为每一条数据加一个版本号。经过上文的分析,可以知道在InnoDB中乐观锁的实现采用的就是MVCC。正因为有了MVCC,才造就了InnoDB的强大的事务处理能力。

悲观锁

正如其名字一样,悲观锁对数据加锁持有一种悲观的态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

在这里,乐观锁和悲观锁就不去深入研究了,想深入研究的读者点这老司机带大家领略MySQL中的乐观锁和悲观锁

参考

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值