并发一致性问题

1. 丢失修改

   T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。

 

2. 脏读

   (针对未提交数据)如果一个事务中对数据进行了更新,但事务还没有提交,另一个事务可以 “看到” 该事务没有提交的更新结果,这样造成的问题就是,如果第一个事务回滚,那么,第二个事务在此之前所 “看到” 的数据就是一笔脏数据。 (脏读又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。 )

例子:

  Mary 的原工资为 1000, 财务人员将 Mary 的工资改为了 8000 (但未提交事务)

  Mary 读取自己的工资,发现自己的工资变为了 8000,欢天喜地!

  而财务发现操作有误,回滚了事务,Mary 的工资又变为了1000

  像这样,Mary记取的工资数8000是一个脏数据。

解决办法:

  把数据库的事务隔离级别调整到 READ_COMMITTED

图解:

  T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。

 

3. 不可重复读

  是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(同时操作,事务1分别读取事务2操作时和提交后的数据,读取的记录内容不一致。不可重复读是指在同一个事务内,两个相同的查询返回了不同的结果。 )

例子:

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

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

(2)在事务2中,这时财务人员修改了 Mary 的工资为 2000,并提交了事务.

con2 = getConnection();  
update employee set salary = 2000;  
con2.commit();  

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

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

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

解决办法:

  如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。把数据库的事务隔离级别调整到REPEATABLE_READ

图解:

  T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。

 

4. 幻读

  事务 T1 读取一条指定的 Where 子句所返回的结果集,然后 T2 事务新插入一行记录,这行记录恰好可以满足T1 所使用的查询条件。然后 T1 再次对表进行检索,但又看到了 T2 插入的数据。 (和可重复读类似,但是事务 T2 的数据操作仅仅是插入和删除,不是修改数据,读取的记录数量前后不一致)

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

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

例子:

目前工资为1000的员工有10人。 (1)事务1,读取所有工资为 1000 的员工(共读取 10 条记录 )

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

(2)这时另一个事务向 employee 表插入了一条员工记录,工资也为 1000

con2 = getConnection();  
Insert into employee(empId,salary) values("Lili",1000);  
con2.commit();  

事务1再次读取所有工资为 1000的 员工(共读取到了 11 条记录,这就像产生了幻读)

//con1  
select * from employee where salary =1000;  

解决办法:

  如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。把数据库的事务隔离级别调整到 SERIALIZABLE_READ

图解:

  T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。

posted @ 2019-07-06 23:47  惯看秋风  阅读(23)  评论(0)    收藏  举报