什么是脏读、不可重复读、幻读

不同的数据库默认具有不同的隔离级别,不同的隔离级别,在并发事务时可能会出现不同的问题。 大多数数据库支持四种隔离级别。

  • 未提交读(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如何实现可重复读的隔离级别

通过MVCC机制实现;

posted @ 2022-10-31 10:07  岁月记忆  阅读(500)  评论(0)    收藏  举报