事务是数据库并发控制不可分割的基本逻辑单位,可以用于确保数据库能被正确修改,避免数据只修改了一部分而导致数据不完整,或者在修改时受到用户干扰。
事务的特性
- 原子性 表示将事务中所做的操作捆绑成一个不可分割的单元,对于事务所进行的数据修改等操作,要么全部执行,要么全部都不执行。
- 一致性 表示事务在完成时,必须使所有的数据保持一致状态,而且在相关数据中,所有规则都必须应用于事务的修改,以保持所有数据的完整性,事务结束时,所有的内部数据结构都应该是正确的。
- 隔离性 表示由并发事务所做的修改必须与任何其他事务所做的修改相隔离,查看数据时,数据所处的状态要么是被另一个并发事务修改之前的状态,要么是被另一个并发事务修改之后的状态,即事务不会查看由另一个并发事务正在修改的数据;
- 持久性 表示事务完成后,它对系统的影响是永久的,即使出现系统故障也可以由日志进行恢复。
并发控制
在多个事务同时使用相同的数据时可能会发生问题,即并发问题。
并发问题包括如下5种:
- 第一类丢失更新
当多个事务同时操作同一个数据,撤消其中一个事务时,把其他事务已经提交的更新数据覆盖,对其他事务来说造成了数据丢失; - 第二类丢失更新
当多个事务同时操作同一个数据时,事务A将修改结果成功提交后,对事务B已经提交的修改结果进行了覆盖,对事务B来说造成了数据丢失; - 脏读
当多个事务同时操作同一个数据时,事务A读到事务B未提交的更新数据,且对数据进行操作,如果事务B撤销更新后,事务A所操作的数据便成了脏数据; - 不可重复读
当多个事务同时操作同一个数据时,事务A对同一行数据重复读取两次,每次读取的结果不同。有可能第二次读取数据的时候原始数据被事务B更改,并成功提交; - 幻象读
当多个事务同时操作同一个数据时,事务A执行两次查询,第二次查询结果比第一次查询多出一行,这是因为在两次查询之间事务B插入了新数据造成的;
隔离级别
为了避免并发问题的发生,在标准SQL规范中,提出了 4 个事务隔离级别,在 Hibernate 配置文件中通过 hibernate.connection.isolation 属性来设置数据库的隔离级别<property name="hibernate.connection.isolation">2</property>
- 序列号(8级)
提供最严格的事务隔离,该隔离级别不允许事务并行执行,只允许一个接着一个执行,不能并发执行。此隔离级别可以有效防止 脏读、不可重复读 和 幻象读。 - 可重复读(4级)
一个事务在执行过程中可以访问其他事务成功提交的新插入数据,但不能访问成功修改的数据。读取数据的事务将会禁止写事务,但允许读事务;写事务则禁止其他事务。此隔离级别可以有效防止不可重复读和脏读。 - 读已提交数据(2级)
一个事务在执行过程中既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该数据。此隔离级别可以有效防止脏读。 - 读未提交数据(1级)
一个事务在执行过程中既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则不允许另外一个事务同时进行写操作,但允许其他事务进行读操作。此隔离级别仅仅可以防止第一类丢失更新。
隔离级别越高,越能保证数据库的完整性和一致性,但对并发性能影响越大。通常设置为 2 级,即读已提交数据,它既能防止脏读,而且又有较好的并发性能。虽然 2 级会导致不可重复读、幻象读和第二类丢失更新这些并发问题,但可以通过在应用程序中采用悲观锁和乐观锁来加以控制。1
<property name="hibernate.connection.isolation">2</property>