MySQL InnoDB存储引擎之锁

概念:

锁是用来管理对共享文件的并发访问。innodb会在行级别上对数据库上锁。不过innodb存储引擎会在数据库内部其他多个地方使用锁,从而允许对不同资源提供并发访问。例如操作缓冲池中的LRU列表,删除,添加,移动LRU列表中的元素,为了保证一致性,必须有锁的介入。MyISAM引擎是表锁,而InnoDB提供一致性的非锁定读、行级锁,且行级锁没有相关额外的开销。

table-level locking(表级锁)

整个表被客户锁定。根据锁定的类型,其他客户不能向表中插入记录,甚至从中读数据也受到限制MyISAM、MEMORY默认锁级别,个别时候,InnoDB也会升级为表级锁

row-level locking(行级锁)

只有线程当前使用的行被锁定,其他行对于其他线程都是可用的InnoDB默认行级锁。是基于索引数据结构来实现的,而不是像ORACLE的锁,是基于block的。InnoDB也会升级为表级锁,全表/全索引更新,请求autoinc锁等

page-level locking(页级锁)

锁定表中某些行集合(称做页),被锁定的行只对锁定最初的线程是可行。如果另外一个线程想要向这些行写数据,它必须等到锁被释放。不过其他页的行仍然可以使用BDB默认页级锁

lock与latch

latch称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。latch可以通过命令show engine innodb mutex来进行查看。如图:

由上图可以看出列Type显示的总是InnoDB,列Name显示latch的信息以及所在源码的行数,列Status中显示的os_waits表示操作系统等待的次数。

lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或者rollback后释放(不同事务隔离级别释放的时间可能不一样)。有死锁机制。二则的区别如下:

特点:

InnoDB是通过对索引上的索引项加锁来实现行锁。这种特点也就意味着,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

锁的类型:

有两种标准的行级锁:

共享锁(S lock):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁.SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

排它锁(X lock):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他锁.SELECT * FROM table_name WHERE ... FOR UPDATE

InnoDB存储引擎支持意向锁且设计比较简练,分为两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。(意向锁是InnoDB自动加的)

意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁.

意向独占锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁.

表级意向锁与行级锁的兼容情况如下图:

锁的查看

在InnoDB1.0版本之前只能通过show engine innodb status(transactions行中查看) 或者 show full processlist来查看当前库中锁的请求。但是在这之后在information_schema架构下新增innodb_trx、innodb_locks和innodb_lock_waits三张表记录当前库中锁的情况。

三个表的字段说明如下图

一致性非锁定读(consistent nonlocking read)

一致性的非锁定读是指InnoDB存储引擎通过行多版本控制(multi_versioning)的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行delete或者update操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB存储引擎会去读取行的一个快照数据(当前行数据的历史版本)。快照数据是指该行的之前版本的数据,该实现是通过undo段来完成。而undo用来在事务回滚数据,因此快照数据本身是没有额外开销。而且,读取快照数据是不需要上锁的。一致性非锁定读是InnoDB存储引擎的默认读取方式(在读取不会占用和等待表上的锁)。但是在不同事务隔离级别下,读取的方式不同,并不是在每个事务隔离级别下都是采用非锁定的一致性读。即使都是使用非锁定的一致性读,但是对于快照数据的定义格式也各不相同。在事务隔离级别READ
COMMITTED(RC)和REPEATABLE READ(RR,InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。然而,对于快照数据的定义去不相同。在RC事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在RR事务隔离解绑下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。

一致性锁定读

有上文知道,默认的事务隔离级别(RR)模式下,InnoDB存储引擎的select操作使用一致性非锁定读。但是在某些情况下,用户需要显式地对数据库读操作加锁以保证数据逻辑的一致性。InnoDB存储引擎对于select语句支持两种一致性的锁定读操作:

select ... for update:对读取的行记录加X锁,其他事物不能对该行加任何锁。

select ... lock in share mode:对读取的行记录加S锁,其他事物可以对该行加S锁,但是如果加X锁,则会被阻塞。

自增长与锁

在InnoDB存储引擎的内存结构中,对每个含有自增长值的表都有一个自增长计数器(auto-increment counter)。当对含有自增长的计数器的表进行插入操作是,这个计数器会被初始化,执行如下的语句来得到计数器的值:select max(auto_inc_col) from t for update。插入操作会依据这个自增长的计数器值加1赋予自增长列。这个实现方式称为AUTO-INC Locking,这是一种特殊的表锁机制,为了提高插入的性能,锁不是在一个事务完成之后才释放,而是在完成对自增长值插入的SQL语句后会立即释放。AUTO-INC
Locking在一定程度上提高了并发插入的效率,但是还存在一些性能上的问题。首先,对于有自增长值的列的并发插入性能较差,事务必须等待前一个插入的完成(不用等待事务的完成)。其次,对于insert ... select的大数据量的插入会影响插入的性能,因为另一个事务中的插入会被阻塞。从MySQL5.1.22版本开始,InnoDB存储引擎提供了一种轻量级互斥量的自增长实现机制,这种机制大大提高了自增长值插入的性能。通过参数innodb_autoinc_lock_mode来控制自增长的模式(默认为1)。自增长的插入进行分类如图:

innodb_autoinc_lock_mode的参数值及其对自增长的影响如下图:

MyISAM存储引擎是表锁,自增长不用考虑并发插入的问题。需要注意的是:在InnoDB存储引擎中,自增长值的列必须是索引,同时必须是索引的第一个列,如果不是第一个列,MySQL是会抛出异常的。异常如图

外键与锁

外键主要用于完整性的约束检查。在InnoDB存储引擎中,对于一个外键列,如果没有显式地对这个列加索引,InnoDB存储引擎会自动对其加一个索引,避免表锁。对于外键值的插入或者更新,首先需要查询父表中的记录,对于父表的select操作,不是使用的一致性非锁定读的方式,因为这样会发生数据不一致的问题,所以这时使用的是select ... lock in share mode方式,即主动给父表加一个S锁。

锁的问题

dirty read 脏读

脏读就是读取到脏数据(未提交的数据)。一个事务(A)读取到另一个事务(B)中修改后但尚未提交的数据,并在这个数据的基础上操作。这时,如果B事务回滚,那么A事务读到的数据是无效的。不符合一致性。如图

首先事务的隔离级别有默认的RR改为RU,由上述例子可以看出会话B中两次select操作取得了不同的结果,并且这2条记录是会话A中并未提交的数据,这就产生了脏读。由此可以得出结论:脏读发生的条件是事务的隔离级别为RU。

unrepeatable read 不可重复读

事务(A)读取到了另一个事务(B)已经提交的更改数据,不符合隔离性。不可重复读和脏读的区别是:脏读是读到未提交的数据,而不可重复读则读到的是已经提交的数据。首先将事务隔离级别调整为RC,然后操作下边的例子:

phantom read 幻读

事务(A)读取到了另一个事务(B)提交的新增数据,不符合隔离性。

锁的范围(锁的算法):

1.Record Lock :单个记录上的锁,总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。

2.Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。

3.Next-key Lock: 锁定一个范围和本身 Record Lock + Gap Lock,防止幻读。

主键索引和唯一辅助索引 = record lock

非唯一辅助索引 = next-key lock

阻塞

不同锁之间的兼容性关系,在有些时刻一个事务中的锁需要等待另外一个事务中的锁释放它所占用的资源,这就是阻塞。阻塞并不是一件坏事,其是为了确保事务可以并发且正常地运行。在InnoDB存储引擎中,参数innodb_lock_wait_timeout用来动态的控制等待的时间(默认50秒),innodb_rollback_on_timeout用来静态的设定释放在等待超时时对进行的事务进行回滚操作(默认OFF,代表不回滚)。

死锁

死锁是指两个或者两个以上的事务在执行过程中,因争夺资源而造成的一种相互等待的现象。解决死锁最简单的一种方式是超时,即当两个事务相互等待是,当一个等待时间超过设置的某一阀值时,其中一个事务进行回滚,另外一个等待的事务就能继续进行。在InnoDB存储引擎中,参数innodb_lock_wait_timeout用来设置超时时间。但若超时的事务所占权重比较大,如果事务操作更新了很多行,占用了较多的undo log,这时采用FIFO的方式就不合适啦,因为回滚这个事务的时间相对于另一个事务所占用的时间可能会很多。因此,除了超时机制,当前数据库都普遍采用wait-for
graph(等待图)的方式来进行死锁检测。要求数据库报错以下两种信息:a.锁的信息链表;b.事务等待链表。通过上述链表可以构造一张图,而在这个图中若存在回路,就代表存在死锁。在wait-for graph中,事务为图中的节点。如图:

如图可以发现存在回路(1,2),因此存在死锁,这时InnoDB存储引擎选择回滚undo量最小的事务。wait-for graph的死锁检测通常采用深度优先的算法实现。

 注意:

1.S X IS IX,表示的是,本锁和其他锁共存的方式,是互斥还是兼容

2.RECORD LOCK、GAP LOCK、NEXT-KEY LOCK,表示的是,这些锁要加载的范围,是行记录本身,还是行记录+间隙,甚至更大的范围

重要的结论:

1、任何辅助索引上的锁,或者非索引列上的锁,最终都要回溯到主键上,在主键上也要加一把锁

2、任何叶子节点上的S或X锁之前,都会在根节点加一个IS或IX锁,也就是表级别的IS、IX锁

3、主键索引上的锁,都是record lock

4、唯一索引辅助索引上的锁,也都是record lock

5、非唯一索引辅助索引上的锁,则是next-key lock

6、不会有单独的gap lock出现,只会伴随着record lock出现,依附于它

时间: 2024-10-19 11:39:49

MySQL InnoDB存储引擎之锁的相关文章

mysql innodb存储引擎的聚集索引

InnoDB聚集索引 MySQL有没有支持聚集索引,取决于采用哪种存储引擎. MySQL InnoDB一定会建立聚集索引,所谓聚集,指实际数据行和相关的键值保存在一块,这也决定了一个表只能有一个聚集索引,即MySQL不会一次把数据行保存在二个地方.InnoDB通常根据主键值(primary key)进行聚集,但是当一个表没有PK怎么办?InnoDB选取聚集索引参照列的顺序是: 1.如果声明了主键(primary key),则这个列会被做为聚集索引2.如果没有声明主键,则会用一个唯一且不为空的索引

MySQL InnoDB存储引擎排它锁和共享锁的研究

1,共享锁实验 session1 在session1建表lisa并插入数据 mysql> create table lisa(name char(10),age int(5)); mysql> insert into lisa values('lisa','26'); 加给age=26这一行加共享锁 mysql> set autocommit=0; mysql> select * from lisa where age=26 lock in share mode; mysql>

mysql的存储引擎与锁

一.背景知识 1.锁是计算机协调多个进程或线程并发访问某一资源的机制. A.锁分类. | 共享锁(读锁):在锁定期间,多个用户可以读取同一个资源,读取过程中数据不会发生变化. | 排他锁(写锁):在锁定期间,只允许一个用户写入数据,其它用户的读取,写入等操作都会被拒绝. B.锁颗粒 | 表锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高,并发度最低. | 行锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突的概率最低,并发度也最高. | 页面锁:开销和加锁时间界于表锁和行

MySQL InnoDB存储引擎

MySQL对应InnoDB版本 MySQL 5.1>InnoDB 1.0.X MySQL 5.5>InnoDB 1.1.X MySQL 5.6>InnoDB 1.2.X 后台线程 1.Master Thread 负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性:包括刷新脏页.合并插入缓冲.undo页的回收. 2.IO Thread innodb存储引擎中大量使用了AIO(Async IO)来处理写IO请求来提高数据库的并发性能,共有四类IO线程,分别是:insert buffer t

MySQL InnoDB存储引擎之表(一)

主要介绍InnoDB存储引擎表的逻辑存储以及实现.重点介绍数据在表中是如何组织和存放的. 1.索引组织表(index organized table) 在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表叫索引组织表.在InnoDB存在引擎表中,每张表都有个主键(Primary key),如果在创建表时没有显示定义主键,则会按照如下方式选择或者创建主键:a.判定是否有非空的唯一索引(unique not null),如果有则该列即为主键.若果有多个,则选择建表是第一个定义的非

mysql innodb存储引擎介绍

innodb存储引擎1.存储:数据目录.可以通过配置修改 存储文件:frm,ibd结尾的文件.frm存储表结构,ibd存储索引和数据 存储日志:ib_logfilen文件 2.innodb存储引擎开启或关闭: 关闭innodb_fast_shutdown= 0 完成所有的full purge和merge insert buffer操作(如:做InnoDB plugin升级时) 1 默认,不需要完成上述操作,但会刷新缓冲池中的脏页 2 不完成上述两个操作,而是将日志写入日志文件,下次启动时,会执行

MySQL InnoDB 存储引擎探秘

在MySQL中InnoDB属于存储引擎层,并以插件的形式集成在数据库中.从MySQL5.5.8开始,InnoDB成为其默认的存储引擎.InnoDB存储引擎支持事务.其设计目标主要是面向OLTP的应用,主要特点有:支持事务.行锁设计支持高并发.外键支持.自动崩溃恢复.聚簇索引的方式组织表结构等. 体系架构 InnoDB存储引擎是由内存池.后台线程.磁盘存储三大部分组成. 线程 InnoDB 使用的是多线程模型, 其后台有多个不同的线程负责处理不同的任务 Master Thread Master T

MySQL InnoDB 存储引擎原理浅析

注:本文主要基于MySQL 5.6以后版本编写,多数知识来着书籍<MySQL技术内幕++InnoDB存储引擎>,本文章仅记录个人认为比较重要的部分,有兴趣的可以花点时间读原书. 一.MySQL体系结构 主要包含以下几部分: 1.管理服务于工具组件. 2.连接池与鉴权. 3.SQL接口. 4.查询分析器. 5.优化器组件. 6.缓存与缓冲区. 7.各式的插件式存储引擎. 8.物理文件. 其中存储引擎是基于表,而非数据库. 二.InnoDB体系结构 InnoDB引擎包含几个重要部分: 1.后台进程

MySQL Innodb 存储引擎学习篇

master thread的县城优先级别最高.其内部由几个循环(loop)组成:主循环(loop).后台循环(background loop).刷新循环(flush loop).暂停循环(suspend loop).master thread 会根据数-据库运行的状态在loop,background loop.flush loop 和suspend loop 中进行切换.                每秒一次的操作:        1.日志缓冲刷新到磁盘,即使这个事务还没有提交(总是).