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

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("今日头条模拟异常抛出"); }


航拍雄安白洋淀码头比邻三面环水别墅群,空中俯瞰美轮美奂4月19日,在位于河北省雄安新区雄县的白洋淀温泉城码头,岸边垂柳吐出新芽,春风拂过,轻盈婆娑。一辆辆摩托艇在淀面上飞驰划过,不时向岸上的游人展示环形漂移的高难度动作,激起层层的浪花天才少女谷爱凌解锁新身份5G冰雪数智达人MEETGU亮相10月27日,以5G领跑助力中国冰雪为主题的中国移动5G冰雪之队发布会通过咪咕视频在云端直播举行。国家体育总局冬运中心副主任党委副书记黄志勇,中国移动副总经理简勤,谷爱凌徐梦桃隋文抓住那只大肥鹅那只鹅曾经很火,也被认为是相当抗寒。不过,皮再厚的鹅,也架不住火上烤。知名国际羽绒服品牌加拿大鹅近日引发了不小的关注。该品牌由于在广告中宣称使用的羽绒来自加拿大北部的哈特莱特地区,超前点播会不会是压倒长视频的稻草?最近,某长视频平台出了一个叫做超前点播的业务,在出台这项业务之前,VIP的用户可以比普通用户多看6集,而有了这个超前点播,用户在VIP的基础上再付50块钱就可以又多看6集。事情变得公厕界的天花板!来河北这座五星级厕所,享受沉浸式如厕有时间一起上厕所有幸去过一次没敢拉出来这厕所比我家都好!最近,河北省石家庄市区的一座公厕,刷爆了朋友圈。厕所不仅有高颜值,还有惊人的高智商。人脸识别智能机器人大数据分析到处能看到黑这仅是给网游的第一拳据媒体报道,有关部门要求,严格限制向未成年人提供网络游戏服务的时间,所有网络游戏企业仅可在周五周六周日和法定节假日每日20时至21时向未成年人提供1小时服务,其他时间均不得以任何形前端图形入门在WebGL中实现3D物体的选中和操作最近一年一直在做3D图形相关的应用,入坑前端图形也了一年,想在这里跟大家分享一下自己学习到一些图形基础知识。今天跟大家分享一下在Web中实现3D物体的选中和移动旋转缩放操作。我们先中国电信还会有几个跌停?支撑位在哪里?中国电信终于在A股上市,上市当天有惊无险,还上涨了不少,但适逢周五,到了第二个交易日的周一,就形势大变。于是,开盘跌停,接下来第二日,开盘依然是跌停。要知道,同样是周二这一天,港股共同富裕需要更多的1000亿,而不是拆字谜据阿里巴巴集团,阿里已启动阿里巴巴助力共同富裕十大行动,将在2025年前累计投入1000亿元,助力共同富裕。为促进十大行动落地,阿里将成立一个专门的常设机构。据了解,阿里助力共同富赛道宽体家轿,东风风神献给新手爸爸的厚礼30而立做爸爸,一款新车爱家庭奕炫MAX是一辆定位于赛道级宽体家轿的潮流座驾,赛道风暴美学是他的设计语言,超越同级的2770mm轴距和1870mm车宽,豪华感扑面却不凝重,搭配音爆中国足球为什么还是不行?第一场,主动出击,被澳大利亚打了个头破血流第二场,龟缩防守,被日本队压制得全场喘不过气。这就是中国足球队。很多人怀念80年代的中国足球,那个时候什么可以吊打日本很多人怀念米卢时代的
工信部回应轻微摇动App就跳广告加强监督工信部表示,近期,发现部分企业采用摇一摇扭一扭作为跳转链接,用户在非知情自愿的情况下,仅轻微摇动或扭动即误导用户触发页面跳转。工信部已组织召开专题会议,要求涉及的主要互联网企业落实戴珊能搬走阿里的大象吗?很少有人能预料到,戴珊走马上任阿里巴巴集团中国数字商业板块分管总裁后,第一把火会烧得如此之猛如此之准。今日,戴珊发布内部信,宣布阿里中国数字商业板块进行新一轮组织架构调整,调整重点耳朵不好,戴助听器能治疗吗?耳朵不好,戴助听器并不能治疗耳朵的疾病,如同拄一个拐杖可以帮助行走,但是拐杖不能治疗腿疼。按照中医的理论肾开窍于耳,往往年岁大了以后,年老体衰,肾就肾虚了。肾为先天之本,随着年龄的三星s22系列设计和配色完全曝光,简单直接的配色风格三星s22设计曝光了,看起来这次和上一代的设计差不多?这个手机看起来后置摄像头设计很简单,但看起来配色不错,颜色看起来有白色,而且确实是简单的设计思路,今年的旗舰手机思路看起来就是K8s的探针探针是由kubelet对容器执行的定期诊断,要执行诊断,kubelet调用容器实现的Handler。说句废话,pod的探针肯定不能给init容器执行探针,init容器执行之后就退出36氪首发安天科技完成C轮融资,整体融资额为9亿元36氪获悉,以自主威胁检测分析技术为内核的全栈能力型厂商安天科技(以下简称为安天)宣布完成C轮融资。据了解,本轮融资规模为9亿元人民币,主要投资方为大型国有基金知名产业投资机构,具腾讯涉诉案件研究丨授权期满后播放谢谢你来了,被判侵权了裁判规则访谈综艺节目属于电影作品或以类似摄制电影的方法创作的作品,依法受著作权法的保护。腾讯公司在授权期限届满后,未经广电英度公司许可,在其腾讯视频上向公众提供涉案作品,侵害了广电移动老用户恭喜了,10年不换号的老用户,可享受4大特权现在的中国移动,是我国最大的通信运营商,因为它的客户是最多的。据中国移动公布的数据显示,截止2021年11月底,中国移动的总用户数已经达到了9。56783亿户,5G用户也累计达到了小巧又便携的电子阅读器,墨案这款新品仅需百元即可墨案作为近年来比较火热的电纸书厂商,旗下推出了多款定位不同的墨水屏产品,其中迷你阅inkPalm5更是近期火热的一款,在功能设计和操控等方面都进行了升级,为我们带来了全新的电纸书使更轻更薄更好用,努比亚大白65W超薄氮化镓充电器评测氮化镓充电器凭借体积小功率高的优势一经上市,便倍受消费者的青睐。目前市面上的氮化镓充电器也是琳琅满目,造型和功率也都不尽相同。目前比较主流的就是65W氮化镓充电器,它基本上可以满足互联网思维下的用户体验和用户创造在讨论购物中心业态创新的时候,业内把传统的零售餐饮娱乐和服务业态重新划分为所谓的体验业态和非体验业态。所谓体验业态,就是用户直接体验了整个交易过程,例如饮食看电影游戏美容,其交易过