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

Spring长事务导致connectionclosed,又熬了一个大夜

  是的,今早一到公司就收到了机器人的告警,从异常日志来看是数据库连接已关闭,然后我在解决这个问题的过程中发现了几个问题,不急,听我一一道来
  异常被try后没有继续抛出,导致继续执行后续操作
  我们看到前文示例代码会发现我们在 try 之后只是 rollback 了,对于异常也只是打印一下并没有继续抛出。
  那么就会导致一种情况:假设你在 Service 层中调用多个调用数据库的修改方法,那么第一个操作失败后异常没有抛出,Service 层不知道,就会继续向后面执行,修复很简单,只需要将异常抛出即可:  // 案例1:参考MybatisPlus的com.baomidou.mybatisplus.extension.toolkit.SqlHelper##executeBatch()实现 batchSqlSession.rollback(); Throwable unwrapped = ExceptionUtil.unwrapThrowable(e); if (unwrapped instanceof RuntimeException) {     MyBatisExceptionTranslator myBatisExceptionTranslator             = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);     throw Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException) unwrapped)); } throw new CommonException(unwrapped);  // 案例2:简单来说,只要能把异常抛出去即可,并不定需要像上面这么复杂 batchSqlSession.rollback(); throw new CustomException(e); 大事务/长事务导致 connection closed代码场景
  我们来看一段业务功能的伪代码,大致如下:  @Transactional(rollbackFor = Exception.class) @Override public Integer billCheck() {     // 获取对应的策略     策略 = getStrategy();     // 前置参数校验     if (必要参数是否存在){         return false;     }     try {         // 解析文件         文件里的数据集合 = 策略.parseFile(file);         // 将文件里的数据插入数据库表         影响的行数 = 策略.handleFileData(文件里的数据);         if (影响的行数 > 0) {             // 将文件里的数据和本地的数据进行对比操作             对比后的数据 = 策略.doBillCheck(参数);             // 将对比的结果分开插入到数据库中             batchUtils.batchUpdateOrInsert(成功的数据,                     某Mapper.class,                     (billErr, mapper) -> mapper.insert(data));             batchUtils.batchUpdateOrInsert(失败的数据,                     某Mapper.class,                     (billErr, mapper) -> mapper.insert(data));             batchUtils.batchUpdateOrInsert(需要更新的数据,                     某Mapper.class,                     (billErr, mapper) -> mapper.update(data));         }         // 发送企业微信机器人通知         策略.sendRobotMessage();         log.info("耗时:{}毫秒", 耗时);     } catch (Exception e) {         log.error("对账出错", e);         throw new CommonException("对账出错");     }     return 影响的行数; }
  我们梳理一下,这是一个普通的模板方法 + 策略模式的应用,因为业务场景中不管是哪个通道的文件都会必经如下几个步骤,所以就将其抽象了。我们可以发现这个方法里面做了很多数据库操作,并且使用了声明式事务注解,然后里面大致有如下几个步骤:  解析文件  将文件里的数据插入数据库表  将文件里的数据和本地的数据进行对比操作  将对比的结果分开插入到数据库中
  然后我们再来看一段配置,它来自  druid  连接池框架,如下: spring:   datasource:     druid:       remove-abandoned: true       ## 单位:秒       remove-abandoned-timeout: 60       log-abandoned: true
  以上三条属性一般是用来 防止连接泄露 的,说明如下: removeAbandoned :要求获取到连接后,如果空闲时间超过 removeAbandonedTimeoutMillis  秒后没有 close,druid 会强制回收,默认false; logAbandoned :如果回收了连接,是否要打印一条 log,默认 false; removeAbandonedTimeoutMillis :连接回收的超时时间,默认5分钟;
  看到这里我想大部分同学可能已经知道是什么问题了,没错, 肯定是因为拿到了连接,但拿的时间超过了这个限制,导致 druid 直接强制回收了该连接 ,但是知根知底方能百战百胜,这么好的机会怎么能不深入了解一下?关注公号:码猿技术专栏,回复关键词:1111 获取阿里内部性能调优手册~ 什么时候获取的连接?
  是的,既然是连接超时被关闭,那我们肯定要先找到是什么时候拿到的连接,是方法中第一次操作数据库【将文件里的数据插入数据库表】的时候?那当然不是,我们知道 Mybatis 有一个 Executor_ _接口,感兴趣的可以自行了解,它定义了数据库操作的基本方法,它才是SQL语句幕后的执行者,我们直接来看获取连接的地方  org.apache.ibatis.executor.BaseExecutor##getConnection : protected Connection getConnection(Log statementLog) throws SQLException {     Connection connection = transaction.getConnection();     if (statementLog.isDebugEnabled()) {         return ConnectionLogger.newInstance(connection, statementLog, queryStack);     } else {         return connection;     }   }
  我们可以看出来,我们是通过  Transaction  去获取连接的,但如果我们是第一次操作的时候才去获取的连接,那怎么会连接超时呢?所以我初步推断是开启事务的时候可能就已经获取连接了,那我们来求证一下,来到 Spring 的事务管理器PlatformTransactionManager ,Mybatis 用的是它的实现类DataSourceTransactionManager , 然后我们一路跟 getTransaction  方法来到AbstractPlatformTransactionManager##getTransaction ,再到DataSourceTransactionManager##doBegin  public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {     // 省略无关代码 ...     doBegin(transaction, definition);     // 省略无关代码 ... }  @Override protected void doBegin(Object transaction, TransactionDefinition definition) {     DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;     Connection con = null;      try {         // 如果数据源事务对象的ConnectionHolder为null或者是事务同步的           if (!txObject.hasConnectionHolder() ||                 txObject.getConnectionHolder().isSynchronizedWithTransaction()) {             // 获取当前数据源的数据库连接               Connection newCon = obtainDataSource().getConnection();             if (logger.isDebugEnabled()) {                 logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");             }             txObject.setConnectionHolder(new ConnectionHolder(newCon), true);         } }
  就是这!它其实在进入方法的最开始,开启事务的时候就已经获取了连接,然后由于【解析文件】耗时过长,导致整个方法的执行时间超过了 60s 被强制回收连接,但你以为这就结束了?没错,当时出现这个问题的时候,我还手动触发了一次,结果第二次通过了,你说诡异不诡异?两次执行的时间都是 90s。  druid removeAbandoned 背后的秘密
  所以我们继续看一下 druid 是怎么强制回收连接的,Druid每隔 timeBetweenEvictionRunsMillis (默认1分钟)会调用DestroyTask,在这里会判断是否可以回收泄露的连接,就是因为它是1分钟执行一次,所以可能第二次正好它执行的时候还没超过 60s,所以这次简直就是玄学了啊。 public class DestroyTask implements Runnable {     public DestroyTask() {      }      @Override     public void run() {         shrink(true, keepAlive);         // 判断removeAbandoned是否为true,默认是false         if (isRemoveAbandoned()) {             removeAbandoned();         }     }  }
  然后我们看到 removeAbandoned 方法,这里面有一段代码如下:  for (; iter.hasNext();) {     DruidPooledConnection pooledConnection = iter.next();      // 判断该连接是否还在运行,只回收不运行的连接     // Druid会在连接执行query,update的时候设置为正在运行,     // 并在回收后设置为不运行     if (pooledConnection.isRunning()) {         continue;     }      long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);      //判断连接借出去的时间大小     if (timeMillis >= removeAbandonedTimeoutMillis) {         iter.remove();         pooledConnection.setTraceEnable(false);         abandonedList.add(pooledConnection);     } }  //判断是否要记录连接回收日志,这个很重要,可以及时发现项目中是否有连接泄露 if (isLogAbandoned()) {     StringBuilder buf = new StringBuilder();     buf.append("abandon connection, owner thread: ");     buf.append(pooledConnection.getOwnerThread().getName());     buf.append(", connected at : ");     buf.append(pooledConnection.getConnectedTimeMillis());     buf.append(", open stackTrace "); }
  是的,如果你的连接被强制回收了的话,你只需要将  LogAbandoned  设置为 true,就可以通过日志看到相关信息了 解决方案
  到这,问题就基本都发现了,那么我最后是怎么解决的呢?原本我是想的把不需要事务的动作抽离出来新建一个方法,后面我发现这样子好像模板方法并不好使了,我就采用了 编程式事务 ,感兴趣的可以自己在了解一下,最后伪代码如下: @Autowired private TransactionTemplate transactionTemplate;  @Transactional(rollbackFor = Exception.class) @Override public Integer billCheck() {     // 获取对应的策略     策略 = getStrategy();     // 前置参数校验     if (必要参数是否存在){         return false;     }     try {         // 解析文件         文件里的数据集合 = 策略.parseFile(file);         // 编程式事务         影响的行数 = transactionTemplate.execute(transactionStatus -> {             // 将文件里的数据插入数据库表             return 策略.handleFileData(文件里的数据);         });         if (影响的行数 > 0) {             // 将文件里的数据和本地的数据进行对比操作             对比后的数据 = 策略.doBillCheck(参数);             // 编程式事务             transactionTemplate.execute(transactionStatus -> {                 // 将对比的结果分开插入到数据库中                 batchUtils.batchUpdateOrInsert(成功的数据,                         某Mapper.class,                         (billErr, mapper) -> mapper.insert(data));                 batchUtils.batchUpdateOrInsert(失败的数据,                         某Mapper.class,                         (billErr, mapper) -> mapper.insert(data));                 batchUtils.batchUpdateOrInsert(需要更新的数据,                         某Mapper.class,                         (billErr, mapper) -> mapper.update(data));                 return Boolean.TRUE;             });         }         // 发送企业微信机器人通知         策略.sendRobotMessage();         log.info("耗时:{}毫秒", 耗时);     } catch (Exception e) {         log.error("对账出错", e);         throw new CommonException("对账出错");     }     return 影响的行数; }
  这样子,我们将解析文件和对比数据(只是查询)这种耗时操作放在了事务外,并且将原本一个事务里的操作拆成了两个小事务,这样子基本就避免了大事务的问题了,完结撒花~  大事务/长事务可能造成的影响并发情况下,数据库连接池容易被撑爆  锁定太多的数据,造成大量的阻塞和锁超时  执行时间长,容易造成主从延迟  回滚所需要的时间比较长  undo log膨胀
  所以在业务涉及中,你一定要对大事务特别对待,比如业务设计时,把大事务拆成小事务。  总结
  声明式事务有一个局限,那就是他的最小粒度要作用在方法上 !所以大家在用的时候要格外格外注意大事务的问题,尽量避免在事务中做一些无关数据库的操作,比如RPC远程调用、文件解析等,都是血泪的教训啊!!
  原文链接:https://mp.weixin.qq.com/s/NEo8zo4YDOMnu87kBbjjnw

美女实名举报前国足球员婚内出轨买海参都是我出钱!相信大家都看了今年的春晚,小岳岳给春晚预测了四个热搜,没想到的是,这年还没过完呢,咱们的国足又上了热搜,国足这几年确实口碑确实不怎么样,除了之前的足协领导层的反腐大地震,近期又爆出范志毅武磊力挺!徐根宝迎来78岁生日,足协送出祝福,宣布目标大家好,我是詹妹,我们一起来关注中国足球。目前中国足球还是因为李铁案陷入了一地鸡毛,出现了打假球,还有赌球。前几天足协的两位高层被带走,可以说中国足球走到如今,和足协的管理也分密不卡梅隆托马斯我会继续向KD欧文请教让自己变得更好直播吧1月25日讯近日篮网小将卡梅隆托马斯接受了媒体采访。记者问道你觉得球队处理杜兰特缺阵时的能力如何?托马斯表示我觉得本赛季我们有一个很棒的团队来处理这件事,上赛季,我们处理得不不再续约?梅西有意离开巴黎,4个潜在下家出炉,或与C罗重逢在率领阿根廷拿到世界杯冠军之后,梅西和大巴黎之间的续约问题就成为了外界关注的焦点,从过去一段时间传出的新闻来看,双方都在朝着续约的方向在努力,但自从梅西归队参加比赛之后,事情已经发塔塔国际象棋大师赛第九轮结束,吉里表现优异2023年塔塔钢铁国际象棋锦标赛第九轮中,迪尔贝克阿卜杜萨托罗夫对阵文森特凯默激战近七个小时,最终以和棋收场,保持了领先优势。阿尼什吉里现在紧随其后,战胜丁立人,缩小了他们之间的差金智秀出席巴黎时装周,齐刘海造型却差评一片,美女也要挑造型最近巴黎时装周即将举行,许多明星都前往巴黎参加活动,作为迪奥品牌大使的blackpink成员金智秀也出席了这次活动,不过看起来观众对她这次的造型似乎评价不太高。金智秀穿着迪奥高定服福建人,公积金贷款的月供,今年可以省一笔了公积金贷款的月供,今年可以省一笔了。2022年9月30日,人民银行决定,自2022年10月1日起,下调首套个人住房公积金贷款利率0。15个百分点,5年以下(含5年)和5年以上利率分健身er们阳康后,如何科学稳妥地恢复运动?视频加载中新冠感染后阳康了好想赶快回健身房撸铁可现在到底适不适合锻炼?应该怎样科学恢复运动?这是近期不少小伙伴们关心的问题让我们听听专家们怎么说问题一恢复期到底能不能运动?山西白求2022年新能源汽车保有量增长67。13数据来源公安部制图汪哲平本报北京1月11日电(记者张天培)公安部11日发布统计显示,2022年全国机动车保有量达4。17亿辆,其中汽车3。19亿辆全国机动车驾驶人达5。02亿人,其透视春运经济大数据出行链复苏,人们都去哪儿了?2023年春运已拉开帷幕。或返乡,或出游,或商旅,在这40天里将有近21亿人次踏上旅途,为流动的中国再添活力。人口流动如同城市系统运转的血液,彰显出城市的活力和吸引力。春运首日,全专访金晶集团董事长王刚拥抱绿色趋势,作碳中和时代的冲锋者1月11日,金晶集团旗下的山东金晶科技股份有限公司博山分公司太阳能导电玻璃基板生产车间里热浪扑面而来,温度高达1500多度的玻璃液在窑炉中翻滚流淌,经过沉积退温镀膜等工艺,生产线终
python学习whileTrue的用法学习python过程中,我们经常会遇到whileTrue的用法。今天我们来讲解下它的用法。一理论while(true)是一个无限循环,表示一直为真。()里的是while的条件,trJAVA开发搞了一年多大数据的总结2021年7月份加入了当前项目组,以一个原汁原味的Java开发工程师的身份进来的,来了没多久,项目组唯一一名大数据开发工程师要离职了,一时间一大堆的数据需求急需人来接手,此刻又招不刚上市就火了,和奔驰GLS同级,5。3秒破百能上绿牌,实拍理想L9对于新能源车型,大部分消费者主要考虑的还是续航问题,担心在路途中电量耗尽,没有办法及时补充电量,所以不敢下手。对于这个方面的问题,可能增程式的新能源汽车能够得到解决,在电动的基础上真菌毒力效应因子触发过敏性炎症的机制撰文十一月侵袭性真菌病原体每年会造成大约150万人的死亡,其导致的疾病称为被忽略的流行病。新的耐药性病原体层出不穷,现有的药物疗效非常有限且毒副作用很高。但是目前真菌病原体是如何逃一个时代的落幕媒体人周刊不知不觉一个时代就要落幕了。马云走了,远赴欧洲。刘强东退出京东,转让了股份。腾讯也在和联通炒CP。互联网的巨头们,逐渐迎来了自己的结局!03年的非典,不出门,也能购物的销新能源代步微型车再添一员悍将,雷诺江铃小麒麟即将上市随着五菱宏光miniev的火爆,也使各家车企甚是眼红,于是各类微型车层出不穷,这不,近日雷诺江铃集团官方就正式曝光了,旗下全新的新能源微型车雷诺江铃小麒麟,并宣布将于11月11日正小鹏的宿命翻看一下股市行情,小鹏汽车的股价一路下跌,标志着新能源汽车这场大戏的开场造车新势力篇正在落幕。何小鹏的造车梦要么就此终结,要么换个地方圆梦,股市已经告诉人们一切。记得当年在中国汽车图扑软件3D组态编辑器,低代码零代码构建数字孪生工厂行业背景随着中国制造2025计划的提出,新一轮的工业改革拉开序幕。大数据积累的指数级增长为智能商业爆发奠定了良好的基础,传统制造业高污染高能耗低效率的生产模式已不符合现代工业要求。县委书记亲自推销,浙江小镇保姆为何被大城市秒抢?11月财经新势力常山模式,给国内一些没有资源产业的劳务输出地区带来启示。正解局出品不久前,一篇题为我的阿姨被人以1万月薪挖走了的文章,讲述了在深圳的一位二胎妈妈请阿姨的苦恼阿姨价格纪晓岚断案,自称短视,其实是大智慧纪晓岚断案,自称短视,其实是大智慧清乾隆五十年,乾隆皇帝有感于纪晓岚编篡四库全书的功劳,颁旨提拔纪晓岚为都察院御史,掌管全国最高监察机构,考察官吏,整饬纲常。显示出乾隆皇帝对纪晓岚浙江九旬老太陈金英,10年还清2077万欠款,她是怎么做到的?你可曾想过有这样一位老人,年过九旬还在大街上经营着自己的生意,只为能还清两千多万的欠款。十年的风风雨雨,她没了自己的工厂,没了自己的住房也没了自己的丈夫,但是她始终没有遗忘自己心中