事务的原子性(Atomicity)
事务的原子性指的是,事务中包含的程序做为数据库的逻辑工做单位,它所作的对数据改操做要所有执行,要么所有不执行。这种特性称为原子性。 事务的原子性要求,若是把一个事务看做是一个程序,它要么完整的被执行,要么彻底执行。就是说事务的操纵序列或者彻底应用到数据库或者彻底不影响数据库。这种特性称为原子性 假如用户在一个事务内完成了对数据库的更新,这时全部的更新对外部世界必须是可见的,或者彻底没有更新。前者称事务已提交,后者称事务撤销。DBMS必须确保由成功提交的事物完成的全部操做在数据库内有彻底的反映,而失败的事务对数据库彻底没有影响html
-------------------------------------------------------------------------------------------------------------java
事务的一致性(Consistency)
指在一个事务执行以前和执行以后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态知足全部的完整性约束,就说该数据库是一致的。(打个比方,若是从 A 帐户转帐到 B 帐户,不可能由于 A 帐户扣了钱,而 B 帐户没有加钱吧)
一致性处理数据库中对全部语义约束的保护。假如数据库的状态知足全部的完整性约束,就说该数据库是一致的。例如,当数据库处于一致性状态S1时,对数据库执行一
个事务,在事务执行期间假定数据库的状态是不一致的,当事务执行结束时,数据库处在一致性状态S2spring
-------------------------------------------------------------------------------------------------------------数据库
隔离性(Isolation)
隔离性指并发的事务是相互隔离的。即一个事务内部的操做及正在操做的数据必须封锁起来,不被企图进行修改的事务看到 分离性是DBMS针对并发事务间的冲突提供的安全保证。DBMS能够经过加锁在并发执行的事务间提供不一样级别的分离。假如并发交叉执行的事务没有任何控制。操纵相同的共享对象的多个并发事务的执行可能引发异常状况(打个比方,当咱们编写了一条 update 语句,提交到数据库的一刹那间,有可能别人也提交了一条 delete 语句到数据库中。也许咱们都是对同一条记录进行操做,能够想象,若是不稍加控制,就会出大麻烦来。咱们必须保证数据库操做之间是“隔离”的(线程之间有时也要作到隔离),彼此之间没有任何干扰。这就是:隔离性(Isolation)。)
DBMS能够在并发执行的事务间提供不一样级别的分离。分离的级别和并发事务的吞吐量之间存在反比关系。较多事务的可分离性可能会带来较高的冲突和较多的事务流产。流产的事务要消耗资源,这些资源必需要从新被访问。所以,确保高分离级别的DBMS须要更多的开销安全
要想真正的作到操做之间彻底没有任何干扰是很难的,因而乎要制定一个规范。多线程
1.READ_UNCOMMITTED并发
2.READ_COMMITTEDide
3.REPEATABLE_READ性能
4.SERIALIZABLEidea
千万不要去翻译,那只是一个代号而已。从上往下,级别愈来愈高,并发性愈来愈差,安全性愈来愈高,反之则反。
-------------------------------------------------------------------------------------------------------------
持久性(Durability)
持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。(打个比方,当咱们执行一条 insert 语句后,数据库必需要保证有一条数据永久地存放在磁盘中,这个也算事务的一条特性, 它就是:持久性)即一旦一个事务提交,DBMS保证它对数据库数据的改变应该是永久性的,耐得住任何系统故障
。持久性经过数据库备份和恢复来保证 持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。即对已提交事务的更新恢复。一旦一个事务被提交,DBMS必须保证提供适当的冗余,使其耐的住系统故障。因此,持久性主要在于DBMS的恢复性能
-------------------------------------------------------------------------------------------------------------
2、三类数据读问题和两类数据更新问题
1.Dirty Read(脏读)
余额应该为 1100 元才对!请看 T6 时间点,事务 A 此时查询余额为 900 元,这个数据就是脏数据,它是事务 A 形成的,明显事务没有进行隔离,渗过来了,乱套了。
2.Unrepeatable Read(不可重复读)
事务 A 其实除了查询了两次之外,其余什么事情都没有作,结果钱就从 1000 变成 0 了,这就是重复读了。可想而知,这是别人干的,不是我干的。其实这样也是合理的,毕竟事务 B 提交了事务,数据库将结果进行了持久化,因此事务 A 再次读取天然就发生了变化。
这种现象基本上是能够理解的,但在有些变态的场景下倒是不容许的。毕竟这种现象也是事务之间没有隔离所形成的,但咱们对于这种问题,彷佛能够忽略。
3.Phantom Read(幻读)
银行工做人员,每次统计总存款,都看到不同的结果。不过这也确实也挺正常的,总存款增多了,确定是这个时候有人在存钱。可是若是银行系统真的这样设计,那算是玩完了。这一样也是事务没有隔离所形成的,但对于大多数应用系统而言,这彷佛也是正常的,能够理解,也是容许的。银行里那些恶心的那些系统,要求很是严密,统计的时候,甚至会将全部的其余操做给隔离开,这种隔离级别就算很是高了(估计要到 SERIALIZABLE 级别了)。
第一类丢失更新,A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能形成很严重的问题,经过下面的帐户取款转帐就能够看出来:
可是,在当前的四种任意隔离级别中,都不会发生该状况,否则绝对乱套,我都没提交事务只是撤销,就把别人的给覆盖了,这也太恐怖了。
第二类丢失更新,B事务覆盖A事务已经提交的数据,形成A事务所作操做丢失
概括一下,以上提到了事务并发所引发的跟读取数据有关的问题,各用一句话来描述一下:
1.脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又作了其余操做。
2.不可重复读:事务 A 读取了事务 B 已提交的更改数据。
3.幻读:事务 A 读取了事务 B 已提交的新增数据。
第一条是坚定抵制的,后两条在大多数状况下可不做考虑。
这就是为何必需要有事务隔离级别这个东西了,它就像一面墙同样,隔离不一样的事务。看下面这个表格,您就清楚了不一样的事务隔离级别能处理怎样的事务并发问题:
根据您的实际需求,再参考这张表,最后肯定事务隔离级别,应该再也不是一件难事了。
如今来看看MySQL数据库为咱们提供的四种隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何状况都没法保证。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,固然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(相似于Java多线程中的锁)使得其余的线程只能在锁外等待,因此平时选用何种隔离级别应该根据实际状况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
例1:查看当前事务的隔离级别:
例2:将事务的隔离级别设置为Read uncommitted级别:
或:
记住:设置数据库的隔离级别必定要是在开启事务以前!
若是是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)方法以前。调用Connection对象的setTransactionIsolation(level)便可设置当前连接的隔离级别,至于参数level,可使用Connection对象的字段:
在JDBC中设置隔离级别的部分代码:
后记:隔离级别的设置只对当前连接有效。对于使用MySQL命令窗口而言,一个窗口就至关于一个连接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操做数据库来讲,一个Connection对象至关于一个连接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其余连接Connection对象无关。
除了 JDBC 给咱们提供的事务隔离级别这种解决方案之外,还有哪些解决方案能够完善事务管理功能呢?
不妨看看 spring 的解决方案吧,其实它是对 JDBC 的一个补充或扩展。它提供了一个很是重要的功能,就是:事务传播行为(TransactionPropagation Behavior)。
Spring 提供了 7 种事务传播行为。
1.PROPAGATION_REQUIRED
2.RROPAGATION_REQUIRES_NEW
3.PROPAGATION_NESTED
4.PROPAGATION_SUPPORTS
5.PROPAGATION_NOT_SUPPORTED
6.PROPAGATION_NEVER
7.PROPAGATION_MANDATORY
首先要明确的是,事务是从哪里来?传播到哪里去?答案是,从方法 A 传播到方法 B。Spring 解决的只是方法之间的事务传播,那状况就多了,好比:
这样就是 4 种了,还有 3 种特殊状况。仍是用个人 Style 给你们作一个分析吧:
@Transactional void A(){ } @Transactional void B(){ A(); }
假设事务从方法 A 传播到方法 B,您须要面对方法 B,问本身一个问题:
方法 A 有事务吗?
Spring 给咱们带来了事务传播行为,这确实是一个很是强大而又实用的功能。除此之外,也提供了一些小的附加功能,好比:
1.事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,因此故意给事务设置一个最大时常,若是超过了,就回滚事务。
2.只事务(Readonly Transaction):为了忽略那些不须要事务的方法,好比读取数据,这样能够有效地提升一些性能。
最后,推荐你们使用 Spring 的注解式事务配置,而放弃 XML 式事务配置。由于注解实在是太优雅了,固然这一切都取决于您自身的状况了。
在 Spring 配置文件中使用:
<tx:annotation-driven/>
在须要事务的方法上使用:
@Transactional public void xxx() { ... }
可在 @Transactional 注解中设置:事务隔离级别、事务传播行为、事务超时时间、是否只读事务。
具体使用请参考个人另外一篇博客
https://my.oschina.net/idea813/blog/1582244
参考资料
https://www.cnblogs.com/lc-ant/p/3981834.html
http://blog.csdn.net/u014079773/article/details/52808193
http://blog.csdn.net/lovesomnus/article/details/44459675
https://www.cnblogs.com/fjdingsd/p/5273008.html