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

调优MyBatissaveBatch25倍性能

  最近在压测一批接口,发现接口处理速度慢的有点超出预期,感觉很奇怪,后面定位发现是数据库批量保存这块很慢。
  这个项目用的是 mybatis-plus,批量保存直接用的是 mybatis-plus 提供的 saveBatch。
  我点进去看了下源码,感觉有点不太对劲:
  我继续追踪了下,从这个代码来看,确实是 for 循环一条一条执行了 sqlSession.insert,下面的 consumer 执行的就是上面的 sqlSession.insert:
  然后累计一定数量后,一批 flush。
  从这点来看,这个 saveBach 的性能肯定比直接一条一条 insert 快。
  我直接进行一个粗略的实验,简单创建了一张表来对比一波!粗略的实验
  1000条数据,一条一条插入    @Test     void MybatisPlusSaveOne() {         SqlSession sqlSession = sqlSessionFactory.openSession();         try {             StopWatch stopWatch = new StopWatch();             stopWatch.start("mybatis plus save one");             for (int i = 0; i < 1000; i++) {                 OpenTest openTest = new OpenTest();                 openTest.setA("a" + i);                 openTest.setB("b" + i);                 openTest.setC("c" + i);                 openTest.setD("d" + i);                 openTest.setE("e" + i);                 openTest.setF("f" + i);                 openTest.setG("g" + i);                 openTest.setH("h" + i);                 openTest.setI("i" + i);                 openTest.setJ("j" + i);                 openTest.setK("k" + i);                 //一条一条插入                 openTestService.save(openTest);             }             sqlSession.commit();             stopWatch.stop();             log.info("mybatis plus save one:" + stopWatch.getTotalTimeMillis());         } finally {             sqlSession.close();         }     }
  可以看到,执行一批 1000 条数的批量保存,耗费的时间是 121011 毫秒。
  1000条数据用 mybatis-plus 自带的 saveBatch 插入    @Test     void MybatisPlusSaveBatch() {         SqlSession sqlSession = sqlSessionFactory.openSession();         try {             List openTestList = new ArrayList<>();             for (int i = 0; i < 1000; i++) {                 OpenTest openTest = new OpenTest();                 openTest.setA("a" + i);                 openTest.setB("b" + i);                 openTest.setC("c" + i);                 openTest.setD("d" + i);                 openTest.setE("e" + i);                 openTest.setF("f" + i);                 openTest.setG("g" + i);                 openTest.setH("h" + i);                 openTest.setI("i" + i);                 openTest.setJ("j" + i);                 openTest.setK("k" + i);                 openTestList.add(openTest);             }             StopWatch stopWatch = new StopWatch();             stopWatch.start("mybatis plus save batch");             //批量插入             openTestService.saveBatch(openTestList);             sqlSession.commit();             stopWatch.stop();             log.info("mybatis plus save batch:" + stopWatch.getTotalTimeMillis());         } finally {             sqlSession.close();         }     }
  耗费的时间是 59927 毫秒,比一条一条插入快了一倍,从这点来看,效率还是可以的。
  然后常见的还有一种利用拼接 sql 方式来实现批量插入,我们也来对比试试看性能如何。
  1000条数据用手动拼接 sql 方式插入
  搞个手动拼接:
  来跑跑下性能如何:    @Test     void MapperSaveBatch() {         SqlSession sqlSession = sqlSessionFactory.openSession();         try {             List openTestList = new ArrayList<>();             for (int i = 0; i < 1000; i++) {                 OpenTest openTest = new OpenTest();                 openTest.setA("a" + i);                 openTest.setB("b" + i);                 openTest.setC("c" + i);                 openTest.setD("d" + i);                 openTest.setE("e" + i);                 openTest.setF("f" + i);                 openTest.setG("g" + i);                 openTest.setH("h" + i);                 openTest.setI("i" + i);                 openTest.setJ("j" + i);                 openTest.setK("k" + i);                 openTestList.add(openTest);             }             StopWatch stopWatch = new StopWatch();             stopWatch.start("mapper save batch");             //手动拼接批量插入             openTestMapper.saveBatch(openTestList);             sqlSession.commit();             stopWatch.stop();             log.info("mapper save batch:" + stopWatch.getTotalTimeMillis());         } finally {             sqlSession.close();         }     }
  耗时只有 2275 毫秒,性能比 mybatis-plus 自带的 saveBatch 好了 26 倍!
  这时,我又突然回想起以前直接用 JDBC 批量保存的接口,那都到这份上了,顺带也跑跑看!
  1000条数据用 JDBC executeBatch 插入    @Test     void JDBCSaveBatch() throws SQLException {         SqlSession sqlSession = sqlSessionFactory.openSession();         Connection connection = sqlSession.getConnection();         connection.setAutoCommit(false);          String sql = "insert into open_test(a,b,c,d,e,f,g,h,i,j,k) values(?,?,?,?,?,?,?,?,?,?,?)";         PreparedStatement statement = connection.prepareStatement(sql);         try {             for (int i = 0; i < 1000; i++) {                 statement.setString(1,"a" + i);                 statement.setString(2,"b" + i);                 statement.setString(3, "c" + i);                 statement.setString(4,"d" + i);                 statement.setString(5,"e" + i);                 statement.setString(6,"f" + i);                 statement.setString(7,"g" + i);                 statement.setString(8,"h" + i);                 statement.setString(9,"i" + i);                 statement.setString(10,"j" + i);                 statement.setString(11,"k" + i);                 statement.addBatch();             }             StopWatch stopWatch = new StopWatch();             stopWatch.start("JDBC save batch");             statement.executeBatch();             connection.commit();             stopWatch.stop();             log.info("JDBC save batch:" + stopWatch.getTotalTimeMillis());         } finally {             statement.close();             sqlSession.close();         }     }
  耗时是 55663 毫秒,所以 JDBC executeBatch 的性能跟 mybatis-plus 的 saveBatch 一样(底层一样)。
  综上所述,拼接 sql 的方式实现批量保存效率最佳。
  但是我又不太甘心,总感觉应该有什么别的法子,然后我就继续跟着 mybatis-plus 的源码 debug 了一下,跟到了 mysql 的驱动,突然发现有个 if 里面的条件有点显眼:
  就是这个叫 rewriteBatchedStatements 的玩意,从名字来看是要重写批操作的 Statement,前面batchHasPlainStatements 已经是 false,取反肯定是 true,所以只要这参数是 true 就会进行一波操作。
  我看了下默认是 false。
  同时我也上网查了下 rewriteBatchedStatements 参数,好家伙,好像有用!
  我直接将 jdbcurl 加上了这个参数:
  然后继续跑了下 mybatis-plus 自带的 saveBatch,果然性能大大提高,跟拼接 SQL 差不多!
  顺带我也跑了下 JDBC 的 executeBatch ,果然也提高了。
  然后我继续 debug ,来探探 rewriteBatchedStatements 究竟是怎么 rewrite 的!
  如果这个参数是 true,则会执行下面的方法且直接返回:
  看下 executeBatchedInserts 究竟干了什么:
  看到上面我圈出来的代码没,好像已经有点感觉了,继续往下 debug。
  果然! sql 语句被 rewrite了:
  对插入而言,所谓的 rewrite 其实就是将一批插入拼接成 insert into xxx values (a),(b),(c)...这样一条语句的形式然后执行,这样一来跟拼接 sql 的效果是一样的。
  那为什么默认不给这个参数设置为 true 呢?
  我简单问了下 ChatGPT:如果批量语句中的某些语句失败,则默认重写会导致所有语句都失败。批量语句的某些语句参数不一样,则默认重写会使得查询缓存未命中。
  看起来影响不大,所以我给我的项目设置上了这个参数!最后
  稍微总结下我粗略的对比(虽然粗略,但实验结果符合原理层面的理解),如果你想更准确地实验,可以使用JMH,并且测试更多组数(如 5000,10000等)的情况。
  批量保存方式
  数据量(条)
  耗时(ms)
  单条循环插入
  1000
  121011
  mybatis-plus saveBatch   1000
  59927
  mybatis-plus saveBatch(添加rewtire参数)   1000
  2589
  手动拼接sql   1000
  2275
  jdbc executeBatch   1000
  55663
  jdbc executeBatch(添加rewtire参数)   1000
  324
  所以如果有使用 jdbc 的 Batch 性能方面的需求,要将 rewriteBatchedStatements 设置为 true,这样能提高很多性能。   然后如果喜欢手动拼接 sql 要注意一次拼接的数量,分批处理。   链接:https://juejin.cn/post/7217836890120306746

预算1。2W左右,现在能装什么配置的电脑?13600KF4070Ti安排上通常来讲,玩家在准备组装电脑前都会给自己设置一个预算,并一再告诫自己要性价比优先。但是到了实际选择配件时,不管你是专业还是非专业的,因为各种因素的影响,大部分玩家还是会不小心超支。丝路驼队载游人沙漠旅游促发展春风和畅,暖意渐浓。入春以来,新疆博湖县进入新一轮旅游旺季,各景区旅游和消费市场呈现一派生机勃勃的景象。2月18日,博湖县博斯腾湖南端的艾勒逊乌拉沙漠游客如织,热闹非凡。远远望去,得罪人?皮尔斯夺冠应该是进名人堂的门槛无冠不该进名人堂直播吧2月18日讯近日,NBA名宿皮尔斯在KGCertified节目中谈到了名人堂。皮尔斯表示夺冠让你觉得自己有资格进入(名人堂),如果没有夺冠我不确定自己是否能入选名人堂。我们在马刺旧将家暴内幕曝光未婚妻是前成人女星唐斯女友介绍两人认识马刺旧将福布斯因家暴被逮捕入狱,北京时间2月18日,外媒DailyMail独家报道了这一事件的内幕和细节。福布斯家暴的对象是他的未婚妻艾尔莎简,她曾是一位成人女星,而两人是通过唐斯0616,53分钟56分钟的蛋肠组合,斯瓦泰克真的无可抵挡吗?大家好,这里是小Gang说,继续陪你聊体育。记得以前看大魔王张怡宁比赛,总感觉很残忍,经常打出110的比分,为了对方面子,放水把球击在球台边上。感觉现在的斯瓦泰克也到了大魔王的级别39。2差值16。4!湖人这拉塞尔太狠,更利好的是对詹姆斯还有后手21分2篮板7助攻1抢断,这是拉塞尔对鹈鹕的数据。忍不住,夸几句。先来说结果当拉塞尔在场时湖人净效率32。5,全队第一下场后球队净效率10。1。说白了,当拉塞尔在场时可以帮助湖人每晚年悲惨的4位明星,有人靠捡垃圾为生,有人连安葬费都付不起很多人羡慕明星们光鲜亮丽的生活,但不为人们所知的事并不是所有的明星都能荣华富贵一辈子。这些明星曾经风光无限,但是晚年却过得十分凄惨,真是让人唏嘘不已。蓝洁瑛早年的蓝洁瑛凭借着和梁朝宋春丽一生做好一件事宋春丽近照光明图片人民需要这样的文艺家飒爽的短发,一身深蓝色T恤白色休闲裤,初见宋春丽,在北京初秋清亮的阳光中,她亲切地向记者招手。与生活中的简单朴素不同,宋春丽的光影生涯是一条长湖人又内讧!詹姆斯与浓眉哥或分道扬镳!湖人队在上一场比赛中终于赢得了胜利,但是,这场胜利的喜悦却被内部矛盾和不满所掩盖。名嘴Colin在参加节目时透露,湖人队内部存在着矛盾和内讧,詹姆斯和浓眉哥之间的不合已经升级,甚至更新换代腾位置,山东泰山队五名球员可以寻找下家了随着中超联赛一线队大名单报名政策的改变,山东泰山阵容厚度的优势有所减弱,很多位置人员过剩,必定会有球员离开,目前来看这五名球员离队概率很大。一郭田雨郭田雨可以租借给青岛海牛,让宿茂湖人主场Crypto。com球馆计划申办2025年全明星赛据NBA记者ArashMarkazi报道,NBA全明星赛可能于2025年重返洛杉矶。消息人士透露,Crypto。com球馆将正式申办2025年的NBA全明星赛。这座球馆目前由湖人和
舌尖红心火旺舌边红肝火旺舌中红胃火旺,送你3个中成药调理大家好,我是刘医生。我们常说心火旺舌尖红肝火旺舌边红胃火旺舌全红。确实,舌头上循行着我们身体的很多经脉,不同的脏腑的火在舌头上的表现是不一样的,今天刘医生就来教你如何通过舌象辨别,既怕冷又怕热,腰膝酸软阳事不兴,阴阳双虚怎么办?大家有没有既怕冷又怕热腰膝酸软身体疲乏潮热盗汗肢体发凉的情况?这类人往往还伴随着遗精功能障碍等问题,这该怎么办?从中医的角度看,这是阴阳两虚。所谓的阳虚生外寒,阴虚生内热怕冷的人是你真的会午睡吗?医生提醒午睡四不要,看你中了几招午睡是否经常陪伴着你?现如今午睡已经是很多人的习惯,人们已经把午睡当成是一种愉快的小假期。它可以使你得到一些放松和恢复活力。不仅仅是现在新时代高压学习的学生们,更有晚上熬夜拼命赚钱乌鸡白凤丸,对男同胞用竟然这么好!千万别再有误解大家好,我是和医生,最近有个粉丝问我啊,说他有一个朋友,自己在家偷偷补肾,原本要吃六味地黄丸,但是错吃了他老婆的乌鸡白凤丸,发现效果更好,这是咋子回事呢?一说到乌鸡白凤丸呢,很多人力不从心,补肾总是没效果,中医教你这样调理,效果更佳经常有患者后台留言,明明是肾虚的问题,但是吃了很多补肾的药,功能还是没有好转,不知道是怎么回事?其实出现这种情况,很大原因是忽略了脾胃的吸收。大家知道脾主运化,咱们外来的食物,营养肝气一通百病无踪千古疏肝第一方,上治头痛,中治胃炎,下治便秘大家好,我是刘医生。肝气一通,百病无踪,今天刘医生给大家推荐一个千古名方,柴胡舒肝丸,上可以治头痛,中可以治胃炎,下可以治便秘。为什么说肝气一通,百病无踪在中医上讲肝主疏泄,掌管全杨澜和老公明明只差1岁,同框却似隔代人,果然男人也经不起肥胖古典风是一种很有品位的风格,对比其他风格,这种风格足够有韵味,而且有着很高的辨识度,更适合大龄女生来尝试,但是大多数人,会觉得这种风格,只有身材好与气质好的人才能驾驭。实际上只要身水乳不是越贵越好,分享4款高性价比水乳,价格不贵却很好用现在水乳套装的护肤功效真的不鸡肋,在护肤品高速升级换代的当下,水乳的护肤功效也迎来了高速升级补水保湿已经不在话下,抗老,抗氧化,提亮等多种功效护肤也有着亮眼的表现。特别重要的是,这火爆全球的辣妹装,连美国男人都穿上了最近,美国老头Jeff靠着穿辣妹挂脖上衣,在Tiktok火了!视频里,他总是穿着热辣小吊带,对着镜头露出鬼马的笑容。拍起年轻人爱玩的变装视频,他也能完美卡点。Jeff还是个社牛,穿南京东南jur美容医院为求美者保驾护航南京东南美容医院设有整形外科美容外科美容皮肤科等科室,其中详细展开了多种诊疗项目。美容事故令人闻之变色,南京东南美容医院及旗下品牌南京jur整形对手术质量的保障刻不容缓。近年来,国时髦的流浪风,真的只是随便穿穿?既野又情的秘诀,拿捏住了。中秋小长假刚刚过,十一假期还会远吗?心心念念的国庆节假期计划做了一大堆,是不是就差衣橱还没跟上?这两年,复古势头不减,而在复古与未来感之间,在城市与旷野之