范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

java项目经验线上对分布式事务的处理

  事务是我们平时项目中对数据操作最为直接、常用的方式,现在无论是大小公司都离不开对事务的操作,伴随业务的提升,客户量的积累也大大增加了对事务管理的难度。
  在本章节中将会讲到如下内容:
  1、线上环境对roll back only 的处理
  2、线上环境对嵌套事务的解决方案
  3、11个demo分析事务失效的场景
  4、分布式事务
  5、事务也能异步
  6、IO Exception会造成事务回滚吗?1、线上环境对roll back only 的处理与产生
  JAVA1 2 3 4 5 6 7 org.springframework.dao.CannotAcquireLockException:  ### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction ### The error may involve xxxMapper.insert-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO xxx ### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction ; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
  产生原因:
  事务嵌套,内层事务将异常捕获抛出。2、线上环境对嵌套事务的解决方案优化点可以从以下几点进行考虑: 最为直接的方法便是去掉嵌套事务,在controller层统一决定异常处理 对于类似开发过程中,需考虑将相关方法长事务中查询方法剔除,将方法内事务缩短为最小事务 出现突发情况,应提供最为简单有效的方案,让业务正常操作,不受影响 开发应对当时的技术方案告知相关测试 在代码层面,后续代码需要前面操作事务释放锁 无需等待插入结果   直接插入后续数据 将查询放在事务外面尽量将大事务变为小事务 捕获异常  自动重试 但是短时间内我还没有时间进行整改,在不影响主流程的情况下未进行整改,但我后续才知道大错特错。 排查
  JAVA1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @timestamp September 1st 2021, 10:20:24.637 # @version  1 t LOG_DATEFORMAT_PATTERN    yyyy-MM-dd HH:mm:ss.SSS t LOG_LEVEL_PATTERN %5p t _id   VMaG t _index    applog-2021.09.01 # _score    1 t _type doc t appindex  applog t appname   app t host  10.0.74.157 t level ERROR # level_value   40,000 t logger_name   ExceptionLogCollector t message   未知异常[500] => Transaction rolled back because it has been marked as rollback-only # port  10,792 t stack_trace   org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:873) ~[spring-tx-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:710) ~[spring-tx-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:533) ~[spring-tx-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304) ~[spring-tx-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE] spring-tx-5.1.4.RELEASE.jar-
  org.springframework.transaction.interceptor.TransactionInterceptor#事务拦截器
  spring事务分为声明式事务和编程式事务,若目标方法存在事务,spring会对bean生成一个代理对象,从日志来看是cglib的
  入口98行springaop事务增强 TransactionAspectSupport在事务中的调用,执行代理类的目标方法触发invoke
  JAVA1 2 3 @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,             final InvocationCallback invocation) throws Throwable
  方法为protected的,根据源代码注释解析
  JAVA1 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) 如果事务属性为null 且事务类型是CallbackPreferringPlatformTransactionManager进入304行commitTransactionAfterReturning(txInfo);方法 意为事务成功后执行,有异常不执行,没有事务不执行,也就是为后面的事务方法异常时没执行进行了铺垫,533行 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());事务进行commit时进行判断 如果不是进行全局事务提交 但是是RollbackOnly的话 走processRollback处理实际回滚
  JAVA1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 @Override     public final void commit(TransactionStatus status) throws TransactionException {         if (status.isCompleted()) {             throw new IllegalTransactionStateException(                     "Transaction is already completed - do not call commit or rollback more than once per transaction");         }          DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;         if (defStatus.isLocalRollbackOnly()) {             if (defStatus.isDebug()) {                 logger.debug("Transactional code has requested rollback");             }             processRollback(defStatus, false);             return;         }          if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {             if (defStatus.isDebug()) {                 logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");             }             日志追踪的710行-----记住此处传true             processRollback(defStatus, true);             return;         }          processCommit(defStatus);     }   private void processRollback(DefaultTransactionStatus status, boolean unexpected) {         try {             入参为true             boolean unexpectedRollback = unexpected;              try {                 triggerBeforeCompletion(status);                  if (status.hasSavepoint()) {                     if (status.isDebug()) {                         logger.debug("Rolling back transaction to savepoint");                     }                     status.rollbackToHeldSavepoint();                 }                 else if (status.isNewTransaction()) {                     if (status.isDebug()) {                         logger.debug("Initiating transaction rollback");                     }                     doRollback(status);                 }                 else {                     // Participating in larger transaction                     if (status.hasTransaction()) {                         if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {                             if (status.isDebug()) {                                 logger.debug("Participating transaction failed - marking existing transaction as rollback-only");                             }                             doSetRollbackOnly(status);                         }                         else {                             if (status.isDebug()) {                                 logger.debug("Participating transaction failed - letting transaction originator decide on rollback");                             }                         }                     }                     else {                         logger.debug("Should roll back transaction but cannot - no transaction available");                     }                     // Unexpected rollback only matters here if we"re asked to fail early                     if (!isFailEarlyOnGlobalRollbackOnly()) {                         unexpectedRollback = false;                     }                 }             }             catch (RuntimeException | Error ex) {                 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);                 throw ex;             }              triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);             日志追踪的873行  抛出异常             // Raise UnexpectedRollbackException if we had a global rollback-only marker             if (unexpectedRollback) {                 throw new UnexpectedRollbackException(                         "Transaction rolled back because it has been marked as rollback-only");             }         }         finally {             cleanupAfterCompletion(status);         }     }
  事务这里场景和传播行为相关知识点太多了,这个后续接着分析
  但就此场景将伪代码贴一下
  JAVA1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 try      {         methodA()     }catch      {     }      @Transactional(rollbackFor = Exception.class)     public methodA() {         methodB()     }      @Transactional(rollbackFor = Exception.class)     public methodB() {         try {             methodC()         } catch {         }     }      methodC() {         当C方法抛出异常时     }
  不知道大家对于rpc行为调用的接口是如何处理的,我们以前是将rpc调用的接口有Biz接收进来,进行参数处理,领域模型转换后,调取service进行内部数据处理的,但此时的接口在主流程上会伴随着另一个第三方接口的写操作,需进行事务处理,那么内层service接口为什么还要进行事务管理?在设计上理应不对rpc接口操作的service进行开放调用的,但业务上区分不同场景,不同供应商,不同酒店等对接口进行了反射调用,或者app调用,导致内层service也进行了事务操作,那么问题来了,嵌套事务时,如果内层事务注解取消不抛出
  UnexpectedRollbackException,实际此方法内并没有完全执行完,
  我希望是怎样的?我希望在保持事务原子性的前提,内层事务回滚则整个全局事务回滚,且不报此异常
  第一种方法isGlobalRollbackOnParticipationFailure方法,让主事务来决定是否回滚, 改动成本大
  而在Springaop中,被拦截的方法需要显式的抛出异常,并不能经过任何处理,这样aop才能进行回滚,默认aop是只catchruntimeException的异常 第二种方法可以在catch块里加上 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 手动回滚 即便上层事务发生了异常,也想要最终提交整个事务呢?如果有这样的需求的话,可以给事务管理器配置一个参数 setGlobalRollbackOnParticipationFailure(false); # 改动成本大
  解决方案:在内层方法中不进行方法的try catch,有异常操作时在外层事务进行处理,且可决定是否回滚,特定的异常也再次处理
  回顾:事务的失效场景(事务不生效和事务不回滚)3、11个demo分析事务失效的场景
  JAVA1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213  @Slf4j @Service public class DemoService {  	@Autowired 	private Test1Mapper test1Mapper; 	 	@Autowired 	private TestMapper testMapper; 	 	@Autowired 	private InvalidTransactionService invalidTransactionService; 	 	@Autowired 	private ExecutorService executorService; 	 	@Autowired 	private DemoService _self; 	 	@Autowired 	private ValidTransactionService validTransactionService; 	 	@Autowired 	private RequireNewTransactionService requireNewTransactionService; 	 	/******************************************************** 	 * 事务不生效场景1 	 * 相当于调用this调用,没有产生代理对象调用,解决方法,自己把自己注入以后调用 	 ********************************************************/ 	public void demo1() {  		invalidTransaction(); 		 		//TODO other logic code here 	} 	 	@Transactional 	public void invalidTransaction() { 		TestDO test = new TestDO(); 		test.setName("11111"); 		testMapper.insert(test); 		 		Test1DO test1 = new Test1DO(); 		test1.setCust("2222"); 		test1Mapper.insert(test1); 		throw new WMSException(ErrorCodeEnum.BD10001001.code(),"事务不生效场景1"); 	} 	 	/******************************************************** 	 * 事务不生效场景二 	 * 这个例子的目的是为了catch住内层事务的异常,让外层事务成功,但是实际上没有内外层事务都回滚了 	 * 	 * 这里A和B都受事务控制,并且是处于同一个事务的。 	 * A调用B,A中抓了B的异常,当B发生异常的时候,B的操作应该回滚,但是A吃了异常,A方法中没有产生异常,所以A的操作又应该提交,二者是相互矛盾的。 	 * spring的事务关联拦截器在抓到B的异常后就会标记rollback-only为true,当A执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。 	 * 	 * 报错提示:Transaction rolled back because it has been marked as rollback-only 	 * 	 * 如果想使外层事务生效可以把内层事务传播特性修改为:@Transactional(propagation = Propagation.REQUIRES_NEW) 	 *  	 ********************************************************/ 	@Transactional 	public void demo2() { 		TestDO test = new TestDO(); 		test.setName("3333"); 		testMapper.insert(test); 		try { 			invalidTransactionService.transaction(); 		}catch (Exception e) { 			log.error("服务异常,异常被捕获", e); 		} 	} 	 	/******************************************************** 	 * 事务不生效场景三 	 * 	 * 因为开了线程异步执行,等于事务完全在两个线程内,不在一个线程,所以即使抛错,也是一个生效一个不生效, 	 * 事务没有回滚 	 *  	 ********************************************************/ 	@Transactional 	public void demo3() { 		TestDO test = new TestDO(); 		test.setName("5555"); 		testMapper.insert(test); 		 		executorService.execute(() -> { 			Test1DO test1 = new Test1DO(); 			test1.setCust("6666"); 			test1Mapper.insert(test1); 		}); 		 		throw new WMSException(ErrorCodeEnum.BD10001001.code(),"事务不生效场景3"); 	} 	 	/******************************************************** 	 * 事务不生效场景八 	 * Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked,如果遇到checked意外就不回滚。 	 * Exception包含RuntimeException体系和其他非RuntimeException的体系 	 * Error和RuntimeException及其子类成为未检查异常(unchecked),其它异常成为已检查异常(checked)。 	 * spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作 	 * 	 * 	 *那么什么是检查型异常什么又是非检查型异常呢? 	 * 1.继承自runtimeexception或error的是非检查型异常,而继承自exception的则是检查型异常(当然,runtimeexception本身也是exception的子类)。 	 * 2.对非检查型类异常可以不用捕获,而检查型异常则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。所以必须在service捕获异常,然后再次抛出,这样事务方才起效。 	 * 	 * @throws IOException  	 *  	 ********************************************************/ 	@Transactional 	public void demo8() throws IOException { 		TestDO test = new TestDO(); 		test.setName("11111"); 		testMapper.insert(test); 		 		Test1DO test1 = new Test1DO(); 		test1.setCust("2222"); 		test1Mapper.insert(test1); 		throw new IOException("事务不生效场景8"); 	} 	 	/******************************************************** 	 * 事务不生效场景九 	 * @throws IOException  	 *  	 ********************************************************/ 	 	public void demo9(){ 		invalidTransaction2(); 	} 	 	@Transactional 	private void invalidTransaction2() { 		TestDO test = new TestDO(); 		test.setName("11111"); 		testMapper.insert(test); 		 		Test1DO test1 = new Test1DO(); 		test1.setCust("2222"); 		test1Mapper.insert(test1); 		throw new WMSException("事务不生效场景9"); 	} 	 	/******************************************************** 	 * 事务生效场景1 	 *  	 ********************************************************/ 	public void demo4() { 		 		_self.invalidTransaction(); 		 		//TODO other logic code here 	} 	 	/******************************************************** 	 * 事务生效场景二 	 * 	 * 因为内层没有事务控制,所以内层报错,不会混回滚,同样外层catch住,所以外层业务成功 	 ********************************************************/ 	@Transactional 	public void demo5() { 		TestDO test = new TestDO(); 		test.setName("7777"); 		testMapper.insert(test); 		 		try { 			validTransactionService.transaction(); 		}catch (Exception e) { 			log.error("服务异常,异常被捕获", e); 		} 	} 	 	/******************************************************** 	 * 事务生效场景三 	 * 	 *内层事务配置的是REQUIRES_NEW,表示自己用自己的,不和外层有牵连,内层如果报错,事务会回滚 	 * 外层如果catch住了,就可以正常执行,外层生效,内层回滚 	 ********************************************************/ 	@Transactional 	public void demo6() { 		TestDO test = new TestDO(); 		test.setName("9999"); 		testMapper.insert(test); 		 		try { 			requireNewTransactionService.transactionWithException(); 		}catch (Exception e) { 			log.error("服务异常,异常被捕获", e); 		} 	} 	 	/******************************************************** 	 * 独立事务 	 * 内外层事务独立,内层操作未报错,事务正常执行,外层有错,事务回滚。 	 ********************************************************/ 	@Transactional 	public void demo7() { 		TestDO test = new TestDO(); 		test.setName("9999"); 		testMapper.insert(test); 		 		requireNewTransactionService.transaction(); 		throw new WMSException(ErrorCodeEnum.BD10001001.code(),"独立事务"); 	} 	 	 	 	 }   4、分布式事务以及分布式事务嵌套
  一次业务操作需要跨多个数据源或需要垮多个系统进行远程调用,就会产生分布式事务问题
  全局事务一致性问题
  全局事务id+三组件 tc+tm+rm
  Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)
  Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而来。
  AT模式 默认
  TCC模式
  XA模式
  SAGA模式 长事务解决方案
  XID 由ip 端口号 加全局事务id生成
  关于分布式事务,工程领域主要讨论的是强一致性和最终一致性的解决方案。典型方案包括:
  两阶段提交(2PC, Two-phase Commit)方案
  eBay 事件队列方案
  TCC 补偿模式
  缓存数据最终一致性一致性理论
  分布式事务的目的是保障分库数据一致性,而跨库事务会遇到各种不可控制的问题,如个别节点永久性宕机,像单机事务一样的ACID是无法奢望的。另外,业界著名的CAP理论也告诉我们,对分布式系统,需要将数据一致性和系统可用性、分区容忍性放在天平上一起考虑。
  两阶段提交协议(简称2PC)是实现分布式事务较为经典的方案,但2PC 的可扩展性很差,在分布式架构下应用代价较大,eBay 架构师Dan Pritchett 提出了BASE 理论,用于解决大规模分布式系统下的数据一致性问题。BASE 理论告诉我们:可以通过放弃系统在每个时刻的强一致性来换取系统的可扩展性。
  CAP理论在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)3 个要素最多只能同时满足两个,不可兼得。
  其中,分区容忍性又是不可或缺的。
  一致性:分布式环境下多个节点的数据是否强一致。可用性:分布式服务能一直保证可用状态。当用户发出一个请求后,服务能在有限时间内返回结果。分区容忍性:特指对网络分区的容忍性。举例:Cassandra、Dynamo
  等,默认优先选择AP,弱化C;HBase、MongoDB 等,默认优先选择CP,弱化A。
  BASE理论核心思想:
  基本可用(BasicallyAvailable):指分布式系统在出现故障时,允许损失部分的可用性来保证核心可用。
  软状态(SoftState):指允许分布式系统存在中间状态,该中间状态不会影响到系统的整体可用性。
  最终一致性(EventualConsistency):指分布式系统中的所有副本数据经过一定时间后,最终能够达到一致的状态。
  2. 一致性模型
  数据的一致性模型可以分成以下 3 类:
  强一致性:数据更新成功后,任意时刻所有副本中的数据都是一致的,一般采用同步的方式实现。 弱一致性:数据更新成功后,系统不承诺立即可以读到最新写入的值,也不承诺具体多久之后可以读到。 最终一致性:弱一致性的一种形式,数据更新成功后,系统不承诺立即可以返回最新写入的值,但是保证最终会返回上一次更新操作的值。 分布式系统数据的强一致性、弱一致性和最终一致性可以通过Quorum NRW算法分析。
  3. 分布式事务解决方案
  2PC方案——强一致性
  2PC的核心原理是通过提交分阶段和记日志的方式,记录下事务提交所处的阶段状态,在组件宕机重启后,可通过日志恢复事务提交的阶段状态,并在这个状态节点重试,如Coordinator重启后,通过日志可以确定提交处于Prepare还是PrepareAll状态,若是前者,说明有节点可能没有Prepare成功,或所有节点Prepare成功但还没有下发Commit,状态恢复后给所有节点下发RollBack;若是PrepareAll状态,需要给所有节点下发Commit,数据库节点需要保证Commit幂等。
  2PC方案的问题:同步阻塞。数据不一致。单点问题。升级的3PC方案旨在解决这些问题,主要有两个改进:增加超时机制。两阶段之间插入准备阶段。但三阶段提交也存在一些缺陷,要彻底从协议层面避免数据不一致,可以采用Paxos或者Raft算法。
  eBay 事件队列方案——最终一致性
  eBay 的架构师Dan Pritchett,曾在一篇解释BASE 原理的论文《Base:An Acid
  Alternative》中提到一个eBay
  分布式系统一致性问题的解决方案。它的核心思想是将需要分布式处理的任务通过消息或者日志的方式来异步执行,消息或日志可以存到本地文件、数据库或消息队列,再通过业务规则进行失败重试,它要求各服务的接口是幂等的。描述的场景为,有用户表user
  和交易表transaction,用户表存储用户信息、总销售额和总购买额,交易表存储每一笔交易的流水号、买家信息、卖家信息和交易金额。如果产生了一笔交易,需要在交易表增加记录,同时还要修改用户表的金额。
  论文中提出的解决方法是将更新交易表记录和用户表更新消息放在一个本地事务来完成,为了避免重复消费用户表更新消息带来的问题,增加一个操作记录表updates_applied来记录已经完成的交易相关的信息。
  这个方案的核心在于第二阶段的重试和幂等执行。失败后重试,这是一种补偿机制,它是能保证系统最终一致的关键流程。
  TCC (Try-Confirm-Cancel)补偿模式——最终一致性
  某业务模型如图,由服务 A、服务B、服务C、服务D 共同组成的一个微服务架构系统。服务A 需要依次调用服务B、服务C 和服务D
  共同完成一个操作。当服务A 调用服务D 失败时,若要保证整个系统数据的一致性,就要对服务B 和服务C 的invoke
  操作进行回滚,执行反向的revert 操作。回滚成功后,整个微服务系统是数据一致的。
  实现关键要素:服务调用链必须被记录下来。每个服务提供者都需要提供一组业务逻辑相反的操作,互为补偿,同时回滚操作要保证幂等。必须按失败原因执行不同的回滚策略。
  缓存数据最终一致性
  在我们的业务系统中,缓存(Redis 或者Memcached)通常被用在数据库前面,作为数据读取的缓冲,使得I/O
  操作不至于直接落在数据库上。以商品详情页为例,假如卖家修改了商品信息,并写回到数据库,但是这时候用户从商品详情页看到的信息还是从缓存中拿到的过时数据,这就出现了缓存系统和数据库系统中的数据不一致的现象。
  要解决该场景下缓存和数据库数据不一致的问题我们有以下两种解决方案:为缓存数据设置过期时间。当缓存中数据过期后,业务系统会从数据库中获取数据,并将新值放入缓存。这个过期时间就是系统可以达到最终一致的容忍时间。更新数据库数据后同时清除缓存数据。数据库数据更新后,同步删除缓存中数据,使得下次对商品详情的获取直接从数据库中获取,并同步到缓存。
  常用组件: Seata,Sega,Atomikos
  TC (Transaction Coordinator) - 事务协调者
  维护全局和分支事务的状态,驱动全局事务提交或回滚。
  TM (Transaction Manager) - 事务管理器
  定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  RM (Resource Manager) - 资源管理器
  管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
  安装
  关键注解全局@GlobalTranstional
  1.更改事务组名称service
  2.store更改mode 修改db
  3.执行sql
  4.修改注册进nacos
  5.启动seata-server.bat
  如何保证分布唯一全局id的生成5、分布式事务异步方案
  看下分布式事务的异步问题,根据事务的xid搭配future在切面里对注解进行处理,实现异步+分布式事务的并存
  注意事项
  这个依赖只是用来解决部分问题,不是解决全部问题
  这个仅用于TM端,不要用来RM端(其实要实现RM端的话,可以仿照SeataAsyncAspect,写一个aspect,很简单的)
  不要进行事务嵌套,不支持事务嵌套!!!
  确保异步的多个操作之间是没有先后顺序的
  这个是一个私人包装处理,仅供参考,还未应用到生产环境6、IO Exception会造成事务回滚吗?
  本地测试代码启动:@PostMapping("save")     public Map save(@RequestBody Message message, HttpServletRequest request){         HashMap map = new HashMap<>();         try {             String ip = IpUtils.getIp(request);             message.setIp(ip);             messageService.saveMessage(message);             map.put("success",true);             map.put("msg","添加数据成功");         }catch (Exception e){             e.printStackTrace();             map.put("success",false);             map.put("msg","添加数据失败: " + e.getMessage());         }         return map;     }
  目的在消息数据表中插入一条数据,后模拟发生IO Exception后看事务是否回滚。
  如图数据插入成功,现在模拟异常,在saveMessage方法的实现类中插入数据后模拟异常看是否会插入数据 @Override     public void saveMessage(Message message) {         messageDao.saveMessage(message);         try {             throw new IOException("今日头条模拟异常抛出");         } catch (IOException e) {            log.error("erro",e);         }     }
  重启:
  说明什么?说明在发生异常后,数据库插入了数据,但是并没有回滚。有人说我没有加事务注解,为了测试效果我是加了的。
  我们看下事务注解@Transactional属性rollbackFor源码注释/** 	 * Defines zero (0) or more exception {@link Class classes}, which must be 	 * subclasses of {@link Throwable}, indicating which exception types must cause 	 * a transaction rollback. 	 * 

By default, a transaction will be rolling back on {@link RuntimeException} * and {@link Error} but not on checked exceptions (business exceptions). See * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)} * for a detailed explanation. *

This is the preferred way to construct a rollback rule (in contrast to * {@link #rollbackForClassName}), matching the exception class and its subclasses. *

Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}. * @see #rollbackForClassName * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ Class<? extends Throwable>[] rollbackFor() default {};   如果我们不指定异常类型,默认是By default RuntimeException运行时异常,那我们的IOException是继承了Exception,所以并不会回滚,结论:分析代码后要分情况,在加了事务注解后看是否执行异常回滚类,不指定默认是运行时异常,而io异常不是运行时异常的子类,则不会回滚,若指定了io异常类则会回滚。   再次验证,重启@Override @Transactional(rollbackFor = IOException.class) public void saveMessage(Message message) { messageDao.saveMessage(message); try { throw new IOException("今日头条模拟异常抛出"); } catch (IOException e) { log.error("erro",e); } }   数据库中是不会插入数据的。   思考:下列哪种情况会回滚?@Override @Transactional public void saveMessage(Message message) { messageDao.saveMessage(message); try { throw new IOException("今日头条模拟异常抛出"); } catch (IOException e) { log.error("erro",e); } } @Override @Transactional(rollbackFor = Exception.class) public void saveMessage(Message message) { messageDao.saveMessage(message); try { throw new IOException("今日头条模拟异常抛出"); } catch (IOException e) { e.printStackTrace(); } } @Override @Transactional(rollbackFor = Exception.class) public void saveMessage(Message message) throws IOException { messageDao.saveMessage(message); throw new IOException("今日头条模拟异常抛出"); } @Override @Transactional(rollbackFor = IOException.class) public void saveMessage(Message message) throws IOException { messageDao.saveMessage(message); throw new IOException("今日头条模拟异常抛出"); }


不怕超时,福州女外卖员路边热舞,婀娜多姿,让路人一脸懵圈女外卖员不送餐却在路边热舞,姿势妖娆,让辛苦的男骑手情何以堪周末,福州天气好,于是便去了商场逛街,正值中午12点,在商场广场的角落里,看到了一位身穿黄色外卖工作服,头戴黄色头盔的女5000亿新能源巨头深夜减持超20亿,汽车整车赛道还能走多远?一合伙创始人套现21亿28日晚间,比亚迪发布公告,公司持股3。31的董事夏佐全拟减持不超过1200万股A股,占其所持A股总股数比例不超过12。69,占公司总股本不超0。42。按周五零跑C11一款有备而来的新能源汽车随着新能源汽车产品的不断涌入,电车市场已经接近饱和状态,如果一款新能源汽车想要在饱和的市场中取得一席之地,一定要有备而来,这样才能更快地在市场中站稳脚跟,成为人们关注的那一款汽车,红外热成像的正确使用方法凯茉锐红外热成像利用红外探测器和光学成像物镜接受被测目标的红外辐射能量分布规律,并将其反射到红外探测器的光敏元件上,从而获得红外热像,对应于物体表面的热分布场。一般来说,红外热成像就是将有家,有她,还有TA,完美生活该是这样完美生活不会被物质所困,也不会有出行的烦恼。幸福家庭的标配,离不开稳定的居所,身边的爱人,还有能说走就走的自由编辑搜图请点击输入图片描述和很多朋友一样,我们家买车就图出行方便,选择兴趣认证,想获得四个优质答案推荐,需要满足什么条件?今日头条官方给出的回答秘籍一选择正确的问题1回答以下问题能获得高阅读量有讨论价值的问题例如为什么老干妈没有遇到强劲的对手优质回答较少的问题选择优质回答较少的问题更容易获得推荐。2不打工人超长带妆,皮肤状态堪忧,是时候做个面部护理啦前些日子国家刚刚出台了三胎政策,办公室里的同事也都在热议,大家纷纷表示,平时工作起来忙的连饭都吃不上,就连谈恋爱的时间都没有,婚都没结,哪里能考虑到生孩子的问题啊。现在的年轻人们即大卖背后,realme真我GT大师探索版做对了哪些事情?realme真我GT大师探索版开售了,可能令大多人意想不到的是它取得了非常好的成绩1秒破亿以及销量销售额双冠军。1秒破亿这个成就听起来非常震撼,它的数据来自realme真我的正规开为什么杭州只要三环?答案在这里很多人都知道,北京有七环成都有六环,为什么杭州只要三环?我们听听专家怎么说!8月12日,杭州召开杭州都市区中环建设推进动员会,在十四五建成杭州市区中环和杭州二绕。杭州中环目前建成的安全模式到底有什么用,不同系统怎么进入安全模式?答案都在这电脑蓝屏流氓软件的克星,方法简单,快速上手,居家旅行杀人灭口必备良药,走过路过,不要错过吖!每次电脑出问题的时候,问工程师小哥哥,他们都要进入安全模式看一下,安全模式到底犯了什么错设计模式16状态模式状态模式当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不
由内而外的优雅V字的西装暴徒光影精灵7实测,优雅永不过时现在一提到某品牌笔记本很快就能想到某系列笔记本怎么样的,好比如提到联想游戏本就立马能想到现在非常火热的拯救者系列,不过提到惠普,那第一时间想到的肯定也是暗影精灵系列。有暗肯定也有光B站日活跃用户超越优酷没广告是核心竞争力?炣燃科技8月12日讯(宏斌)据媒体报道,知名视频网站哔哩哔哩(Bilibili)日活跃用户已经于近日突破了6500万,成功超越优酷,成为仅次于爱奇艺与腾讯视频的第三大长视频平台。对小米11UItra大跳水小米11UItra大家都不陌生吧,重量234g。搭配高通骁龙888处理器,E4的屏幕材质,完美的6。81寸曲面屏,120Hz刷新率,真的香,前置2000万后置9600万,5倍的光学矿主新宠,RX6600XT挖矿效率奇高,能效比碾压所有N卡挖矿行情起起伏伏,但大家期待已久的终极矿难迟迟没有到来,目测短期内也不会有,臭打游戏得只能继续忍。更危险的是,就连最新发布的RX6600XT这么一个主流游戏卡,估计也逃不过矿主的法解封了!挡板拆除红码变绿快乐起飞视频加载中特大好消息!就在今天,成都这些疫中人出圈了!挡板拆除,红码变绿,极速通道现已开通!无需排队,无需等候!专业抗疫团队,为你保驾护航,帮你重塑新生!只要你走出嚣张的步伐!新冠高刷屏放光芒,OPPO旗舰新机曝光,或对标小米MIX4?一提到OPPO手机,想必大家都不会陌生,OPPO手机作为国内知名的手机厂商,也是深受年轻人喜爱的手机厂商。OPPO手机之所以取得不错的成绩,是因为OPPO手机不仅设计十分新颖,而且七夕送什么礼物最走心?高颜值蓝牙耳机品牌推荐每到七夕情人节等重要节日,对于情侣来说还蛮头疼的,送礼物比较难挑选,无比纠结,也不确定对方是否满意。今天就给大家带来了几款高颜值的平价蓝牙耳机!七夕礼物纪念日礼物生日礼物都能用来参一加品牌日正式开启多款手机免息分期最高优惠900元手机中国新闻8月12日00008月13日2400,一加品牌日正式开启,多款产品降价优惠。购买一加9和9Pro不但支持24期免息,而且至高优惠700元。除此之外,其他产品也可享受不同电动车无线充电技术被激活,传统充电桩会被淘汰?工信部近日向社会征求意见,主要内容是针对电动车无线充电的标准进行规范。以便于日后无线充电产业的发展。以前的固定充电桩由于充电电流的限制,不能短时间内马上充满,一停车就要等一两个小时这两天用手机真爽,各大APP开屏广告消失,猜猜能持续多久?对于相当多的用户来说,当他们每天打开应用程序时,往往会弹出屏幕打开自己不感兴趣的信息。但是,当用户想跳过打开屏幕的内容时,跳过按钮往往模糊体积小不易触摸。此外,显示的可疑跳过按钮实小米MIX4搭载这项技术或将颠覆智能家居行业小米MIX4发布在即,根据爆料,除了搭载屏下摄像头之外,小米MIX4还将搭载UWB技术。什么是UWB呢?UWB是英语超宽带(Ultrawideband)的缩写,诞生于上世纪60年代