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

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


为了创新和变化!支付宝开启近7年最大一次高管轮岗CNMO新闻近日,据CNMO了解,有相关媒体报道称,蚂蚁集团旗下支付宝事业群近期开启了近7年来最大一次高管轮岗,涉及多个部门多位副总裁以及资深总监职务。此外,支付宝事业群还于近期新45月社保缴费养老工伤失业等迎8变化,事关我们的钱袋子在这万物复苏的时节里,绿树抽芽,花开竞艳,不仅给人们的生活出行增添了一丝温柔,社保方面也迎来许多新调整,非常暖心。45月份,有8件大事非常值得关注,这关系着社保缴费,养老工伤失业等80。9公里高速改扩建,安徽投资83。67亿!双向八车道,时速120公里安徽省位于我国的华东部地区,属于中国中东部经济区。我国的经济在历经了70多年的快速工业化进程后,许多省份都得到了充分的发展,这也推动着我国逐渐走向了经济大国和制造业大国。2022年梁允超不可接受,但汤臣倍健又能如何?从1930年医学界确认了人体所需的40多种营养物质开始,保健品似乎就走进人类社会当中。到今天,市场上各式各类的保健品也是层出不穷。而在国内市场,提到保健品,汤臣倍健无疑是绕不开的一美国监管方动手!CFTC指控币安和赵长鹏明知故犯非法经营,宣称对整个币圈示警本已遭到美国监管方围剿的全球最大加密货币交易所币安又遭打击。美东时间3月27日周一,美国商品期货交易委员会(CFTC)宣布,向伊利诺伊州北区地区法院提起民事诉讼,指控币安创始人兼C美国真正恐怖的金融风暴还没到来?硅谷瑞信危机只是件小事!硅谷银行本身还不足以造成美国的金融危机,但是不等于美国将来没有,美国几乎一定在将来,也许半年后,也许一年后,总之将来一定会有大型的金融风暴来袭!我们可以从08年金融危机中找到答案,法外狂徒孙宇晨,这次被美国FBI盯上了你可能听说过孙宇晨,一个总是在制造或者登上热点的男人,一个在割韭菜领域留下无数疯狂故事的男人。一个多星期前刚被FBI盯上的孙宇晨,最近又陷入了一场风波,美国证券交易委员会(SEC)星河暗淡,你是唯一的光生活有伤,也有光芒。人生何其短,笑要格外甜。假如人生有如果,但求只如初相见。这次离开你,是风是雨是夜晚。庆幸你来自远方,未曾听及我过往。山野万万里,余生路漫漫。我们都在变,何必感慨今天将上演五星伴月奇观!全球都能观测到美国天文学家提醒,水星木星金星天王星火星和月球已经罕见地在天空中排成一条弧线,28日日落后观测最佳。据美联社援引美国国家航空航天局天文学家比尔库克的话报道,只要天空晴朗无遮挡远离明网曝刘亦菲在梦华录中不止一个替身,爆料者杖责那一场也有替身在2023微博之夜的盛典上,80后女星们一个比一个闪亮,简直让观众们看花了眼。特别是刘亦菲,她凭借孔雀公主造型和微博Queen称号成功获得更多人的喝彩,又妥妥地火了一把。在为刘亦菲顶流刘宇宁前妻被曝光,颜值不输内娱女星,当事人回应真相身处娱乐圈乱流的明星们,根本没有什么隐私可言。随着名气的增大,粉丝关注,狗仔24小时不间断扒料,即使藏得再深,也就被扒个精光。深知没有不透风的墙的内娱明星们,往往都会选择自动官宣相
语音控制智能家居提到语音控制,人们自然会想到晚上回家在楼道里弄出动静,使楼道照明灯打开的情景,只需跺跺脚或咳嗽或大声喊叫,楼道里的灯光就亮了,然后过几十秒灯又灭了,确切地说这是声控操控,在光线不足出其不意!TCL洗衣机Q10全球最大上筒真传自孙子兵法?电视剧狂飙爆火,豆瓣评分9分,剧中高启强手中被翻烂的孙子兵法,成为图书爆款,在各大平台卖脱销读孙子兵法,品启强人生亦成为爆梗,被网友调侃。孙子兵法作为一本有着上千年历史的兵书,写尽父母高度近视,一岁婴儿近视六百度,如何保护视力?近日,河南郑州,慕女士在儿子一岁时带他做视力检查时近视度数竟然达到了六百度,十个月后再次检查度数仍然增长了一百度。慕女士的儿子之所以会有如此高的度数,其实是因为儿子携带了高度近视基老挝的生活,中年男人的梦想之地没有出国之前,我对出国充满了好奇,甚至提前十来天,就开始兴奋了。我想象中的去外国企业,会有比较好的福利和休息制度,因为我们看到欧美国家在中国的企业,很尊重当地的法律,可是当我们中国理清强拆和拆违的关系评女足队员家遭强拆事件近日,女足队员爆其家中遭强拆的事件冲上热搜,而且随着她再次发文质疑官方回应后,事件还在继续发酵中,就此事件,我想从下面几点来分析一什么是强拆,什么是拆违拆违,顾名思义,就是拆除违章婆媳关系为什么毁在坐月子上?几个原因,总有一个戳中你!婆媳关系,从古至今都是难题。按理说同为女人,女人何苦为难女人?可现实就是这么扎心,很多人进入婚姻后,尤其是女人生了孩子在月子里,婆媳关系往往会恶化。表妹刚生完孩子,还没出月子就和婆中年以后,比同龄人显年轻,有这4个好习惯有这样一种说法一个人年轻时的脸是父母决定的,但中年以后的脸应是自己决定的。有的人,年纪轻轻,却老气横秋有的人,韶华已逝,却看不到岁月留痕。抗衡时间的最好方式,莫过于养成良好的习惯方家庭教育投入有利于解决教育经费不足与需求无限扩展之间的矛盾家庭作为教育发展的直接利益相关者,其教育支出影响着幼儿成长,对教育质量具有重要影响,也有研究表明家庭教育投入有利于解决教育经费供给不足与需求无限扩展之间的矛盾,有利于保证教育改革与2023年,浙江省丧葬抚恤待遇新标准实施,还有这四项待遇会调整浙江省是我国最富裕的省份之一。2022年浙江省GDP达到了77715亿元,按不变价格计算增长5。1,跑赢了全国平均水平。人们的收入水平也随之提高。2022年,浙江省居民人均可支配收如何过的充实网上曾有人问这个世界上最了解你的是谁?有一个高赞回答是手机。仔细想想,确实如此。每天,从睁眼,到入睡,手机陪伴我们的时间占了一大半。从获取信息,到娱乐生活,从分享日常,到处理工作,人生,拟像元宵,生活更是如此经历过霜打会更甜,简单平凡也能收获满满喜爱。在网上看到了这句话,觉得很有意思,平凡而有意义,昨天与朋友喝茶聊天,说到关于吃饭,予人,哲理等问题,分享一下两个小主题,朋友们不喜勿喷。