MySQL不同隔离级别并发测试分析
MySQL不同隔离级别并发测试分析
- 背景
- 事务
- 基于锁的并发控制
- MVCC
- 测试方法
- 用例与分析
- 测试结果
- 用例分析
- 总结
背景
事务
事务是数据库执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。事务的出现主要有两个目的,一是提供数据库操作失败时的恢复方法,而是当多个应用程序同时访问数据库时,对其进行隔离,以防止相互干扰。事务具有原子性、一致性、隔离性、持久性四种特性,也就是所谓的ACID特性。在多个应用程序同时访问数据库时,如果不做并发控制,则可能出现脏读、不可重复读、幻读等异常情况。因此,为了各数据库厂商能够更容易地设计并实现数据库,ANSI/ISO SQL 标准规定了四种隔离级别,即READ UNCOMMITTED、READ COMMITTED、REPEATABLE READS以及SERIALIZABLE,明确规定了各个隔离级别下可能出现的异常,见表1。
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
READ UNCOMMITTED | 是 | 是 | 是 |
READ COMMITTED | 否 | 是 | 是 |
REPEATABLE READS | 否 | 否 | 是 |
SERIALIZABLE | 否 | 否 | 否 |
基于锁的并发控制
ANSI/ISO SQL 标准在规定了隔离级别的同时,也提出了基于锁实现并发控制的一种方法,其基本思想是将锁分为三种,即读锁、写锁以及范围锁,读锁和写锁我们都很清楚,那么什么是范围锁?所谓范围锁其实是读锁的一种,但范围锁不是对一条记录加锁,而是对符合条件的一个范围加锁。当使用带WHERE子句的SELECT查询时,为了防止幻读的产生,需要使用范围锁。表2为不同隔离级别下对上述三种锁的持用情况,“是”表示在该隔离级别下需要得到对应的锁,并且直到事务结束才会释放。
下面我们详细分析一下使用锁了进行并发控制的流程,对读操作和写操作分别进行讨论。
写锁 | 读锁 | 范围锁 | |
---|---|---|---|
READ UNCOMMITTED | 是 | 否 | 否 |
READ COMMITTED | 是 | 否 | 否 |
REPEATABLE READS | 是 | 是 | 否 |
SERIALIZABLE | 是 | 是 | 是 |
写操作:
在所有的四种隔离级别下,写事务进行操作的流程都是一样的。如果它要对某条记录进行操作,则需要获得该记录的写锁,与此同时,保存该记录的旧值,然后对该记录进行写操作。当然,根据读写锁的相容性矩阵,只有该记录没有被加任何锁的时候才能获得写锁。对于每条记录来说,同一时刻最多只有一个事务能得到写锁,所以最多只会有两个不同的值,一个是旧值,即写入该值的事务已经成功提交,另外一个是新值,写入该值的事务目前处于未提交状态。如果写入该记录的事务成功提交,就删除该记录的旧值,否则则需要恢复该记录的旧值,最后释放该记录的写锁。
读操作:
在READ UMCOMMITED这个隔离级别下,读事务不需要获得所读记录的读锁,因此进行读事务永远不会阻塞,它会读取该记录最新的值(写入该值的事务可能处于未提交状态)。
在READ COMMITED这个隔离级别下,读事务需要获得所读记录的读锁,并且与READ UMCOMMITED这个级别不一样,此时读事务会读取该记录最新的已经提交的值。但是需要特别注意的一点是,一旦事务的读操作完成,就会将读锁释放掉,读事务并不会一直持有该读锁。
在REPEATABLE READS这个隔离级别下,读事务需要获得所需记录的读锁。如果该记录已经被上了写锁,则读事务会阻塞,直到持有该记录写锁的事务提交为止;否则(即该记录未被上锁或者被上了读锁)直接读取该记录(此时该记录只会有一个值),并对该条记录上读锁。读事务会一直持有读锁,直到该事务提交或者回滚时,才会释放读锁。
在SEREIALIZABLE这个隔离级别下,读事务也需要获得所需记录的读锁。对于一般的读操作,同REPEATABLE READS一样进行处理,即如果该记录已经被上了写锁,则读事务会阻塞,直到持有该记录写锁的事务提交为止;否则直接读取该记录,并对该条记录上读锁。但是对于范围SELECT查询,在这个隔离级别下,需要对符合SELECT条件的记录加范围锁,以保证不会出现幻读。读事务会一直持有读锁或者范围锁,直到该事务提交或者回滚时,才会释放读锁。
MVCC
MVCC的精要用一句话来概括即是“读不阻塞写,写不阻塞读”。由于MVCC有多种实现方式,此处就不一一列举,MVCC的主要实现机制是保存每个记录的多个版本。由于是对MySQL进行测试,所以详细分析一下在MySQL进行并发控制时MVCC的作用。
MVCC本质上并不是由MySQL实现的,MySQL将上层与存储分离,提出了一个插件式的架构,底层可以使用不同的存储引擎。在这些存储引擎中,最著名的应该是Innodb存储引擎,其中很重要的一个原因是Innodb是少数几个支持事务的存储引擎之一。
Innodb使用锁与MVCC相结合的方式实现并发控制,它只在READ COMMITTED以及REPEATABLE READS这两个隔离级别下使用MVCC机制。Innodb把读分为了半一致性读和需要加锁的读,当事务的隔离级别低于SERIALIZABLE时,读都是半一致性读,而当进行半一致性读时,Innodb并不需要对记录行加读锁,Innodb会读取使用MVCC机制保存的旧版本。因此在事务对某条记录进行半一致性读时,其他事务仍然可以进行写操作,与之对应,如果某条记录已经被加了写锁,那么此时其他事务仍然可以进行半一致性读。
测试方法
本次测试环境要求比较简单,最基本的要求当然是MySQL数据库,并且安装了Innodb存储引擎(一般默认在安装MySQL时已经安装完毕)。测试过程较为简单,下面主要描述测试过程中需要注意的一些关键问题。
在创建测试表test_pk时后注意一定要使用Innodb作为存储引擎,使用SHOW CREATE TABLE test_pk这个命令可以查看test_pk的存储引擎,因为有时Innodb没有安装成功,会导致创建的表使用默认的MyISAM存储引擎。
CREATE TABLE test_pk
(
id
int(11) NOT NULL,
PRIMARY KEY (id
),
UNIQUE KEY id
(id
)
) ENGINE=InnoDB;
设置会话的隔离级别使用SET SESSION TRANSACTION ISOLATION LEVEL XXX命令,注意其中SESSION不可省略,这样隔离级别才会对整个会话生效,否则的话设置仅对下一个事务起作用。
测试时必须使用BEGIN或者START TRANSACTION命令开始事务,这样就不会受到Mysql AUTOCOMMIT变量的影响,注意不要使用单行事务(autocommit=1时,Innodb对单行SELECT做了优化,不管在什么隔离级别,都不会加读锁)。
用例与分析
测试结果
表1是测试一(先读后写)的测试结果,表中的项“是”表示写事务阻塞,“否”则表示写事务没有被阻塞。
表1
READ UNCOMMITED | READ COMMITTED | REPEATABLE READS | SERIALIZABLE | |
---|---|---|---|---|
READ UNCOMMITED | 否 | 否 | 否 | 否 |
READ COMMITTED | 否 | 否 | 否 | 否 |
REPEATABLE READS | 否 | 否 | 否 | 否 |
SERIALIZABLE | 是 | 是 | 是 | 是 |
表2是测试二(先写后读)的测试结果,表中的项“是”表示写事务阻塞,“否”则表示写事务没有被阻塞。
表2
READ UNCOMMITED | READ COMMITTED | REPEATABLE READS | SERIALIZABLE | |
---|---|---|---|---|
READ UNCOMMITED | 否 | 否 | 否 | 是 |
READ COMMITTED | 否 | 否 | 否 | 是 |
REPEATABLE READS | 否 | 否 | 否 | 是 |
SERIALIZABLE | 否 | 否 | 否 | 是 |
用例分析
由于Innodb在READ COMMITTED和REPEATABLE READS这两个隔离级别下使用了MVCC机制,下面我们首先分析其他两个隔离级别下的测试结果
READ UNCOMMITTED/SERIALIZABLE(测试一):会话一的隔离级别为READ UNCOMMITTED,会话二的隔离级别为SERIALIZABLE。会话一在进行SELECT操作的时候不需要获得读锁,并没有对相应的记录加锁,因此当会话二进行UPDATE操作时,可以成功地获得该记录的写锁,并不会阻塞。
SERIALIZABLE/READ UNCOMMITTED(测试一):会话一的隔离级别为SERIALIZABLE,会话二的隔离级别为READ UNCOMMITTED。会话一在进行SELECT操作的时候需要获得读锁,因此当会话二进行UPDATE操作时,无法获得该记录的写锁,UPDATE操作被阻塞。
READ UNCOMMITTED/SERIALIZABLE(测试二):会话一的隔离级别为READ UNCOMMITTED,会话二的隔离级别为SERIALIZABLE。会话一在进行UPDATE操作的时候首先获得该记录的写锁,当会话二进行SELECT操作时,由于其隔离级别为SERIALIZABLE,需要获得该记录的读锁,因此SELECT操作被阻塞。
SERIALIZABLE/READ UNCOMMITTED(测试二):会话一的隔离级别为SERIALIZABLE,会话二的隔离级别为READ UNCOMMITTED。会话一在进行SELECT操作的时候需要获得读锁,因此当会话二进行UPDATE操作时,无法获得该记录的写锁,UPDATE操作被阻塞。
根据上一小节关于纯粹的基于锁的并发控制原理的描述,如果使用纯粹锁的并发控制,测试一的预期结果应 该如表3所示,测试二的预期结果应该如表4所示。
表 3
READ UNCOMMITED | READ COMMITTED | REPEATABLE READS | SERIALIZABLE | |
---|---|---|---|---|
READ UNCOMMITED | 否 | 否 | 否 | 否 |
READ COMMITTED | 否 | 否 | 否 | 否 |
REPEATABLE READS | 是 | 是 | 是 | 是 |
SERIALIZABLE | 是 | 是 | 是 | 是 |
对于测试一,对比表1与表3我们可以看到,预期结果与测试结果在会话一的隔离级别为REPEATABLE READS时不一致,这主要是因为在REPEATABLE READS这个隔离级别下,Innodb使用了MVCC机制,根据关于MVCC的描述,Innodb此时进行的读操作是半一致性读,并不需要对记录加读锁,测试二与此类似,不再赘述。
READ UNCOMMITED | READ COMMITTED | REPEATABLE READS | SERIALIZABLE | |
---|---|---|---|---|
READ UNCOMMITED | 否 | 否 | 是 | 是 |
READ COMMITTED | 否 | 否 | 是 | 是 |
REPEATABLE READS | 否 | 否 | 是 | 是 |
SERIALIZABLE | 否 | 否 | 是 | 是 |
总结
前面我们说过,实现并发控制大致有三种方式,即锁、时间戳以及MVCC。锁应该是最早被用来进行并发控制的机制,时间戳和MVCC是随后出现的,它们各有自己的优点与缺点。在当今的数据库中,很少有单纯使用某一种机制来进行并发控制,大多数都杂合了其中的两种或者三种,各取所长,MySQL的Innodb就是其中的一个。Innodb采用了锁与MVCC结合的方式实现并发控制,在实际运行中取得了不错的效果。
MySQL不同隔离级别并发测试分析相关推荐
- mysql 默认事务隔离级别_上个厕所的功夫,搞懂MySQL事务隔离级别
"隔离级别" 出于MySQL四大特性(ACID)中的"I",也就是隔离性.目的是实现数据.事务一致性"C". MySQL在多线程并发场景下, ...
- 上个厕所的功夫,搞懂MySQL事务隔离级别,Java学习视频百度云盘
| 14 | 朱志鹏 | 男 | 25 | 技术1部 | 5000 | 看小说 | | 19 | 李昂 | 男 | 27 | 技术1部 | 7000 | 看片儿 | ±-±----------±--- ...
- 上个厕所的功夫,搞懂MySQL事务隔离级别
"隔离级别" 出于MySQL四大特性(ACID)中的"I",也就是隔离性.目的是实现数据.事务一致性"C". MySQL在多线程并发场景下, ...
- mysql 事务隔离级别实现原理_MySQL事务隔离级别和实现原理 - 米扑博客
开发中经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗, 事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢? MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引 ...
- mysql 事物隔离级别详解
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. Read Uncommitted ...
- MySQL事务隔离级别详解
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. Read Uncommitted(读 ...
- mysql什么隔离级别最好_面试必问的 Mysql 四种隔离级别,看完吊打面试官
什么是事务 事务是应用程序中一系列严密的操做,全部操做必须成功完成,不然在每一个操做中所做的全部更改都会被撤消.也就是事务具备原子性,一个事务中的一系列的操做要么所有成功,要么一个都不作.sql 事务 ...
- 数据库事务转载基础二:MySQL事务隔离级别详解
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. Read Uncommitted(读 ...
- mysql 秒杀 隔离级别_MySQL 四种隔离级别详解,看完吊打面试官
什么是事务 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做. 事务的结束有 ...
最新文章
- 《自然》公布年度十大杰出论文
- 另一种的SQL注入和DNS结合的技巧
- Swift 学习手记1,pod 的 类库使用
- mysql sum函数返回类型_MySQL的sum函数返回的门类
- 数据分析处理——透析表和交叉表
- 在线解析短视频去水印工具
- 火影忍者ol手游服务器注册上限怎么办,火影忍者ol手游服务器人数爆满怎么进_服务器爆满解决方法...
- jdk,jre的下载安装教程
- macOS: 字体(font)文件 的 存放路径
- 牛客网试题+答案分析+大牛面试经验(12)
- web网页设计实例作业 HTML5+CSS大作业——简单的个人图片网站(6页)
- 安卓 Bitmap 高效加载
- 日本雅虎乐天商城批量上传步骤
- “我思故我在”——赋值是个大问题
- 疫情之下的远程办公,基础架构成为重中之重
- (P2笔记)Where does the error come from?(误差从而来?)——机器学习:李宏毅
- 华为GT Runner 您身边的运动智能管家,周期训练更科学
- 双色球机选号码生成器(java)
- 映客都是互刷礼物吗_今日网红
- Java异常转译(exception translation)的使用
热门文章
- 拉普拉斯分布和拉普拉斯变换有什么区别
- MATLAB—离散一元、二元、多元函数求导求梯度(二维、三维、多维空间)(diff和gradient)
- python采用强制缩进的方式是代码具有极佳的可读性_python入门
- 什么是人工智能数据采集?
- 什么是C语言的合法正确标识符?什么是C语言关键字?
- 【Android】世界各国语言代码
- 惠普136w耗材贵吗_小成本高品质 惠普M136w激光一体机评测
- Mybatis的Spring集成、Aop整合
- 微信公众号输入关键字回复WordPress搜索结果
- wordcloud 配置项_wordcloud从安装到入门