对于数据库事物的理解
一、什么是事物
我们通常说的事物指的是数据库事物,是指访问数据库的一个操作序列。数据库系统通过事务集来完成对数据库的存取,事务的正确执行使得数据库从一种状态转换成另一种状态。
事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)的缩写。具体如下:
1)原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
2)一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
3)隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
4)持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
在大多数情况下,通过执行COMMIT或ROLLBACK语句来终止事务。当执行COMMIT语句时,自从事务启动以来对数据库所做的一切更改就成为永久性的了-- 即它们被写到磁盘。当执行ROLLBACK语句时,自从事务启动以来对数据库所做的一切更改都被撤销,并且数据库返回到事务开始之前所处的状态。不管是哪种情况,数据库在事务完成时都保证能回到一致状态。
二、数据库系统支持两种事务提交模式
自动提交模式:每个SQL语句都是一个独立的事务,当数据库系统执行完一个SQL语句后,会自动提交事务。
手动提交模式:必须由数据库客户程序显示指定事务开始边界和结束边界。
在PL/SQL Devveloper等工具中有提供设置是否启用自动提交的功能。
三、事物并发问题
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没采取必要的隔离机制,就会导致各种并发问题,这些并发问题可归纳为以下几类:
第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。
脏读:一个事务读到另一个事务为提交的更新数据。
虚读:一个事务读到另一个事务已提交的新插入的数据。
不可重复读:一个事务读到另一个事务已提交的更新数据。
第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。
四、事物的隔离级别
虽然事务通过确保对数据的更改仅在事务被成功提交之后才成为永久性的,从而提供了一般的数据库一致性,但还是须要用户或应用程序来确保每个事务中执行的SQL操作序列始终会导致一致的数据库。
Serializable(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新。
Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他事务对已有记录的更新。
Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
Read Uncomitted(读未提交数据):一个事务在执行过程中可以拷打其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以有优先考虑把数据库系统的隔离级别设为Read Commited,它能够避免脏读,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
当数据库系统采用read Commited隔离级别时,会导致不可重复读和第二类丢失更新的并发问题,可以在应用程序中采用悲观锁或乐观锁来避免这类问题。从应用程序的角度,锁可以分为以下几类:
A.悲观锁:指在应用程序中显示的为数据资源加锁。尽管能防止丢失更新和不可重复读这类并发问题,但是它会影响并发性能,因此应该谨慎地使用。
B.乐观锁:乐观锁假定当前事务操作数据资源时,不会与其他事务同时访问该数据资源,因此完全依靠数据库的隔离级别来自动管理锁的工作。应用程序采用版本控制手段来避免可能出现的并发问题。
五、悲观锁两种实现方式
A.在应用程序中显示指定采用数据库系统的独占所来锁定数据资源
例如: SQL语句:select ... for update
在Hibernate中使用get,load时如session.get(Account.class,new Long(1),LockMode,UPGRADE)
B.在数据库表中增加一个表明记录状态的LOCK字段
当它取值为“Y”时,表示该记录已经被某个事务锁定,如果为“N”,表明该记录处于空闲状态,事务可以访问它。
六、乐观锁的实现方式
利用Hibernate的版本控制来实现乐观锁。
乐观锁是由程序提供的一种机制,这种机制既能保证多个事务并发访问数据,又能防止第二类丢失更新问题。
在应用程序中可以利用Hibernate提供的版本控制功能来视线乐观锁,OR映射文件中的<version>元素和<timestamp>都具有版本控制的功能,一般推荐采用<version>。