InnoDB 锁原理

栏目: 数据库 · 发布时间: 6年前

内容简介:MySQL是一个支持插件式存储引擎的数据库系统,其中InnoDB是MySQL的事务安全的存储引擎,在OLTP系统中使用非常广乏。InnoDB最大的特性是支持事务,事务的特性包括原子性、一致性、隔离性、持久性,其中事务的隔离性,就是通过锁来实现的。在正式介绍锁之前,先来回顾一下MySql/InnoDB的隔离级别:InnoDB存储引擎有两种行级锁他们的兼容性如下:

背景

MySQL是一个支持插件式存储引擎的数据库系统,其中InnoDB是 MySQL 的事务安全的存储引擎,在OLTP系统中使用非常广乏。InnoDB最大的特性是支持事务,事务的特性包括原子性、一致性、隔离性、持久性,其中事务的隔离性,就是通过锁来实现的。在正式介绍锁之前,先来回顾一下MySql/InnoDB的隔离级别:

  • READ UNCOMMITTED 可以读取到未提交的数据,会产生脏读的问题

  • REA D COMMITTED 读取已经提交的数据,不会有脏读,但是会有不可重复读和幻读。

  • REPEATABLE READ 在同一个事务 中,可以重复读取,同时InnoDB在此隔离级别下不会有幻象读现象。

  • SERIALIZABLE 读取和写入都需要加锁,效率比较低。

锁的类型

InnoDB存储引擎有两种行级锁

  • 共享锁 S,允许事务读取一行数据。

  • 排他锁 X,允许事务修改一行数据。

他们的兼容性如下:

兼容性 X S
X 不兼容 不兼容
S 不兼容 兼容

除此之外,InnoDB还有一种表级别锁,意向锁:

  • 意向共享锁 IS,表示事务想要获取表中某几行的共享锁。

  • 意向排他锁 IX,表示事务想要获取表中某几行的排他锁。

InnoDB的意向锁主要用户多粒度的锁并存的情况。比如事务A要在一个表上加S锁,如果表中的一行已被事务B加了X锁,那么该锁的申请也应被阻塞。如果表中的数据很多,逐行检查锁标志的开销将很大,系统的性能将会受到影响。为了解决这个问题,可以在表级上引入新的锁类型来表示其所属行的加锁情况,这就引出了“意向锁”的概念。举个例子,如果表中记录1亿,事务A把其中有几条记录上了行锁了,这时事务B需要给这个表加表级锁,如果没有意向锁的话,那就要去表中查找这一亿条记录是否上锁了。如果存在意向锁,那么假如事务A在更新一条记录之前,先加意向锁,再加X锁,事务B先检查该表上是否存在意向锁,存在的意向锁是否与自己准备加的锁冲突,如果有冲突,则等待直到事务A释放,而无须逐条记录去检测。事务B更新表时,其实无须知道到底哪一行被锁了,它只要知道反正有一行被锁了就行了。

说白了意向锁的主要作用是处理行锁和表锁之间的矛盾,能够显示“某个事务正在某一行上持有了锁,或者准备去持有锁”。

读取

innoDB中的数据读取分为锁定读取和非锁定读取。

非锁定读取

非锁定读指的是在读取的时候不需要加任何锁,读写不冲突。对于简单的查询语句select * from table where ?;在离级别READ UNCOMMITTED,READ COMMITTED和REPEATABLE READ下,是非锁定读取。在隔离级别SERIALIZABLE下,是锁定读取,需要获取行锁,此隔离级别效率极低,线上都不会采用。

在读多写少的OLTP系统当中,非锁定读取可以极大提高系统的并发处理能力。innoDB通过多版本的并发控制协议——MVCC (Multi-Version Concurrency Control)来实现非锁定读取,在读取的时候不用等待行上的锁释放,直接去读取行的一个快照数据。流程如下图所示:

InnoDB 锁原理

对于行上的快照数据,innoDB是通过undo log来实现的,undo log可用于回滚事务,也可以用来实现MVCC功能。一个行上可能不只有一个版本的快照数据,对于事务隔离级别READ COMMITTED和REPEATABLE READ,他们所读取的快照版本是不一样的。在READ COMMITTED下,读取的快照数据总是最新的版本,在REPEATABLE READ下,总是读取事务开始时的行数据。

锁定读取

在某些情况下,为了保证数据的一致性,要先获取行锁,再进行数据读取。如下所示语句都会产生锁定读取:

  • select ... lock in share mode; S

  • select ... for update; X

  • insert into table values (..); 由于插入时需要唯一性检查,所以需要X锁。

  • update table set ? where ?; X

  • delete from table where ?; X

行锁的算法

  • Record Lock:单个行记录上锁。

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

  • Next-Key Lock:Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身。

Record lock单条索引记录上加锁,Record lock锁住的永远是索引,而非记录本身。索引分为主键索引和非主键索引两种,如果一条 sql 语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X锁。

Gap Lock锁定的是索引之间的间隙,并不是记录本身。例如有一个索引有3,5,6,10和20这几个值,他们之前的间隙包括(-∞,3)、(3,5)、(5,6)、(10,20)、(20,+∞),对于Gap Lock锁定就是这几个范围。

InnoDB在不同的隔离级别下使用的锁算法也不同。

READ COMMITTED

对于innoDB的READ COMMITTED隔离级别下,会存在幻象读问题。在该隔离级别下,除了外键约束和唯一性检查依然需要Gap Lock,其余情况均使用Record Lock进行锁定。

REPEATABLE READ

InnoDB在该隔离级别下,没有幻读现象。幻读是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次执行可能返回之前不存在的行。例如对下述语句:select * from table where id > 10 for update; 在事务1查询之后,如果另一个事务2可以插入id大于10的记录,在事务1下次查询的时候也会返回事务2插入的记录,两次的读取结果不一样,这就是幻读。

InnoDB通过Next-key Lock来避免幻读的现象,除了锁住记录本身之外,还要锁住可能涉及到的间隙(Gap)。对于上述例子select * from table where id > 10 for update; 在REPEATABLE READ隔离级别下锁定是(10,+∞)这个范围。

总结

本文主要介绍了InnoDB的锁的类型及RC和RR级别下的加锁情况,希望能给大家带来帮助。

InnoDB 锁原理


以上所述就是小编给大家介绍的《InnoDB 锁原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C# 6.0本质论

C# 6.0本质论

[美] Mark Michaelis(马克·米凯利斯)、[美] Eric Lippert(埃里克·利珀特) / 周靖、庞燕 / 人民邮电出版社 / 2017-2-1 / 108

这是C#领域中一部广受好评的名作,作者用一种易于理解的方式详细介绍了C#语言的各个方面。全书共有21章和4个附录(其中哟2个附录从网上下载),介绍了C#语言的数据类型、操作符、方法、类、接口、异常处理等基本概念,深入讨论了泛型、迭代器、反射、线程和互操作性等高级主题,还介绍了LINQ技术,以及与其相关的扩展方法、分部方法、Lambda表达式、标准查询操作符和查询表达式等内容。每章开头的“思维导图”......一起来看看 《C# 6.0本质论》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具