事务
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行 。事务是一个不可分割的工作逻辑单元事务必须具备以下四个属性,简称 ACID 属性:
原子性(Atomicity)
事务是一个完整的操作。事务的各步操作是不可分的(原子的)—— 要么都执行,要么都不执
行。
一致性(Consistency)
当事务完成时,数据必须处于一致状态。
隔离性(Isolation)
对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
永久性(Durability)
事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性。
分布式事务
分布式事务:指会涉及到操作多个数据库的事务。
关键在于保证在所有节点的数据写操作,要不全部都执行,要么全部的都不执行。但是一台机器在执行本地事务的时候无法知道其他机器中的本地事务的执行结果。所以也就不知道本次事务到底应该 commit 还是 roolback。
常规的解决办法就是引入一个“协调者”的组件来统一调度所有分布式节点的执行。
CAP 理论
一个分布式系统最多只能同时满足 Consistency(一致性)、Availability(可用性)和 Partition tolerance(分区容错性)这三项中的两项。
CA(放弃P):将所有的数据放在一个节点。满足一致性、可用性;
AP(放弃C):放弃强一致性,用最终一致性来保证;
CP(放弃A):一旦系统遇见故障,受到影响的服务器需要等待一段时间,在恢复期间无法对外提供服务。
Consistency(一致性)
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性:
-
强一致性:对于关系型数据库,要求更新过的数据能被后续的访问都能看到;
-
弱一致性:能容忍后续的部分或者全部访问不到;
-
最终一致性:经过一段时间后要求能访问到更新后的数据。
CAP中说,不可能同时满足的这个一致性指的是强一致性。
Availability(可用性)
服务一直可用,而且是正常响应时间。
大型互联网为了服务可用,舍弃强一致性,保证最终一致性。
Partition Tolerance(分区容错性)
分布式系统在遇到某节点或网络故障的时候,仍然能够对外提供满足一致性和可用性的服务。就是在网络中断的情况下,系统如果还能正常工作。
BASE 理论
什么是 BASE 理论
BASE理论由三个点构成:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)。
Base 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:既是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
Basically Available(基本可用)
假设系统,出现了不可预知的故障,但还是能用,相比较正常的系统而言:
-
响应时间上的损失:正常情况下的搜索引擎 0.5 秒即返回给用户结果,而基本可用的搜索引擎可以在 1 秒作用返回结果;
-
功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单,但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
Soft state(软状态)
硬状态: 相对于原子性而言,要求多个节点的数据副本都是一致的。
软状态:允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
Eventually consistent(最终一致性)
系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。最终一致性包含以下几种:
-
因果一致性(Causal consistency):如果节点 A 在更新完某个数据后通知了节点 B,那么节点 B 之后对该数据的访问和修改都是基于 A 更新后的值。于此同时,和节点 A 无因果关系的节点 C 的数据访问则没有这样的限制;
-
读己之所写(Read your writes):这种就很简单了,节点 A 更新一个数据后,它自身总是能访问到自身更新过的最新值,而不会看到旧值。其实也算一种因果一致性;
-
会话一致性(Session consistency):会话一致性将对系统数据的访问过程框定在了一个会话当中:系统能保证在同一个有效的会话中实现 “读己之所写” 的一致性,也就是说,执行更新操作之后,客户端能够在同一个会话中始终读取到该数据项的最新值;
-
单调读一致性(Monotonic read consistency):单调读一致性是指如果一个节点从系统中读取出一个数据项的某个值后,那么系统对于该节点后续的任何数据访问都不应该返回更旧的值;
-
单调写一致性(Monotonic write consistency):指一个系统要能够保证来自同一个节点的写操作被顺序的执行。
示例:
在Springboot的启动类,或者某个@Configuration的类上加上@EnableTransactionManagement
开启事务。
Conntrol
/**
* @Description:下发立体库出库计划(事务)
* @Param: [id]
* @return: java.util.List<org.muyuan.slaughter.workshop.convertApi.vo.threeStage.store.MySpLiTiKuOrderStatusVO>
* @Author: 孟海鹏
* @Date: 2022/9/18
*/
@ApiOperation(value = "立体库出库计划(三代)(下发立体库出库计划)")
@GetMapping("/UpdateLiTiKuHouseOutPlanStatus")
public R UpdateLiTiKuHouseOutPlanStatus(@NotEmpty String issuer, @NotEmpty String issueDate, @NotEmpty String outPlanNo) {
mySpLitikuhouseoutplanService.updateLiTiKuHouseOutPlanStatus(issuer, issueDate, outPlanNo);
return R.success("下发成功!");
}
Services
只要在需要使用事务的方法上加上@Transactional 关键字开启事务支持。
这里事务分为2步,第一步先根据where条件判断是否满足更新条件,若不满足直接用throw new RuntimeException抛出异常,自动执行回滚操作。若满足之后再执行第二步更新操作。
/**
* @Description: 下发立体库出库计划(事务)
* @Param: [FIssuer, FIssueDate, FOutPlanNo]
* @return: java.util.List<org.muyuan.slaughter.workshop.convertApi.vo.threeStage.store.MySpLiTiKuHouseOutPlanStatusVO>
* @Author: 孟海鹏
* @Date: 2022/9/18
*/
@Transactional
public void updateLiTiKuHouseOutPlanStatus(String issuer, String issueDate, String outPlanNo) {
//1.先校验是否有满足更新条件的数据
List<MySpLitikuhouseoutplanEntity> entities = baseMapper.selectList(Wrappers.<MySpLitikuhouseoutplanEntity>query().lambda()
.eq(MySpLitikuhouseoutplanEntity::getOutPlanNo, outPlanNo)
.eq(MySpLitikuhouseoutplanEntity::getDeleteStatus, CommonConstant.STATUS_INIT));
if (CollectionUtils.isEmpty(entities)) throw new RuntimeException("下发失败");
List<String> ids = entities.stream().map(MySpLitikuhouseoutplanEntity::getId).collect(Collectors.toList());
List<MySpLitikuhouseoutplanDetailEntity> detailEntities = litikuhouseoutplanDetailMapper.selectList(Wrappers.<MySpLitikuhouseoutplanDetailEntity>query().lambda()
.eq(MySpLitikuhouseoutplanDetailEntity::getDeleteStatus, CommonConstant.STATUS_INIT)
.in(MySpLitikuhouseoutplanDetailEntity::getInterID, ids));
if (CollectionUtils.isEmpty(detailEntities)) throw new RuntimeException("下发失败");
List<TBusinessAsrsOutbillEntity> asrsOutbillEntities = businessAsrsOutbillMapper.selectList(Wrappers.<TBusinessAsrsOutbillEntity>query().lambda()
.eq(TBusinessAsrsOutbillEntity::getOutBillNo, outPlanNo)
.eq(TBusinessAsrsOutbillEntity::getSyncStatus, CommonConstant.DATE_DEL));
if (CollectionUtils.isEmpty(asrsOutbillEntities)) throw new RuntimeException("下发失败");
//2.更新状态
baseMapper.UpdateLiTiKuHouseOutPlanStatus(issuer, issueDate, outPlanNo);
baseMapper.UpdateLiTiKuHouseOutPlanStatus2(issuer, issueDate, outPlanNo);
baseMapper.UpdateLiTiKuHouseOutPlanStatus3(issuer, issueDate, outPlanNo);
}
Mapper
/**
* @Description:下发立体库出库计划-sql1(事务)
* @Param: [FIssuer, FIssueDate, FOutPlanNo]
* @return: java.util.List<org.muyuan.slaughter.workshop.convertApi.vo.threeStage.store.MySpLiTiKuHouseOutPlanStatusVO>
* @Author: mhp
* @Date: 2022/9/18
*/
Integer UpdateLiTiKuHouseOutPlanStatus(String issuer, String issueDate, String outPlanNo);
/**
* @Description: 下发立体库出库计划-sql2(事务)
* @Param: [issuer, issueDate, outPlanNo]
* @return: java.lang.Integer
* @Author: mhp
* @Date: 2022/9/29
*/
Integer UpdateLiTiKuHouseOutPlanStatus2(String issuer, String issueDate, String outPlanNo);
/**
* @Description: 下发立体库出库计划-sql3(事务)
* @Param: [issuer, issueDate, outPlanNo]
* @return: java.lang.Integer
* @Author: mhp
* @Date: 2022/9/29
*/
Integer UpdateLiTiKuHouseOutPlanStatus3(String issuer, String issueDate, String outPlanNo);
XML
<update >
update MY_SP_LiTikuHouseOutPlan set
FCheck=1,
exchange_status=0,
FIssuer= #{issuer},
FIssueDate=#{issueDate}
where FOutPlanNo=#{outPlanNo} and FDelete=0;
</update>
<update >
update MY_SP_LiTikuHouseOutPlan_Detail set
FCheck=1,
exchange_status=0
where FInterID in(select FID from MY_SP_LiTikuHouseOutPlan where FOutPlanNo=#{outPlanNo} and FDelete=0);
</update>
<update >
update t_business_asrs_outbill set
exchange_status=0,
sync_status=0
where out_bill_no=#{outPlanNo} and sync_status=-1;
</update>