事务
事务(Transaction),一般是指要做的或所做的事情,这些事情必须全部都完成,或者造成的影响和改变全部都撤销。
四大特性
Java中,事务有四个特性(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
1、原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
(原子性保证事务内的操作要么都发生,要么都不发生。假如所有的操作都成功了,那么事务是成功的,只要其中任何一个操作失败,那么事务会进行回滚。)
2、一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
(A和B一共有2000块钱,无论他们两个谁向谁转账,转了几次帐,始终不能少于或者多余2000块钱。)
3、隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
(假如有两个线程T1和T2,T2要么在T1开始之前结束,要么在T1结束之后开始,两个线程不能互相影响。)
4、持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。
(A卡里有2000块钱,当A从卡里取出500,在不考虑外界因素干扰的情况下,那么A的卡里只能剩1500。不存在取了500块钱后,卡里一会剩1400,一会剩1500,一会剩1600的情况。)
并发造成的问题
1、脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
(A的支付宝里有1000块钱,想往银行卡里提现500,此时已输入密码,显示“银行正在处理中”,这时候A的老婆想用A支付宝里的1000块钱还花呗,结果发现剩下500,过了一会银行因为网络原因导致提现失败,转出的500块钱又回到了支付宝,此时A的支付宝里还是原来的1000。在这期间,A的老婆看到支付宝里的“500”,就是脏读。)
2、幻读(虚读):一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象。
(假如此时有两个线程T1和T2,T1把数据表中每一行的第一个字段都改成了A后,T2新增了一行数据,且第一个字段是B,然后T1再读数据表的时候发现还有一行未被修改。)
3、不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。
(两个线程T1和T2,T1把某一行中的第一个字段改成了A后(此时T1读取的数据是A),T2又把此字段改成了B,然后T1再次读取这个字段时发现数据是B,和第一次读取的A不一样。)
脏读是在另一个事务未提交时读取了数据,而幻读和不可重复读都是在另一个事务已提交后读取了数据。
幻读和不可重复读的区别在于:幻读着重在对数据的整体(例如整张表)而且主要发生在进行增加和删除的操作时,而不可重复读侧重在具体的某一行或者某一个字段主要发生在修改数据时。
隔离级别
- 未提交时读 Read uncommitted(最低级别,任何情况都无法保证。)
- 已提交后读 Read committed(可避免脏读的发生。)
- 可重复读 Repeatable read(可避免脏读、不可重复读的发生。)
- 序列化(串行化) Serializable(可避免脏读、不可重复读、幻读的发生。)
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。大多数数据库的默认级别就是Read committed,比如Sql Server 和 Oracle。 MySQL的默认隔离级别是Repeatable read(实现原理为MVCC)。
补充:
1、事务隔离级别为读提交时,写数据只会锁住相应的行。
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表。
Spring事务的传播
1.Propagation.REQUIRED(默认)∶如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
2.Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
3.Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
4.Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,延缓当前的事务。
5.Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
6.Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
7.Propagation.NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。