6.1 InnoDB存储引擎之锁(什么是锁)

消除关于行级锁的一个"神话"∶人们认为行级锁总会增加开销。实际上,只有当实现本身会增加开销时,行级锁才会增加开销。InnoDB存储引擎不需要锁升级,因为一个锁和多个锁的开销是相同的。

6.1 什么是锁

  • 锁是数据库系统区别于文件系统的一个关键特性。锁机制用于管理对共享资源的并发访问。InnoDB存储引擎会在行级别上对表数据上锁,这固然不错。不过 InnoDB存储引擎也会在数据库内部其他多个地方使用锁,从而允许对多种不同资源提供并发访问。例如,操作缓冲池中的LRU列表,删除、添加、移动 LRU列表中的元素。为了保证一致性,必须有锁的介入。数据库系统使用锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性
  • 对于MyISAM引擎,其锁是表锁设计。并发情况下的读没有问题,但是并发插入时的性能就要差一些了,若插入是在"底部",MyISAM存储引擎还是可以有一定的并发写入操作。
  • InnoDB存储引擎锁的实现和 Oracle 数据库非常类似,提供一致性的非锁定读、行级锁支持。行级锁没有相关额外的开销,并可以同时得到并发性和一致性。

6.2 lock 与 latch

  • 这里还要区分锁中容易令人混淆的概念lock与latch。在数据库中,lock 与latch都可以被称为"锁"。但是两者有着截然不同的含义,本章主要关注的是 lock。
  • latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,latch 又可以分为 mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且, 通常没有死锁检测的机制。
  • lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般 lock 的对象仅在事务 commit 或 rollback 后进行释放(不同事务隔离级别释放的时间可能不同)。此外,lock,正如在大多数数据库中一样,是有死锁机制的。表6-1 显示了lock 与latch 的不同。
    在这里插入图片描述
    对于InnoDB存储引擎中的 latch,可以通过命令 SHOW ENGINE INNODB MUTEX来进行查看。
    在这里插入图片描述

在这里插入图片描述

  • 通过上述的例子可以看出,列Type显示的总是 InnoDB,列Name 显示的是latch 的信息以及所在源码的位置(行数)。列Status 比较复杂,在Debug模式下,除了显示os_waits,还会显示 count、spin_waits、spin_rounds、os_yields、os_wait_times等信息。其具体含义见表6-2。

在这里插入图片描述

  • 相对于latch 的查看,lock信息就显得直观多了。用户可以通过命令 SHOW ENGINE INNODB STATUS 及 information_schema架构下的表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS来观察锁的信息。这将在下节中进行详细的介绍。

6.3 InnoDB 存储引擎中的锁

6.3.1 锁的类型

InnoDB存储引擎实现了如下两种标准的行级锁∶

  1. 共享锁(S Lock),允许事务读一行数据。
  2. 排他锁(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所示。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 在InnoDB 1.0版本之前,用户只能通过命令 SHOW FULL PROCESSLiST,SHOW ENGINE INNODB STATUS等来查看当前数据库中锁的请求,然后再判断事务锁的情况。从 InnoDB1.0开始,在 INFORMATION_SCHEMA 架构下添加了表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS。通过这三张表,用户可以更简单地监控当前事务并分析可能存在的锁问题。我们将通过具体的示例来分析这三张表,在之前,首先了来看表6-5中表INNODB_TRX的定义,其由8个字段组成。
    在这里插入图片描述
    在这里插入图片描述

  • 通过列 state 可以观察到 trx_id为730FEE 的事务当前正在运行,而 trx id为7311F4的事务目前处于"LOCK WAIT"状态,且运行的 SOL语句是 select * from parent lock in share mode。该表只是显示了当前运行的InnoDB事务,并不能直接判断锁的一些情况。如果需要查看锁,则还需要访问表 INNODB LOCKS,该表的字段组成如表6-6 所示。
    在这里插入图片描述
    在这里插入图片描述

  • 这次用户可以清晰地看到当前锁的信息。trx_id为 730FEE 的事务向表parent 加一个X的行锁,ID为7311F4的事务向表 parent 申请了一个S的行锁。lock_data都是1,申请相同的资源,因此会有等待。这也可以解释INNODB TRX中为什么一个事务的trx_state是"RUNNING",另一个是"LOCK WAIT"了。

  • 另外需要特别注意的是,我发现lock_data 这个值并非是"可信"的值。例如当用户运行一个范围查找时,lock_data 可能只返回第一行的主键值。与此同时,如果当前资源被锁住了,若锁住的页因为 InnoDB存储引擎缓冲池的容量,导致该页从缓冲池中被刷出,则查看INNODB LOCKS表时,该值同样会显示为NULL,即 InnoDB存储引擎不会从磁盘进行再一次的查找。

  • 在通过表INNODB LOCKS查看了每张表上锁的情况后,用户就可以来判断由此引发的等待情况了。当事务较小时,用户就可以人为地、直观地进行判断了。但是当事务量非常大,其中锁和等待也时常发生,这个时候就不这么容易判断。但是通过表INNODB_LOCK_WAITS,可以很直观地反映当前事务的等待。表INNODB_LOCK_WAITS由 4个字段组成,如表6-7所示。

  • 在这里插入图片描述
    通过上述的 SOL语句,用户可以清楚直观地看到哪个事务阻塞了另一个事务。
    当然,这里只给出了事务和锁的ID。如果需要,用户可以根据表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS得到更为直观的详细信息。例如,用户可以执行如下联合查询∶
    在这里插入图片描述

6.3.2 一致性非锁定读

  • 一致性的非锁定读(consistent nonlocking read)是指 InnoDB存储引擎通过行多版本控制(multi versioning)的方而SQL式来读取当前执行时间数据库中Query行的数据。如果读取的行正在执行DELETE 或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB存储引擎会去读取行的一个快照数据。如图6-4所示。
  • 图6-4直观地展现了InnoDB 存储引擎一致性的非锁定读。之所以称其为非锁定读,因为不需要等待访问的行上X锁的释放。
    在这里插入图片描述
    快照数据是指该行的之前版本的数据,该实现是通过 undo 段来完成。而 undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。此外,读取快照数据是不需要上锁的,因为没有事务需要对历史的数据进行修改操作。
    可以看到,非锁定读机制极大地提高了数据库的并发性。在 InnoDB存储引擎的默认设置下,这是默认的读取方式,即读取不会占用和等待表上的锁。但是在不同事务隔离级别下,读取的方式不同,并不是在每个事务隔离级别下都是采用非锁定的一致性读。此外,即使都是使用非锁定的一致性读,但是对于快照数据的定义也各不相同。
    通过图6-4可以知道,快照数据其实就是当前行数据之前的历史版本,每行记录可能有多个版本。就图6-4所显示的,一个行记录可能有不止一个快照数据,一般称这种技术为行多版本技术。由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control, MVCC)。
    在事务隔离级别READ COMMITTED和 REPEATABLE READ(InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在 READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在 REPEATABLE READ事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。来看下面的一个例子,首先在当前MySQL数据库的连接会话A中执行如下 SQL 语句∶
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值