事务及事务隔离级别

什么是事务

事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换为另一种状态

事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)的缩写,这四种状态的意思是:

1、原子性

即不可分割,事务要么全部被执行,要么全部不执行。如果事务的所有子事务全部提交成功,则所有的数据库操作被提交,数据库状态发生变化;如果有子事务失败,则其他子事务的数据库操作被回滚,即数据库回到事务执行前的状态,不会发生状态转换

2、一致性

事务的执行使得数据库从一种正确状态转换成另外一种正确状态

3、隔离性

在事务正确提交之前,不允许把事务对该数据的改变提供给任何其他事务,即在事务正确提交之前,它可能的结果不应该显示给其他事务

4、持久性

事务正确提交之后,其结果将永远保存在数据库之中,即使在事务提交之后有了其他故障,事务的处理结果也会得到保存

 

并发下事务会产生的问题

对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:

1、脏读

对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的. 

2、不可重复读

对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了. 

3、幻读

对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.

 

事务隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

1、DEFAULT(spring 提供给用户)

默认隔离级别,每种数据库支持的事务隔离级别不一样,如果Spring配置事务时将isolation设置为这个值的话,那么将使用底层数据库的默认事务隔离级别。

2、READ_UNCOMMITTED

读未提交,幻读、不可重复读和脏读都允许。一个会话可以读取其他事务未提交的更新结果,如果这个事务最后以回滚结束,这时的读取结果就可能是不正确的,所以多数的数据库都不会运用这种隔离级别。

3、READ_COMMITED

读已提交,允许幻读、不可重复读,不允许脏读。一个会话只能读取其他事务已提交的更新结果,否则,发生等待,但是其他会话可以修改这个事务中被读取的记录,而不必等待事务结束,显然,在这种隔离级别下,一个事务中的两个相同的读取操作,其结果可能不同。

4、REPEATABLE_READ

重复读取,允许幻读,不允许不可重复读和脏读。即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决

5、SERLALIZABLE

串行化,幻读、不可重复读和脏读都不允许。最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了

 

网上专门有图用表格的形式列出了事务隔离级别解决的并发问题:

设置隔离级别的原则

1、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

2、对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为READ_COMMITED。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁乐观锁来控制。

 

拓展知识

1、Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED

2、Mysql 支持 4 种事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ

  如果要察看当前的隔离级别,可使用如下SQL命令:

  –mysql> select @@tx_isolation;

  如果要把当前mysql.exe程序的隔离级别改为Read Committed,可使用如下SQL命令:

  set  [glogal | session]  transaction isolation level 隔离级别名称

  –mysql> set transaction isolation level read committed;

3、在Hibernate中设置隔离级别:

  在Hibernate的配置文件中可以显式的设置隔离级别。每一种隔离级别都对应一个整数:

  1:Read Uncommitted

  2:Read Committed

  4:Repeatable Read

  8:Serializable

  •例如,以下代码把hibernate.cfg.xml文件中的隔离级别设为Read Committed:

  hibernate.connection.isolation=2

  对于从数据库连接池中获得的每个连接,Hibernate都会把它改为使用Read Committed隔离级别。

 

posted @ 2017-10-13 13:38  予我渡北川  阅读(240)  评论(0编辑  收藏  举报