前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >惊!MySQL事务隔离级别原来这么简单

惊!MySQL事务隔离级别原来这么简单

作者头像
黑洞代码
发布2021-01-14 15:20:55
4230
发布2021-01-14 15:20:55
举报

MySQL事务隔离级别(1)

第1节:

事务概述

第2节:

MySQL4种事务隔离级别分析

第3节:

总结

1 事务概述

什么是事务?

数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。事务的使用是数据库管理系统区别文件系统的重要特征之一。

事务拥有四个重要的特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),人们习惯称之为 ACID 特性。

事务

特性

事务具有ACID特性

1. 原子性(Atomicity)。事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。例如,如果一个事务需要新增 100 条记录,但是在新增了 10 条记录之后就失败了,那么数据库将回滚对这 10 条新增的记录。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

2. 一致性(Consistency)。指事务将数据库从一种状态转变为另一种一致的的状态。事务开始前和结束后,数据库的完整性约束没有被破坏。例如工号带有唯一属性,如果经过一个修改工号的事务后,工号变的非唯一了,则表明一致性遭到了破坏。

3. 隔离性(Isolation)。要求每个读写事务的对象对其他事务的操作对象能互相分离,即该事务提交前对其他事务不可见。也可以理解为多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。这指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。例如一个用户在更新自己的个人信息的同时,是不能看到系统管理员也在更新该用户的个人信息(此时更新事务还未提交)。

4. 持续性(Durability)。事务一旦提交,则其结果就是永久性的。即使发生宕机的故障,数据库也能将数据恢复,也就是说事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。这只是从事务本身的角度来保证,排除 RDBMS(关系型数据库管理系统,例如 Oracle、MySQL 等)本身发生的故障。

2 MySQL4种事务隔离级别分析

隔离

级别

事务隔离级别概述

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

1. Read Uncommitted(读取未提交内容)。在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

2. Read Committed(读取提交内容)。

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

3. Repeatable Read(可重读)。

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

4. Serializable(可串行化)。

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

以上这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

  • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新了原有的数据。
  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

MySQL数据库实现了这以上4种隔离级别,每种隔离级别可能会产生的问题如下表所示:

隔离级别

脏读

不可重复读

幻读

Read Uncommitted

Y

Y

Y

Read Committed

N

Y

Y

Repeatable Read

N

N

Y

Serializable

N

N

N

隔离

级别

测试各种隔离级别

创建一张Student表。

代码语言:javascript
复制
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `age` int(4) NOT NULL,
  `gender` varchar(8) NOT NULL,
  `createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

分别使用2个客户端A和B操作Student表,不断修改客户端A的隔离级别,在客户端B进行数据修改。

01 客户端A将隔离级别设置为Read Uncommitted

客户端A:将隔离级别调整为Read Uncommitted,然后查询当前隔离级别如下图所示。

客户端A:开启一个事务,此时数据为初始状态。

客户端B:启动一个事务,更新id=1学生,将其name属性修改为wushuang,但不提交事务。

客户端A:再次读取数据,发现数据已经被修改了,这就是所谓的“脏读”。

客户端B:事务回滚。回滚后的id=1的学生name属性仍然为zhangsan。

客户端A:再次读取Student表中的数据,发现id=1的学生name属性仍然为zhangsan。

经过上面的实验可以得出结论,事务B更新了一条记录,但是没有提交,此时事务A可以查询出未提交记录。造成脏读现象。未提交读是最低的隔离级别。

02 客户端A将隔离级别设置为Read Committed

客户端A:将隔离级别调整为Read Committed,然后查询当前隔离级别。

客户端A:开启一个事务,此时数据为初始状态。

客户端B:启动一个事务,更新id=1学生,将其name属性修改为wushuang,但不提交事务。

客户端A:再次读取数据,发现数据未被修改。

客户端B:事务提交。

客户端A:再次读取Student表中的数据,发现id=1的学生name属性为wushuang。

经过上面的实验可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。已提交读只允许读取已提交的记录,但不要求可重复读。

03 客户端A将隔离级别设置为Repeatable Read

客户端A:将隔离级别调整为Repeatable,然后查询当前隔离级别。

客户端A:开启一个事务,此时数据为初始状态。

客户端B:启动一个事务,更新id=1学生,将其name属性修改为无双,但不提交事务。

客户端A:再次读取数据,发现数据未被修改。

客户端B:事务提交。

客户端A:再次读取数据,发现数据未被修改。

客户端B:插入一条新的数据,并提交。

客户端A:再次读取Student表中的数据,发现数据未被修改。

客户端A:提交事务。再次读取Student表中的数据,发现多了一条数据wangwu。

由以上的实验可以得出结论,可重复读隔离级别只允许读取已提交记录,而且在一个事务两次读取一个记录期间,其他事务不得更新该记录。但该事务不要求与其他事务可串行化。例如,当一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读问题(注意是可能,因为数据库对隔离级别的实现有所差别)。像以上的实验,就没有出现数据幻读的问题。

重新执行以上步骤,分别开启客户端A和客户端B,事务隔离级别依旧保持为repeatable read。在客户端B插入一条数据。

客户端A插入一条id=4的学生数据。

客户端A出现主键冲突问题。其实这就是该隔离级别下可能产生的问题,MySQL称之为幻读。

04 客户端A将隔离级别设置Serializable

客户端A:将隔离级别调整为Serializable,然后查询当前隔离级别。

客户端B:启动一个事务,插入一个name=xiaoming的同学。

由于客户端A事务并未提交,因此客户端B的insert语句将会阻塞。

客户端A:提交事务。

客户端B:在客户端A提交后,客户端B的insert语句执行结束。

3 总结

事务的机制是通过视图(read-view)来实现的并发版本控制(MVCC),不同的事务隔离级别创建读视图的时间点不同。

可重复读是每个事务重建读视图,整个事务存在期间都用这个视图。

读已提交是每条 SQL 创建读视图,在每个 SQL 语句开始执行的时候创建的。隔离作用域仅限该条 SQL 语句。

读未提交是不创建,直接返回记录上的最新值

串行化隔离级别下直接用加锁的方式来避免并行访问。

这里的视图可以理解为数据副本,每次创建视图时,将当前已持久化的数据创建副本,后续直接从副本读取,从而达到数据隔离效果。

更多有关事务隔离级别实现的方式请参考下一篇文章。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 落叶飞翔的蜗牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库管理
数据库管理(Database Management Center,DMC)是一个高效,安全,可靠的数据库一站式管理平台。DMC 提供可视化的库管理、实例会话管理、SQL 窗口、SQL 安全审计、SQL 变更审批、实时监控、操作审计等数据库管理能力,集成诊断优化和数据可视化分析能力,从而简化和规范数据库管理操作、降低数据库运维门槛、提升运维效率。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档