幻读(PHANTOM READ)又被称为虚读,是指在一个事务内两次查询中数据条数不一致,幻读和不可重复读有些类似,同样是在两次查询过程中,不同的是,幻读是由于其他事物做了插入记录的操作,导致记录数有所增加。
例如,银行在做统计报表时统计account表中所有用户的总额时,此时总共有三个账户总共金额有3000,这时新增了一个账户,并且存入了1000元,这时银行在统计时发现账户的总金额变为4000,造成了幻读的情况,接下来就通过案例来演示幻读的情况,具体步骤如下。
1)设置b账户的隔离级别
b账户:由于前面将事物的隔离级别设置为REPEATABLE READ(可重复读),这种隔离级别可以避免幻读的出现,因此需要将事务的隔离级别设置的更低,下面将事务的隔离级别设置为READ COMMITTED,具体语句如下:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
上述语句执行成功后,b账户事务的隔离级别为READ COMMITTED。
2)演示幻读
b账户,首先在b账户中开启一个事务,然后再当前事务中查询账户的余额信息,查询结果如下:
a账户:在对a账户进行添加操作之前,使用SELECT语句查看当前a账户中的信息,执行语句如下所示:
接下来对a账户进行添加操作,a账户不用开启事务,直接执行添加操作即可,具体语句如下:
INSERT INTO account (name , money) VALUES ('c' , 1000);
b账户:当a账户添加记录成功后,可以在b账户中再次查询账户的与余额信息,查询结果如下:
通过对比币账户设置READ COMMITTED隔离级别前后,发现第二次查询数据时比第一次查询时多了一条记录,这种情况并不是错误的,但可能不符合实际需求。需要注意的是,上述情况演示完成后,将b账户中的事物提交。
3)重新设置b账户的隔离级别
b账户:为了防止出现幻读,可以将b账户的隔离级别设置为REPEATABLE READ,具体语句如下:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
上述语句执行成功后,事务的隔离级别被设置为REPEATABLE READ。
4)验证是否出现幻读
b账户:在b账户中重新开启一个事务,并在该事务中查询当前账户的余额信息,查询结果如下:
a账户:在对a账户进行添加操作之前,使用select语句查看当前a账户中的信息,执行语句如下所示:
接下来对a账户进行添加操作,在a账户中不开启事务,直接执行添加操作,具体语句如下:
INSERT INTO account (name , money) VALUES ('d' , 1000);
b账户:当a账户的添加操作执行成功后,再次查询当前账户的余额信息,查询结果如下:
对比b账户的两次查询结果可以看出,在同一事务中两次的查询结果是一致的,并没有出现重复读取的情况,因此可以说明当前事务的隔离级别可以避免幻读,最后还需要使用COMMIT语句提交当前事务,提交后的账户查询结果如下所示:
4.可串行化
可串行化(SERIALIZABLE)是事务的最高隔离级别,它在每个读的数据行上加上锁,使之不可能相互冲突,因此会导致大量的超时现象,接下来通过具体的案例来演示,具体步骤如下。
1)设置b账户中事务的隔离级别
b账户:首先将b账户的隔离级别设置为SERIALIZABLE,具体语句如下:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
上述语句执行成功后,当前事务的隔离级别被成功设置为SERIALIZABLE。
2)演示可串行化
b账户:首先在b账户中开启事务,然后使用SELECT语句查询各个账户的余额信息,查询结果如下:
a账户:开启一个事务,并在该事务中执行插入操作,执行结果如下:
从上述执行结果可以看出,当b账户正在事务中查询余额信息时,a账户中的操作是不能立即执行的。
3)提交事务
b账户:当b账户查询完余额信息后,立即提交当前事务,具体语句如下:
a账户:当b账户中的事务提交成功后,a账户中的添加操作才能执行成功,并输出如下语句:
从上述情况可以说明,如果一个事务使用了SERIALIZABLE(可串行化)隔离级别时,在这个事务没有被提交前,其他的线程只能等到当前操作完成后,才能进行操作,这样会非常耗时,而且会影响数据库的性能,通常情况下是不会使用这种隔离级别的。
网友评论