什么是脏读、不可重复读、幻读
不同的数据库默认具有不同的隔离级别,不同的隔离级别,在并发事务时可能会出现不同的问题。 大多数数据库支持四种隔离级别。
-
未提交读(Read uncommited)
-
已提交读(Read commited),Oracle数据库的默认隔离级别
-
可重复读(Repeatable Read),Mysql数据库的隔离级别
-
串行化(Serializable ),并发的事务按照顺序执行,类似于排队的概念,好处是安全,坏处是效率低
| 隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
|---|---|---|---|
| 未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
| 已提交读(Read committed) | 不可能 | 可能 | 可能 |
| 可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
| 串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
不同的隔离级别会出现不同的问题;
# 查看当前数据库隔离级别:
# mysql中,系统内置变量有两个@@
SELECT @@tx_isolation; # 适用mysql8以下的版本
select @@transaction_isolation; # 适用mysql8的版本
#设置本次会话为read uncommitted级别:
set session transaction isolation level read uncommitted;
#设置本次会话为read committed级别:
set session transaction isolation level read committed;
#设置本次会话为repeatable read级别:
set session transaction isolation level repeatable read;
#设置本次会话为serializable级别:
set session transaction isolation level serializable;
什么脏读(Read Uncommitted)
通俗的讲,一个事务在处理过程中读取了另外一个事务未提交的数据。 你都还没提交,我就读到了你刚操作的数据,万一你回滚了怎么办,你说这脏不脏。 举例:

A事务
-- 将当前会话的事务隔离级别设置为读未提交
set session transaction isolation level read uncommitted;
-- 查询当前事务隔离级别
select @@tx_isolation;
-- 和另外一个对数据库更改操作的事务同时执行,注意执行的顺序
-- 1. 开启事务
start TRANSACTION;
-- 4. 当前事务再次查询,当前事务读取到了另外一个事务没有提交的数据
-- 称为脏读
select * from actor where actor.actor_id = 1;
B事务
-- 2. 开启事务
start TRANSACTION;
-- 3. 修改id为1的数据
update actor set actor.actor_name = '周星驰' where actor_id = 1;
-- 5. 要么提交,要么回滚
rollback;
不可重复读(Non-repeatable Read)
通俗的讲,一个事务范围内,多次查询某个数据,却得到不同的结果。 与脏读的区别:脏读是读到未提交的数据,而不可重复读读到的却是已经提交的数据,但实际上是违反了事务的一致性原则。 举例:
A事务
#设置本次会话为read committed级别:
set session transaction isolation level read committed;
# 查看事务隔离级别
SELECT @@tx_isolation;
-- 1.开启A事务
START TRANSACTION;
-- 3.A事务执行第一次查询语句
select * from actor where actor.actor_id = 1;
-- 5.A事务再次执行相同的查询语句
select * from actor where actor.actor_id = 1;
-- 7.A事务在B事务提交后,再次执行相同的查询语句
select * from actor where actor.actor_id = 1;
B事务
-- 2.开启B事务
START TRANSACTION;
-- 4.在B事务中修改id为1的数据
update actor set actor.actor_name = '周星驰' where actor_id = 1;
-- 6.B事务提交数据
commit;
幻读
在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。 事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。 举例:
看到了吗,在一个事务A中,第一次查询某条记录,是没有的,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。 实际上,在InnoDB引擎中,对于索引的扫描,不仅锁住扫描到的索引,而且还锁住这些索引覆盖的范围(gap锁),因此这个范围是内插入数据是不允许的。
A事务
#设置本次会话为read committed级别:
set session transaction isolation level repeatable read;
# 查看事务隔离级别
SELECT @@tx_isolation;
# mysql默认事务的隔离级别为可重复读,解决了读已提交隔离级别下,同一个事务执行同一条sql语句
# 查询到不同的结果
-- 1.开启A事务
START TRANSACTION;
-- 3.A事务执行第一次查询语句
select * from actor where actor.actor_id = 1;
-- 5.A事务再次执行相同的查询语句
select * from actor where actor.actor_id = 1;
-- 7.A事务在B事务提交后,再次执行相同的查询语句
select * from actor where actor.actor_id = 1;
-- 8.已知A事务查询到了演员名称为刘德华
-- 但是我能够做一次反逻辑的操作
-- 对周星驰的数据做DML操作就会出现幻读的情况。
-- 明明就没有查询出id为1的周星驰,结果还能够更改它,就更发生了幻觉一样
update actor set actor_name = '吴孟达' where actor_id = 1 and actor_name = '周星驰';
select * from actor where actor.actor_id = 1;
-- 8. 明明就没有查询到id为7的数据,结果还能够更改
-- 更改id为7的数据后,再次执行查询,莫名其妙的多出了id为7的数据
-- 就更出现了幻觉一样。则为幻读
update actor set actor_name = '张曼玉' where actor_id = 7;
select * from actor;
commit;
B事务
-- 2.开启B事务
START TRANSACTION;
-- 4.在B事务中修改id为1的数据
update actor set actor.actor_name = '周星驰' where actor_id = 1;
-- 6.B事务提交数据
commit;
自学mysql如何实现可重复读的隔离级别

浙公网安备 33010602011771号