1. 脏读、幻读、不可重复读
脏读 :是指事物读取到其他事务没提交的数据
client1 开启事务查询了 id = 1 的数据,然后它跑去做别的事情(未提交),这时 client2对 id = 1 的数据进行了修改,然后他也跑去做别的事情(未提交),这时 client1 回来又查询了 id = 1 的数据,发现被改变了???然后带着疑惑又去做别的事情,client2 那边因为某个原因对 id = 1 的数据进行了回滚(rollback),而 client1 则又又查询了 id = 1 的数据,发现数据又变回来了,这下彻底懵了。client1:???
client1 : begin; # 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
client1 :select * from user where id = 1; # 查询 id = 1 的用户
mysql> select * from user where id = 1;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
+----+--------+
1 row in set (0.00 sec)
client2:begin;
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
client2:update user set name=‘李四’ where id = 1; # 在 client1 没有 commit 时进行修改
mysql> update user set name='李四' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
client1 :select * from user where id = 1; # 在 client2 没有 commit 或者 rollback 之前再次进行查询
mysql> select * from user where id = 1;
+----+--------+
| id | name |
+----+--------+
| 1 | 李四 |
+----+--------+
1 row in set (0.00 sec)
client2:rollback;# client2 因为未知原因进行了回滚
mysql> rollback;
Query OK, 0 rows affected (0.11 sec)
client1 :select * from user where id = 1; # 这时 client1 还没有 commit ,但是又拿到了不同的数据。
mysql> select * from user where id = 1;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
+----+--------+
1 row in set (0.00 sec)
在整个过程当中 client1 都是处于一脸懵逼的状态,为啥每次拿到的数据都是不一样的?啥情况?
幻读 :是指一次事务中前后数据量发生变化,用户产生不可预料的问题
client1 开启事务查询了 user 表所有的数据,之后进行了对表清空的动作,然后跑去做别的事情(未提交),这时,client2 对数据进行了 insert 操作,拍拍屁股走人了,client1 做完其他事情之后回来再次查询表的时候发现表中有有了数据。client1:???
client1 : begin; # 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
client1 : select * from user; # 先查看 user 表中的数据。
mysql> select * from user;
+----+--------+
| id | name |
+----+--------+
| 1 | 李四 |
| 2 | 王五 |
+----+--------+
2 rows in set (0.00 sec)
client1 :delete from user; # 对表进行清空操作
mysql> delete from user;
Query OK, 2 rows affected (0.00 sec)
client2 :begin; # 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
client2 :insert into user value(3,‘张三’); # 对 user 表插入一条数据
mysql> insert into user value(3,'张三');
Query OK, 1 row affected (0.14 sec)
client2 :commit; # 提交事务
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
client1 :select * from user; # 再次对自己清空过的 user 表查询。
mysql> select * from user;
+----+--------+
| id | name |
+----+--------+
| 3 | 张三 |
+----+--------+
1 row in set (0.00 sec)
clien1又懵了。 client1 :???
不可重复读 :是指在同一次事务前后查询不一致的问题
client1 开启事务查询了 id = 1 的数据,然后 client1 去做别的事情(未提交),但是就在它刚走,client2 对这条数据进行了修改,然后 client1 回来查询读取这条数据就会发现数据改变了。client1:???
client1 :begin; # 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
client1 :select * from user where id = 1; # 查询 id = 1 的用户
mysql> select * from user where id = 1;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
+----+--------+
1 row in set (0.00 sec)
client1 : select * from user where id = 2; # client1 查询完 id = 1 的数据之后并没有 commit ,而是去做别的操作,这时 client2 进行了修改
mysql> select * from user where id = 2;
+----+--------+
| id | name |
+----+--------+
| 2 | 王五 |
+----+--------+
1 row in set (0.00 sec)
client2 :begin; # 开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
client2 :update user set name=‘李四’ where id = 1; # 修改 id = 1 的数据
mysql> update user set name='李四' where id = 1;
Query OK, 1 row affected (0.35 sec)
Rows matched: 1 Changed: 1 Warnings: 0
client2 :commit; # 提交
mysql> rollback;
Query OK, 0 rows affected (0.11 sec)
client1 :select * from user where id = 1; # 在 client2 进行了修改后 client1 又回来查询,拿到了不一样的数据。
mysql> select * from user where id = 1;
+----+--------+
| id | name |
+----+--------+
| 1 | 李四 |
+----+--------+
1 row in set (0.00 sec)
这时 client1 又又又懵了,咋又不一样了?????????????????
2. 如何解决问题?答:MySQL 事务隔离级别
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
Repeatable Read(可重读)(Mysql默认为此项:REPEATABLE-READ)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
3. 总结
可能看完的小伙伴还是有一些不明白的地方,那么在这里总结一下:
- 脏读指读取到其他事务正在处理的未提交的数据。
- 不可重复度读指并发更新时,另一个事务前后查询相同数据时的数据不符合预期
- 幻读指并发新增、删除这种会产生数量变化的操作时,另一个事务前后查询相同数据时的不符合预期