脏读、幻读、不可重复读
- 脏读 dirty read
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
- 不可重复读 nonrepeatable read
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)
- 幻读 phantom read
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象
发生了幻觉一样。
参考: https://blog.csdn.net/jiesa/article/details/51317164
隔离级别
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
- Repeatable read (可重复读):可避免脏读、不可重复读的发生。
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。
- Read committed (读取已提交的):可避免脏读的发生。
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
- Read uncommitted (读未提交):最低级别,任何情况都无法保证。
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。
如下表所示:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
- √: 可能出现 ×: 不会出现
MySQL 命令
- 开始事务
start transaction;
- 提交事务
commit;
- 回滚事务
rollback;
- 查看并设置事务的隔离级别,设置为"read-uncommitted"隔离级别
select @@tx_isolation;
set tx_isolation='read-uncommitted';
- 查看和设置事务的自动提交,设置为不自动提交事事务
show variables like '%autocommit%';
set autocommit = off;
MySQL环境
mubideMacBook-Pro:kafka-doc mubi$ mysql --version
mysql Ver 14.14 Distrib 5.6.40, for macos10.13 (x86_64) using EditLine wrapper
1 设置隔离级别为如“read-uncommitted”
两个mysql 客户端A,B, 都设置为 "read-uncommitted"隔离级别,A设置事务不自动提交。
A | B | 说明 |
---|---|---|
select * from tb_pet; | select * from tb_pet; | A,B获取到的数据记录一致 |
start transaction | - | 事务A开始 (事务A未提交) |
insert into tb_pet values(4,“mouse”,1,30); | - | - |
select * from tb_pet; | select * from tb_pet; | 读取的记录也一样,都增加了一条记录,对于B事务来说是脏读 |
commit; | - | 事务A提交 |
insert into tb_pet values(4,“mouse”,1,30); | - | A开始一个新事物自动提交 |
- | select * from tb_pet; | B读取的记录又增加了一条记录,正常 |
rollback; | - | 插入记录的事务A回滚 |
- | select * from tb_pet; | 事务正确回滚,B读取不到相应的记录了 |
2 设置隔离级别为如“read-committed”
两个mysql 客户端A,B, 都设置为 "read-committed"隔离级别,A设置事务不自动提交,B设置为事务不自动提交。
A | B | 说明 |
---|---|---|
select * fromn tb_pet; | select * fromn tb_pet; | A,B获取到的数据记录一致 |
start transaction | - | 事务A开始 (事务A未提交) |
- | start transaction | 事务开始(事务B未提交) |
- | insert into tb_pet values(5,“mouse”,1,500); | B插入数据(事务B未提交) |
select * from tb_pet; | select * from tb_pet; | A没有获取到B插入的数据,不产生脏读 |
- | commit; | 事务B提交 |
select * from tb_pet; | - | A读取到B新增的数据了,产生可重复读(此时事务A并未提交) |
3 设置隔离级别为“repeatable-read"
两个mysql 客户端A,B, 都设置为 "repeatable-read"隔离级别,A设置事务不自动提交,B设置为事务不自动提交。
A | B | 说明 |
---|---|---|
select * fromn tb_pet; | select * fromn tb_pet; | A,B获取到的数据记录一致 |
start transaction | - | 事务A开始 (事务A未提交) |
- | start transaction | 事务开始(事务B未提交) |
- | insert into tb_pet values(6,“bull”,1,1000); | B插入数据(事务B未提交) |
select * from tb_pet; | select * from tb_pet; | A没有获取到B插入的数据,不产生脏读 |
- | commit; | 事务B提交 |
select * from tb_pet; | - | A读取不到B新增的数据了,不产生可重复读(此时事务A并未提交) |
insert into tb_pet values(6,“bull”,1,1000); | A插入失败,报Duplicate key错误(i是主键),产生幻读,A查询不到id=6的记录,理论上可以插入该记录,但事实上B已经插入了这条记录,对于A来说幻读了id=6的记录 |
4 设置隔离级别为“serializable"
两个mysql 客户端A,B, A设置为 "serializable"隔离级别,B设置为"repeatable-read"隔离级别,A设置事务不自动提交,B设置为事务不自动提交。
A | B | 说明 |
---|---|---|
select * fromn tb_pet; | select * fromn tb_pet; | A,B获取到的数据记录一致 |
start transaction | - | 事务A开始 (事务A未提交) |
- | start transaction | 事务开始(事务B未提交) |
- | insert into tb_pet values(7,“eee”,1,1000); | B插入数据(事务B未提交) |
select * from tb_pet; | - | 这里A读取会一一直等待,读取不到 |
- | commit | 事务B提交 |
- | - | A上一步的的select返回了 |
转载:
-
MYSQL隔离级别及测试包括脏读,幻读.小黑手600[EB/OL]https://blog.csdn.net/johnstrive/article/details/46724315
-
数据库事务的四大特性以及事务的隔离级别.fjdingsd[EB/OL]https://www.cnblogs.com/fjdingsd/p/5273008.html