事务,就是一组操作数据库的动作集合.事务是现代数据库理论中的核心概念之一.如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务.当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交.由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态.事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction
和end transaction
语句(或函数调用)来界定.事务由事务开始(begin transaction)
和事务结束(end transaction)
之间执行的全体操作组成.
Atomicity 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节.事务在执行过程中如果发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样.
Consistency 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏.
Isolation 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致.
Durability 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失.
三. 事务的隔离级别当多个线程都开启事务同时操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性.
1. 事务操作时几种的现象问题在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
1️⃣. 脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据.
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致.例如:用户A向用户B转账100元,对应SQL命令如下:
#(此时A通知B)
update account set money=money+100 where name=’B’;
update account set money=money - 100 where name=’A’;
当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转.
2️⃣. 不可重复读
不可重复读是指对于数据库中的某个数据,在一个事务范围内多次查询却返回了不同的数据值,这是由于在查询的间隔期间,被另一个事务修改并提交了.
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读现象.
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务已提交的数据.
在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主.但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询可能会有不同,A和B就可能打起来了...
3️⃣. 虚读(幻读)
幻读是事务非独立执行时发生的一种现象.例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给了数据库.而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读.
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据(比如数据的个数).
2. 事务的隔离级别:-
1️⃣. Serializable(串行化):最高的隔离级别,可避免脏读、不可重复读、幻读的发生;
-
2️⃣. Repeatable read(可重复读):默认的隔离级别,可避免脏读、不可重复读的发生;
-
3️⃣. Read committed(读已提交):可避免脏读的发生;
-
4️⃣. Read uncommitted(读未提交):最低的隔离级别,最低级别,任何情况都无法保证. 以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低.像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况.在MySQL数据库中默认的隔离级别为Repeatable read(可重复读).
几种隔离级别的对比:
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read(可重复读);而在Oracle数据库中,只支持Serializable(串行化)级别和Read committed(读已提交)这两种级别,其中默认的为Read committed级别.
在MySQL数据库中查看当前事务的隔离级别:
select @@tx_isolation;
在MySQL数据库中设置事务的隔离级别:
set [glogal | session] transaction isolation level 隔离级别名称;

或者:
set tx_isolation=’隔离级别名称;’

记住:设置数据库的隔离级别一定要是在开启事务之前!
4. JDBC中对事务隔离级别的操作如果是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)
方法之前,调用Connection
对象的setTransactionIsolation(level)
即可设置当前链接的隔离级别,至于参数level,可以使用Connection
对象的字段:
JDBC中事务的level:
在JDBC中设置隔离级别的部分代码:
注意:隔离级别的设置只对当前链接有效.对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection
对象相当于一个链接,而对于Connection
对象设置的隔离级别只对该Connection
对象有效,与其他链接Connection
对象无关.
在本地事务中,服务和资源在事务的包裹下可以看做是一体的.
我们的本地事务由资源管理器进行管理,以下是本地事务的基本执行过程.
事务的ACID
特性是通过InnoDB
引擎的日志和锁机制来保证的,我们知道InnoDB
是mysql
的一个存储引擎.事务的隔离性是通过数据库锁的机制实现的,持久性通过Redo log
(重做日志)来实现,原子性和一致性通过Undo log
来实现.UndoLog
的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为UndoLog
),然后进行数据的修改.如果出现了错误或者用户执行了ROLLBACK
语句,系统可以利用UndoLog
中的备份将数据恢复到事务开始之前的状态.与UndoLog
相反,RedoLog
记录的是新数据的备份,在事务提交前,只要将RedoLog
持久化即可,不需要将数据持久化.当系统崩溃时,虽然数据没有持久化,但是RedoLog
已经持久化,系统可以根据RedoLog
的内容,将所有数据恢复到最新的状态.
-
PROPAGATION_REQUIRED --(默认), 支持事务,如果当前没有事务,就新建一个事务.这是最常见的选择.
-
PROPAGATION_SUPPORTS -- 支持事务,如果当前没有事务,就以非事务方式执行.
-
PROPAGATION_MANDATORY -- 支持事务,如果当前没有事务,就抛出异常.
-
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起,然后新建一个事务.
-
PROPAGATION_NOT_SUPPORTED -- 以非事务的方式执行操作,如果当前存在事务,就把当前事务挂起.
-
PROPAGATION_NEVER -- 以非事务方式的执行,如果当前存在事务,则抛出异常.
-
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则进行与
PROPAGATION_REQUIRED
类似的操作.
1️⃣. 编程式事务: 编程式事务需要你在代码中直接加入处理事务的代码逻辑,可能需要在代码中显式调用beginTransaction()、commit()、rollback()
等事务管理相关的方法.我们可以使用TransactionTemplate
或者直接使用底层的PlatformTransactionManager
,对于编程式事务管理,Spring推荐使用TransactionTemplate
.
当系统需要明确的,细粒度的控制各个事务的边界时,应选择编程式事务.编程式事务侵入性比较强,但处理粒度更细.
2️⃣. 声明式事务: 声明式事务是建立在AOP之上的,它的做法是在a方法的外围添加注解或者直接在配置文件中定义a方法需要的事务处理,在Spring中会通过配置文件在a方法前后进行拦截,并添加事务.其本质是对方法的前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务. 声明式事务最大的优点就是不需要通过编程的方式管理事务,当系统对于事务的控制粒度较粗时,应该选择申明式事务.
无论你选择上述何种事务方式去实现事务控制,Spring都提供了基于门面设计模式的事务管理器供选择.