浅析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 ...
随机推荐
- C# .Net String字符串效率提高-字符串拼接
字符串操作是编程中非常频繁的操作,特别是在拼接字符串的时候.下面来说说字符串拼接的效率提升. 1. 减少装箱 值类型与引用类型之间的转换存在装箱与拆箱操作:将值类型转换成引用类型的操作叫装箱,将引用类 ...
- ArrayList Vector
100000条数据时:测了4次,分别是9ms/13ms:8ms/6ms:8ms/6ms:8ms/6ms[其中/前为ArrayList数据,/后为Vector数据]1000000条数据时:测了4次,分别 ...
- es6中的...三个点
...是es6中新添加的操作符,可以称为spread或rest 定义一个数组 let name=['小红','小明','小白']; 我们在控制台输出 console.log(name); 结果: ...
- php+ajax文件上传
php+ajax文件上传 html: <input id="user_real_name" class="input_show" type="t ...
- 锋利的jquery 事件 动画
事件 $(function){} bind(type, [data],function) 事件类型, 传递参数, 处理函数 hover(enter, leave) 光标停留时,函数enter,离开时函 ...
- jenkins-参数化构建(三)插件:Git Parameter
一.下载插件Git Parameter (更加省事) 在配置中branch和tag用的比较多 注意:Credential 可以添加密码,jenkins如果在root用户下载的请改 /etc/sys ...
- python语法_字符串
字符串 a = 'asdb' #双引号和打印号没区别, 操作 "abc"*2 打印两遍"abc" #字符串 加* 重复打印字符串 “abc”[2:1] #切片 ...
- IOS不支持iframe的解决办法
最近做ios的兼容,因为入口不同,需要把公共页面抽取出来,然后根据不同的值,传递给后台不同的入口.刚开始用iframe页面嵌套(第一次尝试使用),但发现iOS系统对iframe嵌套页面的高度和定位控制 ...
- Cassandra最小化安装
Cassandra最小化安装 环境 CentOS 7.2 64位 IP_address:172.27.0.8 安装包装备 [root@master ~]# ll /usr/local/src tota ...
- ide phpStorm常用代码片段设置
1.打开设置(File -> Settings) 2.如图 3 . 最后,在PHP文件中输入 ll 并按 TAB 即可打出代码块