MySQL事务隔离级别解密
- READ UNCOMMITTED 未提交读
在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称作脏读。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但确缺乏其他级别的很多好处,除非真的有必要的理由,在实际应用中一般很少使用。 - READ COMMITED 提交读
大多数数据库系统的默认隔离级别是 READ COMMITED(但mysql不是)。READ COMMITED满足前面提到的隔离性简单定义:一个事务开始时,只能看见已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。 - REPEATABLE READ 可重复写
MySQL的默认事务隔离级别。
REPEATABLE READ解决了脏读的问题。该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另一个幻读的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。 - SERIALIZABLE 可串行化
SERIALIZABLE 是最高的隔离级别。它通过强事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在曲度的每一行数据都加上锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据一致性而且可以接受没有并发的情况下,才考虑使用该级别。
实验
上面是摘自《高性能MySQL》一书,光这么看,可能也理解不到多少东西,不如直接做实验亲身体验一下,加深理解。
我们先简单创建一个表 users,结构如下:
+------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+-------------------+----------------+
| id | int(11) unsigned | NO | PRI | <null> | auto_increment |
| name | varchar(50) | NO | | | |
| created_at | datetime | NO | | CURRENT_TIMESTAMP | |
| updated_at | datetime | NO | | CURRENT_TIMESTAMP | |
| deleted_at | datetime | YES | | <null> | |
+------------+------------------+------+-----+-------------------+----------------+
实现插入三条数据:
+----+-------------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+-------------+---------------------+---------------------+------------+
| 1 | coolcao2018 | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+-------------+---------------------+---------------------+------------+
然后打开两个终端,A和B分别表示两个用户同时在操作。
READ UNCOMMITTED
对于用户A,操作:
mysql root@localhost:test> set session transaction isolation level read uncommitted;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> select * from users;
+----+-------------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+-------------+---------------------+---------------------+------------+
| 1 | coolcao2018 | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+-------------+---------------------+---------------------+------------+
然后,用户B操作,
mysql root@localhost:test> set session transaction isolation level read uncommitted;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> update users set name='coolcao' where id=1;
此时,用户B并未提交事务,用户A进行查询操作看看:
mysql root@localhost:test> select * from users;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+---------+---------------------+---------------------+------------+
从整个过程来看,用户B还并未提交事务,但是A却已经能够直接读到B的更新。
从上面实验结果来看,不难理解上面对于 READ UNCOMMITTED级别的描述: 在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。
READ COMMITTED
同时将A,B两个终端的事务级别设置为 read committed:
// 在A,B两个终端都执行
set session transaction isolation level read committed;
对于A,我们开启一个事务,然后更新一下数据,但并不提交事务:
mysql root@localhost:test> set session transaction isolation level read committed;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> select * from users;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+---------+---------------------+---------------------+------------+
mysql root@localhost:test> update users set name='coolcao222' where id=1;
mysql root@localhost:test> select * from users;
+----+------------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+------------+---------------------+---------------------+------------+
| 1 | coolcao222 | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+------------+---------------------+---------------------+------------+
然后,在B终端,开启另外一个事务,进行数据查询:
mysql root@localhost:test> set session transaction isolation level read committed;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> select * from users;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+---------+---------------------+---------------------+------------+
然后,将A事务提交:
commit;
这时,再在B查询 :
mysql root@localhost:test> select * from users;
+----+------------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+------------+---------------------+---------------------+------------+
| 1 | coolcao222 | 2018-05-28 12:48:43 | 2018-05-28 12:48:43 | <null> |
| 2 | tom | 2018-05-28 13:24:28 | 2018-05-28 13:24:28 | <null> |
| 3 | lili | 2018-05-31 08:54:28 | 2018-05-31 08:54:28 | <null> |
+----+------------+---------------------+---------------------+------------+
从结果来看,也不难理解 read committed级别,对于一个事务,只能读取到当前事务的数据和其他已经提交的事务的数据,对于其他未提交事务的数据,读不到。
而且,从上面的实验结果中,我们也看到了,会话B在会话A提交事务前后查询的结果并不一致,这也就是上面所说的,不可重复读。
REPEATABLE READ 可重复读
我们将会话A设置为REPEATABLE READ :
mysql root@localhost:test> set session transaction isolation level repeatable read;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> select * from users;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-08-10 15:21:02 | 2018-08-10 15:21:02 | <null> |
| 2 | tom | 2018-08-10 15:21:07 | 2018-08-10 15:21:07 | <null> |
| 3 | lili | 2018-08-10 15:21:11 | 2018-08-10 15:21:11 | <null> |
+----+---------+---------------------+---------------------+------------+
此时,我们在B终端插入一条数据:
mysql root@localhost:test> insert into users (id,name) values (4,'coco');
mysql root@localhost:test> commit;
mysql root@localhost:test> select * from users;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-08-10 15:21:02 | 2018-08-10 15:21:02 | <null> |
| 2 | tom | 2018-08-10 15:21:07 | 2018-08-10 15:21:07 | <null> |
| 3 | lili | 2018-08-10 15:21:11 | 2018-08-10 15:21:11 | <null> |
| 4 | coco | 2018-08-10 15:23:50 | 2018-08-10 15:23:50 | <null> |
+----+---------+---------------------+---------------------+------------+
在终端B中,插入一条记录,并提交,这时id=4的用户已经被插入到数据库。
此时,再回到终端A,查询:
mysql root@localhost:test> select * from users;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-08-10 15:21:02 | 2018-08-10 15:21:02 | <null> |
| 2 | tom | 2018-08-10 15:21:07 | 2018-08-10 15:21:07 | <null> |
| 3 | lili | 2018-08-10 15:21:11 | 2018-08-10 15:21:11 | <null> |
+----+---------+---------------------+---------------------+------------+
哎,查询的结果中,没有B刚插入的id=4的用户,这也就是说该级别的事务隔离,保证了在同一个事务中多次读取同样的记录的结果是一致的。这时,我们在A中插入一条记录:
mysql root@localhost:test> insert into users (id,name) values (4,'coco');
(1062, u"Duplicate entry '4' for key 'PRIMARY'")
哎,这个时候,数据库报错了,提示主键重复。明明我在这个事务中,查询的数据只有1,2,3,为什么插入4的时候提示主键冲突呢?是发生幻觉了么?是的,发生“幻读”了。由于REPEATABLE READ级别的隔离,在一个事务中,多次读取同样记录的结果是一致的,在这多次读取之间,被别的事务插入了新的数据,这时前事务再插入数据,必然会导致错误。
SERIALIZABLE 可串行化
我们将A,B同时设置为SERIALIZABLE, 然后在A开启是个事务,做一个简单查询:
mysql root@localhost:test> set session transaction isolation level serializable;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> select * from users where id<10;
+----+---------+---------------------+---------------------+------------+
| id | name | created_at | updated_at | deleted_at |
+----+---------+---------------------+---------------------+------------+
| 1 | coolcao | 2018-08-10 15:21:02 | 2018-08-10 15:21:02 | <null> |
| 2 | tom | 2018-08-10 15:21:07 | 2018-08-10 15:21:07 | <null> |
| 3 | lili | 2018-08-10 15:21:11 | 2018-08-10 15:21:11 | <null> |
| 4 | coco | 2018-08-10 15:23:50 | 2018-08-10 15:23:50 | <null> |
| 5 | juli | 2018-08-10 15:31:32 | 2018-08-10 15:31:32 | <null> |
+----+---------+---------------------+---------------------+------------+
此时,A事务并未提交,然后在B再开启一个事务,进行插入操作:
mysql root@localhost:test> set session transaction isolation level serializable;
mysql root@localhost:test> start transaction;
mysql root@localhost:test> insert into users (id,name) values (6,'kate');
(1205, u'Lock wait timeout exceeded; try restarting transaction')
你会发现,哎我去,B事务被挂住了,然后过了一段时间,提示了错误 (1205, u'Lock wait timeout exceeded; try restarting transaction')
,说等待锁超时。
是的,在串行化级别,会在读取的每一行数据都加上锁,也就是说,上面A事务在读取时,已经加了锁,此时B事务在插入操作时,得等待锁的放开,时间一长,A锁未放开,B就报错了。
从实验中可以看出,可串行化级别,由于要保证避免幻读而加了锁导致效率以及可能会触发的等待锁超时等错误,实际应用中,该级别的事务隔离也很少使用。
对照着实验结果,来理解上面四个隔离级别,就容易理解了。
1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。
3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加群。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加群。
5.群号 468947140,点击链接加入群聊【Java-BATJ企业级资深架构】:https://jq.qq.com/?_wv=1027&k=57VIGuh
6.阿里Java高级大牛直播讲解知识点,分享知识,知识点都是各位老师多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!
MySQL事务隔离级别解密相关推荐
- 5、MySQL事务隔离级别详解
事务的隔离性就是指当多个事务同时运行时,各事务之间相互隔离,不可互相干扰.如果事务没有隔离性,就容易出现脏读.不可重复读和幻读等情况. 为了保证并发时操作数据的正确性,数据库都会有事务隔离级别的概念. ...
- Mysql事务隔离级别及MVCC(多版本并发控制)
一.MySQL事务隔离级别 先注明一点:以下讨论都是在多事务并发的情境下讨论的 事务的特性(InnoDB引擎才有事务): ACID 原子性:一个事务不可再分割,要么都执行要么都不执行 一致性:一个事务 ...
- mysql 事务 隔离级别_MySQL的四种事务隔离级别
https://www.cnblogs.com/huanongying/p/7021555.html 本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的 ...
- mysql事务隔离级别 花_mysql事务隔离级别
很多PHP开发者在面试的时候遇到这个问题都会卡壳.这是因为理解得不够透彻,今天让我带领大家梳理一下mysql事务隔离级别 数据库有四种隔离级别,分别是Read uncommitted,Read com ...
- mysql事务隔离级别之锁实现原理,脏读、不可重复读、幻读出现原因及解决方案
mysql事务隔离级别原理 观看了很多网上的博客,挺令人伤心,很难找到想要的答案... 一:所需知识 1,mysql中的锁 1.1,读锁(共享锁) 规则:若事务1对数据对象A加上读锁,则事务1只能读A ...
- 揭秘Mysql事务隔离级别之可重复读
揭秘Mysql事务隔离级别之可重复读 1.可重复读的来源 2.何为不可重复读 3.那么可重复读和不可重复读究竟有什么关系呢? 4.模拟不同事务隔离级别对不可重复的处理情况(有线程执行顺序). 4.1. ...
- mysql 事务隔离级别实现原理_MySQL事务隔离级别和实现原理 - 米扑博客
开发中经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗, 事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢? MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引 ...
- Java面试题及答案2019版(下),mysql事务隔离级别原理
答:TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小.TreeMap要求存放的键值对映射的 ...
- MySQL事务隔离级别介绍
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. Read Uncommitted(读 ...
最新文章
- (转)ArcEngine读取数据(数据访问)
- python中文名字叫什么-Python为什么取名为Python,很少人知道
- python django 模板
- java 小数运算 少0.1_计算器是如何实现0.1的?
- Python 爬虫进阶一之爬虫框架概述
- 一张图读懂什么是专属分布式存储
- php中sha1,PHP中sha1()函数和md5()函数的绕过
- mysql如何开启对外连接?
- 删除Win7通知区域的无效图标
- vue 基于 exceljs 导出 excel实战示例
- matlab用辛普森公式求积分_1.1高等数学公式大全
- SecureCRT Win免安装版本,简单好用
- 微信小程序广告接入,小程序加广告
- Tomcat NIO、APR 对比
- 免证书发布ipa文件真机测试
- 古月居ROS入门21讲——10-12.Publisher和Subscriber的编程实现 话题消息的定义与使用
- CVE-2020-2905: VirtualBox 虚拟机逃逸漏洞通告
- 视频教程-深度学习与TensorFlow 2入门实战-深度学习
- 三星s9android recovery,三星手机进入recovery模式的方法_三星各型号手机recovery模式怎么进入...
- javaweb校园订餐系统
热门文章
- sprintboot入门
- 数据结构和算法面试题系列—二叉树面试题汇总
- 「每天一道面试题」谈String和StringBuffer、StringBuilder区别
- hadoop-0.20.1+120 hive-0.3.99.1+0 试用hwi(hive web interface
- 沿海产业基地与海润光伏共建产业园开工在即
- eclipse没有Web分支Dynamic Web Project
- PHP自己实现var_dump函数
- MYSQLmy-innodb-heavy-4G.cnf配置文件注解
- Hibernate中的命名SQL查询
- Android 模拟器 GPU ON