内容简介: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是通过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 锁原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- java反射原理, 注解原理
- Webpack 原理(二):加载原理
- Docker原理之 - CGroup实现原理
- 【Vue原理】响应式原理 - 白话版
- Docker实现原理之 - OverlayFS实现原理
- UAV MOF工作原理之Agent注入机制原理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。