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

2017年6月5日,天气——雨。mysql

      前两天整理以前的学习笔记时,发现对事务并发产生的问题——脏读、幻读、不可重复读和丢失更新这些概念有点模糊,因而又从新温习了一遍,如今把本身的一些理解概括整理以下,方便你们学习。sql

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

1.脏读(Dirty Read)——一个事务读取到了另一个事务没有提交的数据。并发

详细解释:当一个事务正在访问数据而且对数据进行了修改,而这种修改尚未提交到数据库中,这时另一个事务也访问这个数据,而后使用了这个数据。由于这个数据是尚未提交的数据,那么另一个事务读到的这个数据是脏数据,依据脏数据所作的操做多是不正确的。学习

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

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

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

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终于尝试提交时,数据库会检验它的结果是否和事务一、事务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)。

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