浅析MySQL InnoDB的隔离级别
MySQL InnoDB存储引擎中事务的隔离级别有哪些?对应隔离级别的实现机制是什么?
本文就将对上面这两个问题进行解答,分析事务的隔离级别以及相关锁机制。
隔离性简介
隔离性主要是指数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的"独立"环境执行,意思就是多个事务并发执行时,一个事务的执行不应影响其它事务的执行。
4种隔离级别介绍
在SQL标准中定义了4种隔离级别,分别是:
- Read uncommitted: 未提交读,事务中的修改,即使没有提交,对其他事务也是可见的。存在脏读
- Read committed: 提交读,大多数数据库系统的默认隔离级别(MySQL不是), 一个事务从开始到提交之前,所做的修改对其他事务不可见。解决脏读,存在幻读和不可重复读
- repeatable read: 可重复读,该级别保证在同一事务中多次读取同样记录的结果是一致的。解决脏读和不可重复读,理论上存在幻读,但是在InnoDB引擎中解决了幻读
- Serializable:可串行化,强制事务串行执行。
上面4种隔离级别是SQL标准定义的,但是在不同的存储引擎中,实现的隔离级别不尽相同。本文主要介绍MySQL InnoDB 存储引擎中的隔离级别,在InnoDB存储引擎中,Repeatable Read 是默认的事务隔离级别,同时该引擎的实现基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control),解决了幻读问题,当然 脏读和不可重复读也是不存在的。MVCC最大的好处就在于读不加锁,读写不冲突,这样极大的增加了系统的并发性能
Read uncommitted
未提交读,这种情况下,一个事务A可以看到另一个事务B未提交的数据,如果此时事务B发生回滚,那么事务A拿到的就是脏数据,这也就是脏读的含义。此隔离级别在MySQL InnoDB一般不会使用,不做过多说明。
Read Committed
提交读,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。解决了脏读问题,但是存在幻读现象。
所谓幻读,指的是在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行,也就是"幻行"。
比如下面这个例子:
- 1. 首先创建一张表,
CREATE TABLE `t` ( `a` int(11) NOT NULL, PRIMARY KEY (`a`) ) ENGINE=InnoDB insert into t(a) values(1); insert into t(a) values(2); insert into t(a) values(4); 复制代码
- 1. 分别执行事务1和事务2:

可以从上图看出,Read Committed这种隔离级别存在幻读现象。实际上,Read Committed还可能存在不可重复读的问题,不可重复读,指的是一个事务内根据同一条件对行记录进行多次查询,但是查询出的数据结果不一致,原因就是查询区间数据被其他事务修改了。
不可重复读感觉和幻读有点像,实际上,前者强调是同一行记录数据结果不一样,后者强调的时多次查询返回的结果集不一样,增加了或减少了。
Repeatable Read
可重复读,该级别保证在同一事务中多次读取同样记录的结果是一致的,在InnoDB存储引擎中同时解决了幻读和不可重复读问题。至于InnoDB通过什么方式解决幻读和不可重复读问题,后续内容揭晓。
Serializable (可串行化)
Serializable 是最高的隔离级别,它通过 强制事务串行执行,避免了幻读的问题,但是 Serializable 会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题,因此并发度急剧下降,在MySQL InnoDB不被建议使用
Read Committed隔离级别下的加锁分析
隔离级别的实现与锁机制密不可分,所以需要引入锁的概念,首先我们看下InnoDB存储引擎提供的两种标准的行级锁:
- 共享锁(S Lock):又称为读锁,可以允许多个事务并发的读取同一资源,互不干扰。即如果一个事务T对数据A加上共享锁后,其他事务只能对A再加共享锁,不能再加排他锁,只能读数据,不能修改数据
- 排他锁(X Lock): 又称为写锁,如果事务T对数据A加上排他锁后,其他事务不能再对A加上任何类型的锁,获取排他锁的事务既能读数据,也能修改数据。
注意: 共享锁和排他锁是不相容的。
MySQL InnoDB存储引擎是使用多版本并发控制的,读不加锁,读写不冲突,除非特定场景下的显示加读锁(这里不去探究)。本小节主要分析Read Committed隔离级别下的加锁情况,在MVCC的作用下,一般也就是写操作加X锁了。
加锁操作是和索引紧密相关的,对一个SQL语句进行加锁分析时,也要仔细考究其属性列上的索引类型。假设有数据表t1,有两个列,name列和id列,插入了几条数据,没有明确索引情况:
insert into t1(name,id) values("a",10); insert into t1(name,id) values("b",11); insert into t1(name,id) values("c",13); insert into t1(name,id) values("d",20); 复制代码
下面执行 delete from t1 where id = 10 这条SQL语句,这里的隔离级别设置为Read Committed,从这条SQL语句不能得知id列的索引情况,所以需要分情况讨论:
- id列是主键
- id列是二级唯一索引
- id列是二级非唯一索引
- id列上没有索引
id列是主键
id是主键时,上述SQL只需要在id=10这条记录上加X锁即可
id列是二级唯一索引
若id列是唯一索引,而主键是name列,那么SQL需要加上两个X锁,一个对应于id索引上的id=10的记录,另一把锁对应于主键索引上的[name="a",id=10]的记录
id列是二级非唯一索引
若id列上有非唯一索引,那么对应的所有满足SQL查询条件的记录,都会被加锁,同时,这些记录在主键索引上的记录也会被加锁。
id列上没有索引
若id列上没有索引,SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Sever层面进行的,因此每条记录,无论是否满足条件,都会被加上X锁。
Repeatable Read隔离级别下的加锁分析
前面说过,在Repeatable Read隔离级别下,InnoDB存储引擎解决了幻读和不可重复读问题,具体的原理是怎么样的呢?
之前简短的介绍了InnoDB中行锁的知识,下面来看下行锁的三种算法:
- Record Lock: 单个索引记录上的锁,即加X锁
- Gap Lock: 间隙锁,锁定一个范围,但不包含记录自身
- Next-Key Lock: Gap Lock + Record Lock,锁定一个范围,并且锁定本身。
Record Lock总是会去锁住索引记录,如果InnoDB存储引擎在建表的时候没有设置任何一个索引,那么这时InnoDB会使用隐式的主键来进行锁定。(表没有定义主键的情况,InnoDB会默认添加一个隐式的主键索引)
Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,比如一个索引列有10,11,13和20这4个值,那么该索引可能被Next-Key Locking的区间为:
- (
$-\infty$,10) - (10,11]
- (11,13]
- (13,20]
- (20,
$+\infty$)
需要注意一点的是,当查询的索引含有唯一属性时,即是主键索引或者唯一索引时,InnoDB存储引擎会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,一般加上X锁。
Next-Key Lock机制设计的目的就是为了解决幻读问题,主要针对查询列索引为非唯一索引的时候。以下面这个例子进行说明:
- 1. 首先创建测试表t1,name是主键索引,id为非唯一索引,即辅助索引
CREATE TABLE `t1` ( `id` int(11) NOT NULL, `name` varchar(200) DEFAULT NULL, PRIMARY KEY (`name`), KEY `id_indx` (`id`) ) ENGINE=InnoDB insert into t1(name,id) values("a",10); insert into t1(name,id) values("b",11); insert into t1(name,id) values("c",13); insert into t1(name,id) values("d",20); 复制代码
- 1. 执行 delete from t1 where id = 11,其加锁情况如下图所示

这条SQL通过索引列id进行删除操作,该索引为非唯一索引,所以其使用传统的Next-Key Locking 技术加锁,并且由于有主键索引和辅助索引两个,需要分别进行锁定。对于主键索引(即聚集索引),其仅对列name = "b"的索引加上 Record Lock,实际上就是X锁。
而对于非唯一索引,其加上的时Next-Key Lock,锁定范围是(10,11),对其加上Gap Lock(间隙锁),GAP锁实际上就是加在两条边界记录之间的位置。还需要注意的是,InnoDB还会对辅助索引下一个键值加上gap lock,即看到在(11,13)之间加了一个GAP锁。对于11值本身加上Record Lock,即X锁。
若此时开启另外一个事务执行下面的语句,就会阻塞:
1. select * from t1 where name = "b"; 2. insert into t1(name,id) values("c",12); 复制代码
比如第一条语句不能执行,因为在开始的事务中已经对聚集索引中的列name="b"的值加上了X锁。因此执行会被阻塞。而第二个SQL,同样不能执行,插入的值12在锁定范围(11,13)中,需要阻塞等待。
所以,从上例就可以看出,GAP Lock的作用就是为了阻止多个事务将记录插入到同一范围内,这样就有效的解决了幻读问题。
隔离级别总结
下面总结下InnoDB存储引擎下的各种隔离级别:
浅析MySQL InnoDB的隔离级别的更多相关文章
- MySQL实战 | 03 - 谁动了我的数据:浅析MySQL的事务隔离级别
原文链接:这一次,带你搞清楚MySQL的事务隔离级别! 使用过关系型数据库的,应该都事务的概念有所了解,知道事务有 ACID 四个基本属性:原子性(Atomicity).一致性(Consistency ...
- mysql事务之一:MySQL数据库事务隔离级别(Transaction Isolation Level)及锁的实现原理
一.数据库隔离级别 数据库隔离级别有四种,应用<高性能mysql>一书中的说明: 然后说说修改事务隔离级别的方法: 1.全局修改,修改mysql.ini配置文件,在最后加上 1 #可选参数 ...
- MySQL事务学习-->隔离级别
MySQL事务学习-->隔离级别 6 事务的隔离级别 设置的目的 在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别. 数据库是要被广大客户所共享访问的,那么在数据库操作过程中 ...
- MySQL事务及隔离级别详解
MySQL事务及隔离级别详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL的基本架构 MySQL的基本架构可以分为三块,即连接池,核心功能层,存储引擎层. 1> ...
- 重新学习MySQL数据库8:MySQL的事务隔离级别实战
重新学习Mysql数据库8:MySQL的事务隔离级别实战 在Mysql中,事务主要有四种隔离级别,今天我们主要是通过示例来比较下,四种隔离级别实际在应用中,会出现什么样的对应现象. Read unco ...
- 【MySQL】事务隔离级别及ACID
注:begin或start transaction并不是一个事务的起点,而是在执行它们之后的第一个操作InnoDB表的语句,事务才真正开始.start transaction with consist ...
- 一文读懂MySQL的事务隔离级别及MVCC机制
回顾前文: 一文学会MySQL的explain工具 一文读懂MySQL的索引结构及查询优化 (同时再次强调,这几篇关于MySQL的探究都是基于5.7版本,相关总结与结论不一定适用于其他版本) 就软件开 ...
- MySQL 事务的隔离级别
转载:https://developer.aliyun.com/article/743691?accounttraceid=80d4fddb3dc64b97a71118659e106221tozz 简 ...
- MySQL - 数据库的隔离级别
MySQL - 数据库的隔离级别 隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read) 未提交读(Read uncommitte ...
随机推荐
- Java Spring Boot VS .NetCore (三)Ioc容器处理
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- creating server tcp listening socket 127.0.0.1:6379: bind No error
参考链接:https://blog.csdn.net/n_fly/article/details/52692480 1.window10环境下面安装的redis,之前安装好弄了一下,过了好几天,再次使 ...
- Servlet(六):Cookie
Cookie 学习:问题: HTTP 协议是没有记忆功能的,一次请求结束后,相关数据会被销毁.如果第二次的请求需要使用相同的请求数据怎么办呢?难道是让用户再次请求书写吗?解决: 使用 Cookie 技 ...
- 问题集 & 知识点
芝士 [事件绑定的三种方法] 在以类继承的方式定义的组件中,为了能方便地调用当前组件的其他成员方法或属性(如:this.state),通常需要将事件处理函数运行时的 this 指向当前组件实例. 绑定 ...
- Java-正则使用
Java-正则使用 注意 在Java中由于string的设计,导致斜杠是特殊的字符,所以如若想要在正则中使用斜杠,则需要两个斜杠来表示 eg: \d 需要写成: \\d ,两外 \\\\ 表示匹配单个 ...
- clipboard.js一个可以在移动端一键复制的插件
网址:https://clipboardjs.com/ 使用方法: 1.引入js <script src="dist/clipboard.min.js"></sc ...
- MVC中EF代码优先问题
在练习Mvc项目时,提示如下数据库错误: The model backing the 'EFDbContext' context has changed since the database was ...
- 小甲鱼Python第十七讲课后习题
笔记: 1.分清楚形参和实参 2.函数文档:是函数的一部分,于解释不同,使用help(函数名)或者 函数名__doc__可以查看到 3.关键字参数(在一个函数的参数较多的时候作用比较明显): 给参数的 ...
- 关于DTO的理解
转自大神loveis715博文:http://www.cnblogs.com/loveis715/p/4379656.html 在一个web服务的实现中,我们常常需要访问数据库,并将从数据库中取得的数 ...
- Linux搭建git服务端
1.安装$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel$ yum inst ...