由于事务的ACID特性,即Atomicity、Consistency、Isolation、Durability(原子性、一致性、隔离性、持续性),那么对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
- 脏读:又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。
- 不可重复读:是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
- 幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全
部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
数据库事务的隔离性:数据库系统必须具有隔离并发运行的各个事务的能力,使它们不会相互影响,避免各种并发问题。
一个事务与其他事务隔离的程度成为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性就越弱。
数据库提供了以下4种事务隔离级别:
- READ-UNCOMMITTED(读未提交数据,代号为1):允许事务读取未被其他事务提交的变更,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。在这种隔离级别下,脏读,不可重复读和幻读的问题都会出现。
- READ-COMMITTED(读已提交数据,代号为2):只允许事务读取已经被其它事务提交的变更。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。可以避免脏读,但不可重复读和幻读的问题仍然存在。
- REPETABLE-READ(可重复读,代号为4):确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其它事务对这个字段进行更新。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。可以避免脏读和不可重复读,但是不能避免出现幻读。
- SERIALIZEABLE(串行化,代号为8):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其它事务对该表执行插入,更新和删除操作,提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。所有并发问题都可以避免,但性能十分低下。
Oracle在支持两种事务隔离级别:READ-COMMITTED和SERIALIZEABLE,默认的事务隔离级别为READ-COMMITTED。
Mysql支持四种事务隔离级别,默认的事务隔离级别为REPETABLE-READ。
在Mysql中设置隔离级别:
每启动一个mysql程序,就会获得一个单独的数据库链接。每个数据库链接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。
可以通过select @@tx_isolation;语句查看当前mysql连接的隔离级别,如下图:
可以通过set transaction isolation level read committed;语句来设置当前mysql连接的隔离级别为read committed。
可以通过set global transaction isolation level read committed;来设置数据库系统的全局的事务隔离级别为read committed。
在Hibernate中设置隔离级别:
JDBC数据库连接使用数据库默认的隔离级别,在Hibernate的配置文件中可以显式的设置其隔离级别,每一个隔离级别对应一个整数(如上述四个括号中注明的代号)。设置方法为在Hibernate.cfg.xml中加入: