事务的相关性知识,事务的隔离,ACID具体的含义,分布式的事务处理,数据库的事务隔离级别你所不知道的!

1、ACID,事务的四个特性

1)原子性,原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

2)一致性,如果事务执行之前数据库是一个完整性的状态,那么事务结束后,无论事务是否执行成功,数据库仍然是一个完整性状态。

数据库的完整性状态:当一个数据库中的所有的数据都符合数据库中所定义的所有的约束,此时可以称数据库是一个完整性状态。

3)隔离性:事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间事务要隔离。

4)持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

 

2、数据库事务隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(读未提交) 、Read committed (读提交)、Repeatable read (重复读)、Serializable (序列化)

1) Read UnCommitted(读未提交)

最低的隔离级别。一个事务可以读取另一个事务并未提交的更新结果。

2)Read Committed(读提交)

大部分数据库采用的默认隔离级别。一个事务的更新操作结果只有在该事务提交之后,另一个事务才可以的读取到同一笔数据更新后的结果。

3) Repeatable Read(重复读)

mysql的默认级别。整个事务过程中,对同一笔数据的读取结果是相同的,不管其他事务是否在对共享数据进行更新,也不管更新提交与否。

4)Serializable(序列化)

最高隔离级别。所有事务操作依次顺序执行。注意这会导致并发度下降,性能最差。通常会用其他并发级别加上相应的并发锁机制来取代它。

https://blog.csdn.net/qq_36897901/article/details/80454504

3、脏读、幻读、不可重复读

1.脏读:

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2.不可重复读:

是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)

例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

3.幻读:

是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

4、丢失更新:

一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。

4.事务隔离五种级别:

TRANSACTION_NONE  不使用事务。

TRANSACTION_READ_UNCOMMITTED  允许脏读。

TRANSACTION_READ_COMMITTED  防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别

TRANSACTION_REPEATABLE_READ  可以防止脏读和不可重复读,

TRANSACTION_SERIALIZABLE  可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率

 

5.Spring中的事务回滚

1)代码中事务控制的3种方式

编程式事务:就是直接在代码里手动开启事务,手动提交,手动回滚。优点就是可以灵活控制,缺点就是太麻烦了,太多重复的代码了。

声明式事务:就是使用SpringAop配置事务,这种方式大大的简化了编码。需要注意的是切入点表达式一定要写正确。

注解事务:直接在Service层的方法上面加上@Transactional注解,现在咱们框架用这种方式。

2)事务不回滚的原因

事务不回滚的都是采用的声明式事务或者是注解事务;编程式事务都是自己写代码手动回滚的,因此是不会出现不回滚的现象。

再说下声明式事务和注解事务回滚的原理:当被切面切中或者是加了注解的方法中抛出了RuntimeException异常时,Spring会进行事务回滚。默认情况下是捕获到方法的RuntimeException异常,也就是说抛出只要属于运行时的异常(即RuntimeException及其子类)都能回滚;但当抛出一个不属于运行时异常时,事务是不会回滚的。

3)下面说说我经常见到的3种事务不回滚的产生原因:

声明式事务配置切入点表达式写错了,没切中Service中的方法

Service方法中,把异常给try catch了,但catch里面只是打印了异常信息,没有手动抛出RuntimeException异常

Service方法中,抛出的异常不属于运行时异常(如IO异常),因为Spring默认情况下是捕获到运行时异常就回滚

4)如何保证事务回滚

正常情况下,按照正确的编码是不会出现事务回滚失败的。下面说几点保证事务能回滚的方法

(1)如果采用编程式事务,一定要确保切入点表达式书写正确

(2)如果Service层会抛出不属于运行时异常也要能回滚,那么可以将Spring默认的回滚时的异常修改为Exception,这样就可以保证碰到什么异常都可以回滚。具体的设置方式也说下:

① 声明式事务,在配置里面添加一个rollback-for,代码如下

<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

② 注解事务,直接在注解上面指定,代码如下

@Transactional(rollbackFor=Exception.class)

(3)只有非只读事务才能回滚的,只读事务是不会回滚的

(4)如果在Service层用了try catch,在catch里面再抛出一个 RuntimeException异常,这样出了异常才会回滚

(5)如果你不喜欢(4)的方式,你还可以直接在catch后面写一句回滚代码(TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();)来实现回滚,这样的话,就可以在抛异常后也能return 返回值;比较适合需要拿到Service层的返回值的场景。具体的用法可以参见考下面的伪代码

/** TransactionAspectSupport手动回滚事务:*/

@Transactional(rollbackFor = { Exception.class })

public boolean test() {

try {

doDbSomeThing();

} catch (Exception e) {

e.printStackTrace();

//就是这一句了, 加上之后抛了异常就能回滚(有这句代码就不需要再手动抛出运行时异常了)

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

return false;

}

return true;

}

6.分布式事务管理

 

1)Spring中事务针对的的是同一数据库中的操作,所以分布式分库操作不能拥有同一事务

2)解决方案:TCC编程模式

所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。

@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)   //开启事务
	public Long insertNew(LhStoreOrder lhStoreOrder, List<LhStoreOrderProduct> list) {
		// 新增订单总单
		lhOrderMapper.insertSelective(lhStoreOrder);

		if (lhStoreOrder.getId() != null) {
			for (int i = 0; i < list.size(); i++) {
				list.get(i).setOrderId(lhStoreOrder.getId());
			}
			// 新增订单详情
			Integer num = lhStoreOrderProductMapper.inserNewOrderProductList(list);
			if (num != list.size()) {
				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                //手动回滚
				return -1l;
			}
			// 修改库存
			Integer updateRepertory = lhStoreGoodsMapper.updateGoodsRepertory(list);
			if (updateRepertory ==0) {
				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
				return -1l;
			}
			if (lhStoreOrder.getOrderType() == 2) {// 如果是会员订单
				LhVipCard lhVipCard = lhVipCardMapper.selectVipCardByPrimaryKey(lhStoreOrder.getVipCardId());
				if (lhVipCard.getVipBalance().compareTo(lhStoreOrder.getPracticalPrice()) == -1) {// 如果会员卡余额小于实付金额
					return -2l;
				} else {
					Integer isSuccess = lhVipCardMapper.updateVipBalance(lhStoreOrder.getVipCardId(),
							lhStoreOrder.getPracticalPrice());
					if (isSuccess != 1) {
						TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
						return -1l;
					}
				}
			}
			return lhStoreOrder.getId();
		} else {
			return -1l;
		}

	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值