目前项目开发中都是基于Spring+MyBatis的,各类配置已经封装好,能够进行快速开发,不须要关心底层的实现。可是在线上出现问题时,若是对于Java事务底层的机制不是很熟悉的话,会很难分析并定位出来,在这里对Java事务进行分类整理,并不断的完善。数据库
1、数据库事务概念数据结构
结合《数据库系统概念》一书和百度百科,数据库事务的基本概念以下:多线程
(1)数据库事务(Database Transaction) 概念:并发
是指做为单个逻辑工做单元执行的一系列操做,要么彻底地执行,要么彻底地不执行,是一个不可分割的工做单位。 事务处理能够确保除非事务性单元内的全部操做都成功完成,不然不会永久更新面向数据的资源。经过将一组相关操做组合为一个要么所有成功要么所有失败的单元,能够简化错误恢复并使应用程序更加可靠。一个逻辑工做单元要成为事务,必须知足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工做单位,由DBMS中的事务管理子系统负责事务的处理。性能
(2)数据库事务的周期:ui
事务一般是以begin transaction开始,以commit或rollback结束。Commint表示提交,即提交事务的全部操做。具体地说就是将事务中全部对数据的更新写回到磁盘上的物理数据库中去,事务正常结束。Rollback表示回滚,即在事务运行的过程当中发生了某种故障,事务不能继续进行,系统将事务中对数据库的全部已完成的操做所有撤消,滚回到事务开始的状态。spa
事务必须是原子工做单元;对于其数据修改,要么全都执行,要么全都不执行。一般,与某个事务关联的操做具备共同的目标,而且是相互依赖的。若是系统只执行这些操做的一个子集,则可能会破坏事务的整体目标。原子性消除了系统处理操做子集的可能性。.net
事务在完成时,必须使全部的数据都保持一致状态。在相关数据库中,全部规则都必须应用于事务的修改,以保持全部数据的完整性。事务结束时,全部的内部数据结构(如 B 树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制全部已知的完整性约束。例如,当开发用于转账的应用程序时,应避免在转账过程当中任意移动小数点。线程
由并发事务所做的修改必须与任何其它并发事务所做的修改隔离。事务查看数据时数据所处的状态,要么是另外一并发事务修改它以前的状态,要么是另外一事务修改它以后的状态,事务不会查看中间状态的数据。这称为隔离性,由于它可以从新装载起始数据,而且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将得到最高的隔离级别。在此级别上,从一组可并行执行的事务得到的结果与经过连续运行每一个事务所得到的结果相同。因为高度隔离会限制可并行执行的事务数,因此一些应用程序下降隔离级别以换取更大的吞吐量。日志
事务完成以后,它对于它对数据库的修改被永久保持,该修改即便出现致命的系统故障也将一直保持。事务日志可以保持事务的永久性。
一个事务读取了另外一个事务还没有提交的数据
例如:事务A、B并发执行时,当A事务update后,B事务select读取到A还没有提交的数据,此时A事务rollback,则B读到的数据是无效的"脏"数据。
一个事务的操做致使另外一个事务先后两次读取到不一样的数据
例如:当B事务select读取数据后,A事务update操做更改B事务select到的数据,此时B事务再次读去该数据,发现先后两次的数据不同。
一个事务的操做致使另外一个事务先后两次查询的结果数据量不一样。
例如:当B事务select读取数据后,A事务insert或delete了一条知足A事务的select条件的记录,此时B事务再次select,发现查询到前次不存在的记录("幻影"),或者前次的某个记录不见了。
从以上事务并发的分析中能够看出,当事务并发进行的时候,可能会出现脏读、不可重复读、幻读等问题,相似于Java 多线程并发,若是彻底采用串行化的话,系统的开销会很大,并发度不高,若是采用较低力度的控制时,可能达不到某些业务场景的要求。所以,SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,在哪些事务内和事务间是可见的,哪些是不可见的。较低级别的隔离一般能够执行更高的并发,系统的开销更低。四种隔离级别以下:
READ UNCOMMITTED (未提交读):
在未提交读级别中,事务中的修改,即便没有提交,对其余事务也都是可见的。事务能够读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会致使不少问题,从性能上来讲 ,READ UNCOMMITTED不会比其余的级别好太多,但却缺少其余级别的不少好处,除非真的有很是必要的理由,在实际应用中通常不多使用。
READ COMMITTED (提交读):
大多数数据库系统的默认隔离级别就是READ COMMITTED(但MYSQL 不是)。READ COMMITTED知足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所作的修改。换句话说,一个事务从开始知道提交前,所作的任何修改对其它事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable read),由于两次执行一样的查询,可能会获得不同的结果。
REPEATABLE READ(可重复读):
可重复读解决了脏读的问题。该级别保证了在同一个事务中屡次读取一样的记录的结果是一致的。可是理论上,可重复读隔离级别仍是没法解决另一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当以前的事务再次读取改范围内的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎经过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。可重复读是MySQL默认的事务隔离级别。
SERIALIZABLE(可串行化):
可串行化是最高的隔离级别。它经过强制事务串行执行,避免了前面说的幻读的问题。简单来讲,可串行化会在读取的每一行数据上都加锁,因此可能致使大量的超时和锁竞争的问题。实际应用中也不多用到这个隔离级别,只有在很是须要确保数据的一致性并且能够接受没有并发的状况下,才考虑采用该级别。
Java事务机制主要是基于JDBC的Connection:
Connection提供了一个auto-commit的属性来指定事务什么时候结束,默认是true。
a、当auto-commit为true时,当每一个独立SQL操做的执行完毕,事务当即自动提交,也就是说每一个SQL操做都是一个事务。JDBC规范是这样规定一个独立SQL操做何时算执行完毕的:
i)、对数据操做语言(DML,如insert,update,delete)和数据定义语言(如create,drop),语句一执行完就视为执行完毕。
ii)、对select语句,当与它关联的ResultSet对象关闭时,视为执行完毕。
iii)、对存储过程或其余返回多个结果的语句,当与它关联的全部ResultSet对象所有关闭,全部update count(update,delete等语句操做影响的行数)和output parameter(存储过程的输出参数)都已经获取以后,视为执行完毕。
b. 当auto-commit为false时,每一个事务都必须显示调用commit方法进行提交,或者显示调用rollback方法进行回滚。
JDBC提供了5种不一样的事务隔离级别(Transaction Isolation Levels),在Connection中进行了定义:
TRANSACTION_NONE JDBC驱动不支持事务
TRANSACTION_READ_UNCOMMITTED 容许脏读、不可重复读和幻读。
TRANSACTION_READ_COMMITTED 禁止脏读,但容许不可重复读和幻读。
TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读。
TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读。
代码以下:
/** * A constant indicating that transactions are not supported. */ int TRANSACTION_NONE = 0; /** * A constant indicating that * dirty reads, non-repeatable reads and phantom reads can occur. * This level allows a row changed by one transaction to be read * by another transaction before any changes in that row have been * committed (a "dirty read"). If any of the changes are rolled back, * the second transaction will have retrieved an invalid row. */ int TRANSACTION_READ_UNCOMMITTED = 1; /** * A constant indicating that * dirty reads are prevented; non-repeatable reads and phantom * reads can occur. This level only prohibits a transaction * from reading a row with uncommitted changes in it. */ int TRANSACTION_READ_COMMITTED = 2; /** * A constant indicating that * dirty reads and non-repeatable reads are prevented; phantom * reads can occur. This level prohibits a transaction from * reading a row with uncommitted changes in it, and it also * prohibits the situation where one transaction reads a row, * a second transaction alters the row, and the first transaction * rereads the row, getting different values the second time * (a "non-repeatable read"). */ int TRANSACTION_REPEATABLE_READ = 4; /** * A constant indicating that * dirty reads, non-repeatable reads and phantom reads are prevented. * This level includes the prohibitions in * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the * situation where one transaction reads all rows that satisfy * a <code>WHERE</code> condition, a second transaction inserts a row that * satisfies that <code>WHERE</code> condition, and the first transaction * rereads for the same condition, retrieving the additional * "phantom" row in the second read. */ int TRANSACTION_SERIALIZABLE = 8;
JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,能够rollback到该保存点处的状态,而不是rollback整个事务。Connection接口的setSavepoint和releaseSavepoint方法能够设置和释放保存点。
JDBC规范虽然定义了事务的以上支持行为,可是各个JDBC驱动,数据库厂商对事务的支持程度可能各不相同。若是在程序中任意设置,可能得不到想要的效果。为此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持状况的获取方法。好比,经过DatabaseMetaData.supportsTransactionIsolationLevel方法能够判断对事务隔离级别的支持状况,经过DatabaseMetaData.supportsSavepoints方法能够判断对保存点的支持状况。
参考链接:http://blog.csdn.net/huilangeliuxin/article/details/43446177
参考连接:http://blog.csdn.net/sinat_33536912/article/details/51200630