【MySQL】事务隔离级别及ACID
注:begin或start transaction并不是一个事务的起点,而是在执行它们之后的第一个操作InnoDB表的语句,事务才真正开始。start transaction with consistent snapshot命令可以马上启动一个事务。
1、隔离级别
1.1、基本概念
读未提交
当前事务能读取到其他事务尚未提交的修改值。
读提交
当前事务能读取到其他事务已经提交的修改值。
可重复读
一个事务在任何时刻查询到的结果跟事务一开始启动时查询的结果一样。
串行化
强行使所有事务串行执行。
1.2、小测试一
理解了隔离级别的基本概念后,我们做个小测试,看看是不是真的理解了,下面两个会话按顺序执行,最后在各个隔离级别下的V1、V2值各为多少呢?
| sessionA | sessionB |
|---|---|
| 启动事务 | |
| 查询得到值1 | |
| 启动事务 | |
| 把值1更新成值2 | |
| 查询得到值V1 | |
| 提交事务 | |
| 查询得到值V2 | |
| 提交事务 | |
| 查询得到值V3 |
读未提交可以读取到其他事务未提交的值,所以V1读取到了sessionB未提交的更改,值为2。
读提交不能读取到其他事务未提交的值,所以V1的值为1,当查询V2时,sessionB已经提交了,这时读取到的V2为2。
可重复读查询得到的值为事务启动时的值,所以V1和V2值都为1。
串行化时,sessionA启动事务后,sessionB会被卡住,直至sessionA的事务提交后才执行,所以V1和V2都是1,V3查询时sessionB已经提交事务,所以V3的值为2。
| V1 | V2 | V3 | |
|---|---|---|---|
| 读未提交 | 2 | 2 | 2 |
| 读提交 | 1 | 2 | 2 |
| 可重复读 | 1 | 1 | 2 |
| 串行化 | 1 | 1 | 2 |
1.3、小测试二
上面的小测试一加深了我们对事务隔离级别的了解,下面我们针对可重复读隔离级别下对小测试一进行修改再加深理解。例子如下:
| sessionA | sessionB |
|---|---|
| 启动事务 | |
| 查询得V值为1 | |
| 启动事务 | |
| set V=V+1 | |
| 查询得到值V1 | |
| 提交事务 | |
| set V=V+1 | |
| 查询得到值V2 | |
| 提交事务 | |
| 查询得到值V3 |
小测试一可重复读隔离级别下V1、V2的值都为1,V3的值为2。根据小测试一的思路,你可能会得出小测试二的结果为V1为1,V2为2,那么V3是多少呢?
其实正确的答案是V1为1,V2为3,V3为3。如果sessionA中的set V=V+1是set V=1+1的话,这样就丢失了sessionB更新的操作,使数据不正确,这时因为sessionB提交了,所以sessionA中的更新同一个值为当前读,为set V=2+1,所以V2为3。小测试一中读取的数据在概念中为一致性读。
如果流程中把sessionB的提交事务放在sessionA的提交事务后面呢?这个时候sessionA的set V=V+1会被阻塞,直至sessionB的事务提交后才会执行,结果还是V1为1,V2为3,V3为3。
MySQL的事务隔离级别默认为可重复读,oracle的事务隔离级别默认为读提交,所以在把oracle迁移至MySQL时需要记得把MySQL的设置读提交后操作。
可重复读在事务启动时会创建一个视图,后继查询都在此视图上操作。
读提交在事务启动时也会创建一个视图,但其他事务更新操作会直接更新此视图。
读未提交直接读取数据库中最新的数据。
串行化直接进行加锁避免其他事务修改。
| view1 |
view2 |
|---|---|
| 1 | 2 |
如上表格所示视图,行数据从1更新为2的过程对应视图view1到view2的过程。
读提交隔离级别下,事务启动时是view1视图,当sessionB发生更新数据后,视图view1会更新成视图view2,后续查询在view2上进行。
可重复读隔离级别下,事务启动时是view1视图,当sessionB发生更新数据后,事务还是照样读取view1视图。
可重复读隔离级别下,是怎么找到view1视图的呢?
其实这就是MVCC的功能了,事务在启动时会获得系统会分配一个按顺序递增的transaction id,然后事务在更新数据时会把这个transaction id 分配给这一行数据当做trx row_id,顺便记录undo log日志。比如上面sessionA事务在启动时拿到的transaction id为889,sessionB的transaction id为890,那么sessionB更新完后数据的trx row_id为890。在查询V2的值时,sessionA发现数据版本为890,当前自己事务id为889,所以利用undo log“回滚”显示出上一版本的数据,上一版本可以是889也可能是比889小的trx row_id,取值。
1.4、隔离级别解决脏读、不可重复读、幻读
声明一点,读未提交、读提交、可重复读、串行化隔离级别依次递增,隔离得越严实,效率就越低。
脏读
事务读取到其他事务修改后未提交的值。
不可重复读
事务在启动时读取到的值,跟事务在执行过程中重新读取到的值不一样。
幻读
事务启动后,其他事务对数据库新插入的行被本事务读取到了。幻读仅专指新插入的行。
根据上述脏读、不可重复读、幻读的概念与隔离级别能做到的,我可以得出以下关系:
| 脏读 | 不可重复读 | 幻读 | |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 串行化 | 不可能 | 不可能 | 不可能 |
2、ACID
A.Atomicity.原子性
一个事务的操作是要么全部成功,要么全部失败。例如A给B转账100块,保证A账户会减少100块,B账户增加100块,这100块是A转过来的,不出现A账户减少了,B账户却没对应增加100块的情况。
原子性通过undo log实现,undo log记录了回滚信息,当事务失败或进行rollback时,就会根据信息回滚到操作前的样子。
当delete时,undo log会相应记录被删除数据的信息,当发生回滚时,进行insert操作。
当update时,undo log会记录更新操作的信息,当发生回滚时,进行update操作。
当insert时,undo log会记录对应的主键,当发生回滚时,进行delete操作。
C.Consistency.一致性
事务在执行前后,数据从一种合法的状态,转变成另一种合法的状态。怎么样才算合法,都是可以自己定义的,比如事务启动时A用户账户了1000块,B用户有500块,这个时候是启动时合法的状态。A用户向B用户转账100块,A用户账户余额变成900,B变成600,这是结束时合法的状态。或者平台会扣除1%的手续费,转账完成后A用户余额为899,B用户变成600,平台账户增加1块钱,这也是合法的状态。
一致性是通过原子性、隔离性、持久性来保证的。所以只有保证了AID特性,才能保证C特性。
I.Isolation.隔离性
一个事务的查询结果不会被其他事务所影响,事务之间是隔离的。例如X事务在启动时查询id=1的结果为1,X事务在没有对此记录进行修改操作的时候,多次查询结果还是为1,不管其他事务是否对此记录进行了修改。
隔离性通过MVCC实现,具体实现可以参考上面中的小测试逻辑。
D.Durability.持久性
一个事务执行完成了,所修改的数据能保存至数据库,不管宕机还是突然断电,数据库重新启动后查询到的数据为事务操作过后的数据。
持久性通过redo log实现,MySQL中有个WAL的逻辑,就是编辑数据时,会先更新redo log日志,再更新内存,最后再写磁盘。写redo log日志是一个非常快的操作,而更新完内存后,写进磁盘是很忙的。当还没写进磁盘时发生宕机等,MySQL重启后会自动去对比redo log日志与磁盘内容,把未刷进磁盘的数据刷进磁盘中,从而保证了数据的持久性。
3、扩展知识
undo log
回滚日志记录数据修改的逆向还原操作记录。例如数据值从1按顺序修改成了2、3、4。那么回滚日志就会按顺序记录:2->1,3->2,4->3
MVCC
Multi-Version Concurrency Control是MySQL数据库中的多版本并发控制,就是同一条记录在数据库中是可以存在多个版本。依靠undo log与乐观锁实现。
乐观锁
乐观地认为不会冲突,在最后需要修改数据的时候去拿一下锁认证一下就好。常见的方法有版本号控制和时间戳控制。
悲观锁
悲观的认为数据会被其他事务修改,所以在拿到数据时直接加上锁,阻止其他事务进行修改。例如for update。
【MySQL】事务隔离级别及ACID的更多相关文章
- Mysql事务-隔离级别
MYSQL事务-隔离级别 事务是什么? 事务简言之就是一组SQL执行要么全部成功,要么全部失败.MYSQL的事务在存储引擎层实现. 事务都有ACID特性: 原子性(Atomicity):一个事务必须被 ...
- MySQL事务隔离级别(一)
本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做 ...
- JDBC之事务隔离级别以及ACID特性
JDBC之事务隔离级别以及ACID特性 事务隔离级别: 1.更新遗失(Lost update) 两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了.这是因为系统没有 ...
- [51CTO]新说MySQL事务隔离级别!
新说MySQL事务隔离级别! 事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!本文所讲大部分内容,皆有官网作为佐证,因此对本文内 ...
- 查询mysql事务隔离级别
查询mysql事务隔离级别 查询mysql事务隔离级别 分类: DB2011-11-26 13:12 2517人阅读 评论(0) 收藏 举报 mysqlsessionjava 1.查看当前会话隔离 ...
- MySQL事务隔离级别测试实例
https://www.cnblogs.com/huanongying/p/7021555.html MySQL事务隔离级别 事务隔离级别 脏读 不可重复读 幻读 读未提交(read-uncommit ...
- MySQL事务隔离级别 解决并发问题
MySQL事务隔离级别 1. 脏读: 骗钱的手段, 两个窗口或线程分别调用数据库转账表,转账后未提交,对方查看到账后,rollback,实际钱没转. 演示方法: mysql默认的事务隔离级别为repe ...
- mysql事务隔离级别、脏读、幻读
Mysql事务隔离级别本身很重要,再加上可能是因为各大公司面试必问的缘故,在博客中出现的概率非常高,但不幸的是,中国的技术博客要么是转载,要么是照抄,质量参差不齐,好多结论都是错的,对于心怀好奇之心想 ...
- mysql事务隔离级别与设置
mysql数据库,当且仅当引擎是InnoDB,才支持事务: 1.隔离级别 事务的隔离级别分为:未提交读(read uncommitted).已提交读(read committed).可重复读(repe ...
- MySQL事务隔离级别(二)
搞清楚MySQL事务隔离级别 首先创建一个表 account.创建表的过程略过(由于 InnoDB 存储引擎支持事务,所以将表的存储引擎设置为 InnoDB).表的结构如下: 为了说明问题,我们打开两 ...
随机推荐
- Java面试-如何获取客户端真实IP
在进行一些小游戏开发时,我们经常比较关注的一个功能便是分享.针对分享,我们希望能根据各个城市或者地区,能有不同的分享文案,辨识地区的功能如果由服务器来完成的话,我们就需要知道客户端的真实IP.今天我们 ...
- IO流——File类(文件流类)
java语言的输入输出操作是借助于输入输出包java.io来实现的,按传输方向分为输入流与输出流,从外设传递到应用程序的流为输入流,将数据从应用程序输入到外设的流为输出流. File类的构造方法: 1 ...
- linux常见报错
零.目录 一. 文件和目录类 File exist 文件已经存在 No such file or directory 没有这个文件或目录(这个东西不存在) command not found 命令找不 ...
- Git同步更新操作GitHub和码云仓库上面的代码
一.前言 问题: 小编在生活中,一般都是将代码保存到github上,但由于国内的码云仓库确实速度比github快很多,用起来也很方便,于是后来就慢慢转码云了,当然小编在github上的代码也不想放弃更 ...
- 操作DOM会影响WEB应用的性能
平时在工作中,要优化自己开发的WEB应用的性能,一般是遵循以下几个原则: 1.减少网络请求. 2.压缩.合并静态资源文件,以此来减轻网络传输的带宽压力和资源消耗. 3.代码逻辑层面上的性能优化.比如减 ...
- netty源码解解析(4.0)-22 ByteBuf的I/O
ByteBuf的I/O主要解决的问题有两个: 管理readerIndex和writerIndex.这个在在AbstractByteBuf中解决. 从内存中读写数据.ByteBuf的不同实现主要 ...
- Hadoop集群常用的shell命令
Hadoop集群常用的shell命令 Hadoop集群常用的shell命令 查看Hadoop版本 hadoop -version 启动HDFS start-dfs.sh 启动YARN start-ya ...
- 03爬虫-requests模块基础(1)
requests模块基础 什么是requests模块 requests模块是python中原生基于网络模拟浏览器发送请求模块.功能强大,用法简洁高效. 为什么要是用requests模块 用以前的url ...
- 【面试必备】小伙伴栽在了JVM的内存分配策略。。。
周末有小伙伴留言说上周面试时被问到内存分配策略的问题,但回答的不够理想,小伙伴说之前公号里看过这一块的文章的,当时看时很清楚,也知道各个策略是干嘛的,但面试时脑子里清楚,心里很明白,但嘴里就是说不清楚 ...
- 用call或bind实现bind()
一.bind方法 让我们看一下MDN上对bind方法的解释 bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用 ...