脏读、幻读、不可重复读和丢失更新

avatar 2018年07月04日19:31:55 6 2836 views
博主分享免费Java教学视频,B站账号:Java刘哥

锁就是防止其他事务访问指定资源的手段。锁是实现并发控制的主要方法,是多个用户能够同时操纵同一个数据库中的数据而不发生数据不一致现象的重要保障。 一般来说,锁可以防止脏读、不可重复读和幻读。



1、脏读(Dirty Read)


针对未提交数据

详细解释:当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。


事务T1:更新一条数据
-->事务T2:读取事务T1更新的记录
事务T1:调用commit进行提交
此时事务T2读取到的数据是保存在数据库内存中的数据,称为脏数据,这个过程称为脏读。


脏读发生在一个事务A读取了被另一个事务B修改,但是还未提交的数据。假如B回退,则事务A读取的是无效的数据。这跟不可重复读类似,但是第二个事务不需要执行提交。


解决脏读问题:修改时加排他锁,直到事务提交后才释放,读取时加共享锁,读取完释放事务1读取数据时加上共享锁后(这样在事务1读取数据的过程中,其他事务就不会修改该数据),不允许任何事务操作该数据,只能读取,之后1如果有更新操作,那么会转换为排他锁,其他事务更无权参与进来读写,这样就防止了脏读问题。但是当事务1读取数据过程中,有可能其他事务也读取了该数据,读取完毕后共享锁释放,此时事务1修改数据,修改完毕提交事务,其他事务再次读取数据时候发现数据不一致,就会出现不可重复读问题,所以这样不能够避免不可重复读问题。





















































时间转账事务A取款事务B
T1开始事务
T2开始事务
T3查询账户余额为1000元
T4取出500元余额修改为500元
T5查询账户余额为500元(脏读)
T6撤销事务余额恢复为1000元
T7汇入100元把余额修改为600元
T8提交事务

 


2、幻读(Phantom)


针对其他提交前后,读取数据条数的对比

详细解释:幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。


事务T1:查询表中所有记录
-->事务T2:插入一条记录
-->事务T2:调用commit进行提交
事务T1:再次查询表中所有记录


此时事务T1两次查询到的记录是不一样的,称为幻读。

注意:幻读重点在新增或删除。


幻读发生在当两个完全相同的查询执行时,第二次查询所返回的结果集跟第一个查询不相同。


发生的情况:没有范围锁。


如何避免:实行序列化隔离模式,在任何一个低级别的隔离中都可能会发生。


解决幻读问题:采用的是范围锁RangeS RangeS_S模式,锁定检索范围为只读,这样就避免了幻读问题。




3、不可重复读(Nonrepeatable Read)


针对其他提交前后,读取数据本身的对比

事务T1:查询一条记录


-->事务T2:更新事务T1查询的记录


-->事务T2:调用commit进行提交


事务T1:再次查询上次的记录


此时事务T1对同一数据查询了两次,可得到的内容不同,称为不可重复读。

注意:不可重复读重点在修改。


在基于锁的并行控制方法中,如果在执行select时不添加读锁,就会发生不可重复读问题。


在多版本并行控制机制中,当一个遇到提交冲突的事务需要回退但却被释放时,会发生不可重复读问题。


有两个策略可以防止这个问题的发生:


(1) 推迟事务2的执行,直至事务1提交或者回退。这种策略在使用锁时应用。


(2) 而在多版本并行控制中,事务2可以被先提交,而事务1继续执行在旧版本的数据上。当事务1终于尝试提交时,数据库会检验它的结果是否和事务1、事务2顺序执行时一样。如果是,则事务1提交成功;如果不是,事务1会被回退。


解决不可重复读问题:读取数据时加共享锁,写数据时加排他锁,都是事务提交才释放锁。读取时候不允许其他事物修改该数据,不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题。


 

4、丢失更新(Lost Update) 


事务T1读取了数据,并执行了一些操作,然后更新数据。事务T2也做相同的事,则T1和T2更新数据时可能会覆盖对方的更新,从而引起错误。

5.处理以上隔离级别的问题,采用如下方法:


  事务隔离五种级别:
(1)TRANSACTION_NONE  不使用事务。
(2)TRANSACTION_READ_UNCOMMITTED  允许脏读。
(3)TRANSACTION_READ_COMMITTED  防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。


(4)TRANSACTION_REPEATABLE_READ  可以防止脏读和不可重复读。
(5)TRANSACTION_SERIALIZABLE  可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率。

  以上的五个事务隔离级别都是在Connection接口中定义的静态常量,使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。


  如:con.setTransactionIsolation(Connection.REPEATABLE_READ)。


  注意:事务的隔离级别受数据库的限制,不同的数据库支持的的隔离级别不一定相同。


 

 

不可重复读和幻读区别


对于不可重复读和幻读,可以借用下面的例子理解:

 

不可重复读

不可重复读的重点是修改:


同样的条件, 你读取过的数据, 再次读取出来发现值不一样了


例子:


在事务1中,Mary 读取了自己的工资为1000,操作并没有完成 



con1 = getConnection();  
select salary from employee empId ="Mary"; 

在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务. 
con2 = getConnection();  
update employee set salary = 2000;  
con2.commit();  

在事务1中,Mary 再次读取自己的工资时,工资变为了2000 

//con1  
select salary from employee empId ="Mary";  

在一个事务中前后两次读取的结果并不致,导致了不可重复读。

 

幻读

幻读的重点在于新增或者删除 (数据条数变化)

同样的条件, 第1次和第2次读出来的记录数不一样


例子:


目前工资为1000的员工有10人。
事务1,读取所有工资为1000的员工。 



con1 = getConnection();  
Select * from employee where salary =1000;  

共读取10条记录 

这时另一个事务向employee表插入了一条员工记录,工资也为1000 
con2 = getConnection();  
Insert into employee(empId,salary) values("Lili",1000);  
con2.commit();  

事务1再次读取所有工资为1000的员工 
//con1  
select * from employee where salary =1000;

共读取到了11条记录,这就产生了幻读。 

 

原文地址:https://blog.csdn.net/qq_33290787/article/details/51924963

 

最后附一张数据库隔离级别和脏读,幻读,不可重复读的关系最后附一张图
796066-20170402181558649-581777223.jpg



  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
匿名评论,评论回复后会有邮件通知

  

已通过评论:0   待审核评论数:0