码迷,mamicode.com
首页 > 数据库 > 详细

MYSQL事务隔离

时间:2015-07-24 20:30:26      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:

MySQL主要有4种类型的事务隔离级别,分别为:

read uncommitted 【读取未提交内容】

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

read committed  【读取提交内容

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

repeatable read  【可重读

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

serializable  【可串行化

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

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

脏读(Drity Read):

某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):

在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):

在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

接下来我们使用MySQL客户端来测试MySQL事务的不同隔离级别:

1、read uncommitted【读取未提交内容】

首先设置客户端1、客户端2当前session的隔离模式都为read uncommitted、注意有2个客户端都要设置,使用2个客户端的目的主要是为了模拟并发的情况

客户端1:

mysql1> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

mysql1> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set (0.00 sec)

客户端2:

mysql2> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

mysql2> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set (0.00 sec)

接着在客户端1查询下表数据,并且开始事务,更新数据:

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
+----+------+------+
2 rows in set (0.01 sec)

开始事务:

mysql1> begin;
Query OK, 0 rows affected (0.00 sec)

更新数据:

mysql1> update student set name = tom where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

客户端1事务开始操作完,并没有提交,这时候我们用客户端2模拟2个事务并发的情况。

客户端2首先开始事务,然后查询数据

开始事务:

mysql2> begin;
Query OK, 0 rows affected (0.00 sec)

查询数据:

mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | tom  |   18 |
+----+------+------+
2 rows in set (0.00 sec)

这时候我们看到,虽然客户端1的事务并没有commit提交,但是客户端2的事务已经查到了客户端1事务待提交的数据,但是如果此时客户端1rollback,

客户端1,事务回滚

mysql1> rollback;
Query OK, 0 rows affected (0.00 sec)

客户端2,再查询数据:

mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
+----+------+------+
2 rows in set (0.00 sec)

此时,客户端2里的事务就出现了所谓的脏读。

现在我们在客户端2里面,更新数据,更新成功

mysql2> update student set name = bob where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

客户端1,也更新数据,发现更新超时,也就是说,在一个事务A中更新数据,在未提交或者未回滚的这段时间里,这条数据将被锁定,这时候其他连接的更

新操作会被锁定,一直等待到超时或者等待事务A被提交或者被回滚才会进行更新操作。

mysql1> update student set name = test where id = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

 2.read committed 【读取提交内容】

首先设置客户端1、客户端2的事务隔离级别

客户端1:

mysql1> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql1> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)

客户端2:

mysql2> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql2> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)

接着我们先查询下客户端1的数据

客户端1:查询数据

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
+----+------+------+
2 rows in set (0.00 sec)

客户端1:开始事务:

mysql1> begin;
Query OK, 0 rows affected (0.00 sec)

客户端1:插入一条数据

mysql1> insert into student values(3, wmq, 20);
Query OK, 1 row affected (0.00 sec)

 数据插入成功,这时候我们转到客户端2:

开始事务,查询数据

mysql2> begin;
Query OK, 0 rows affected (0.00 sec)

mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
+----+------+------+
2 rows in set (0.00 sec)

这时候我们发现,客户端2并没有查询到客户端1中事务插入的数据

我们再转到客户端1:

执行commit提交

mysql1> commit;
Query OK, 0 rows affected (0.00 sec)

客户端2再进行查询:

mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | wmq  |   20 |
+----+------+------+
3 rows in set (0.00 sec)

数据出来了,这就是所谓的读取提交的内容,也就是在事务A内必须要在事务B提交之后才能出现。

3. repeatable read  【可重读

先设置客户端1,客户端2的事务隔离级别:

mysql1> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql1> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

客户端2:

mysql2> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql2> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

客户端1、客户端2查询数据,并且开始事务:

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | wmq  |   20 |
+----+------+------+
3 rows in set (0.00 sec)

mysql1> begin;
Query OK, 0 rows affected (0.00 sec)
mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | wmq  |   20 |
+----+------+------+
3 rows in set (0.00 sec)

mysql2> begin;
Query OK, 0 rows affected (0.00 sec)

接着我们在客户端2

更新数据:

mysql2> update student set name = mary where id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

然后再客户端1

查询:

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | wmq  |   20 |
+----+------+------+
3 rows in set (0.00 sec)

这时候我们发现,并没有引起脏读的问题。

接着我们在客户端2

插入数据:

mysql2> insert into student values(4,lucy,18);
Query OK, 1 row affected (0.00 sec)

然后到客户端1继续查询:

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | wmq  |   20 |
+----+------+------+
3 rows in set (0.00 sec)

发现也没有出现脏读。

把客户端2的事务提交:

mysql2> commit;
Query OK, 0 rows affected (0.01 sec)

接着再在客户端1查询:

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | wmq  |   20 |
+----+------+------+
3 rows in set (0.00 sec)

依然没有数据,由此我们得出结论,在repeatable read的这个隔离级别中,同一个事务,不会出现脏读,和不可重复读的情况。

接着我们rollback客户端2再查询

mysql2> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | mary |   20 |
|  4 | lucy |   18 |
+----+------+------+
4 rows in set (0.00 sec)

出来了,在repeatable read级别中,事务之间是相互独立的。

4. serializable  【可串行化】

 首先也是设置客户端1、客户端2的隔离级别

客户端1:

mysql1> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql1> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)

 客户端2:

mysql2> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql2> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)

首先我们在客户端2开始事务,并且执行更新操作

mysql2> begin;
Query OK, 0 rows affected (0.00 sec)

mysql2> update student set name = lucy where id = 4;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

然后客户端1也开始事务,执行查询

mysql1> begin;
Query OK, 0 rows affected (0.00 sec)

mysql1> select * from student;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

这时候发现客户端1的事务查询失败,说明在客户端2事务在开始更新的时候,记录已经被锁定,这导致客户端1里的事务1查询失败,一直需要等待解锁。

接着我们把客户端2的事务提交,查询数据

mysql2> commit;
Query OK, 0 rows affected (0.00 sec)

mysql2> begin;
Query OK, 0 rows affected (0.00 sec)

mysql2> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | mary |   20 |
|  4 | lucy |   18 |
+----+------+------+
4 rows in set (0.00 sec)

然后在客户端1的事务进行查询:

mysql1> select * from student;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | kate |   18 |
|  2 | jim  |   18 |
|  3 | mary |   20 |
|  4 | lucy |   18 |
+----+------+------+
4 rows in set (0.00 sec)

发现不影响,说明在读取数据相互都不影响。

接着我们继续在客户端2进行数据更新:

mysql2>  update student set name = wmq where id = 4;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

发现更新失败,说明客户端1的事务已经对数据加了读锁,导致客户端2更新失败。 

 

MYSQL事务隔离

标签:

原文地址:http://www.cnblogs.com/honeybaobao/p/4668549.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!