0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何调优MyBatis 25倍性能

jf_ro2CN3Fa 来源:芋道源码 2023-05-30 09:56 次阅读
来源:yes的练级攻略
  • 粗略的实验
  • 最后


最近在压测一批接口,发现接口处理速度慢的有点超出预期,感觉很奇怪,后面定位发现是数据库批量保存这块很慢。

这个项目用的是 mybatis-plus,批量保存直接用的是 mybatis-plus 提供的 saveBatch。

我点进去看了下源码,感觉有点不太对劲:

2ad185ce-fe8a-11ed-90ce-dac502259ad0.png

我继续追踪了下,从这个代码来看,确实是 for 循环一条一条执行了 sqlSession.insert,下面的 consumer 执行的就是上面的 sqlSession.insert:

2adaae10-fe8a-11ed-90ce-dac502259ad0.png

然后累计一定数量后,一批 flush。

从这点来看,这个 saveBach 的性能肯定比直接一条一条 insert 快。

我直接进行一个粗略的实验,简单创建了一张表来对比一波!

粗略的实验

1000条数据,一条一条插入

@Test
voidMybatisPlusSaveOne(){
SqlSessionsqlSession=sqlSessionFactory.openSession();
try{
StopWatchstopWatch=newStopWatch();
stopWatch.start("mybatisplussaveone");
for(inti=0;i< 1000;i++){
OpenTestopenTest=newOpenTest();
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();
}
}
2ae2c12c-fe8a-11ed-90ce-dac502259ad0.png

可以看到,执行一批 1000 条数的批量保存,耗费的时间是 121011 毫秒。

1000条数据用 mybatis-plus 自带的 saveBatch 插入

@Test
voidMybatisPlusSaveBatch(){
SqlSessionsqlSession=sqlSessionFactory.openSession();
try{
ListopenTestList=newArrayList<>();
for(inti=0;i< 1000;i++){
OpenTestopenTest=newOpenTest();
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);
}
StopWatchstopWatch=newStopWatch();
stopWatch.start("mybatisplussavebatch");
//批量插入
openTestService.saveBatch(openTestList);
sqlSession.commit();
stopWatch.stop();
log.info("mybatis plus save batch:"+stopWatch.getTotalTimeMillis());
}finally{
sqlSession.close();
}
}
2ae81f1e-fe8a-11ed-90ce-dac502259ad0.png

耗费的时间是 59927 毫秒,比一条一条插入快了一倍,从这点来看,效率还是可以的。

然后常见的还有一种利用拼接 sql 方式来实现批量插入,我们也来对比试试看性能如何。

1000条数据用手动拼接 sql 方式插入

搞个手动拼接:

2aef8592-fe8a-11ed-90ce-dac502259ad0.png来跑跑下性能如何:

@Test
voidMapperSaveBatch(){
SqlSessionsqlSession=sqlSessionFactory.openSession();
try{
ListopenTestList=newArrayList<>();
for(inti=0;i< 1000;i++){
OpenTestopenTest=newOpenTest();
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);
}
StopWatchstopWatch=newStopWatch();
stopWatch.start("mappersavebatch");
//手动拼接批量插入
openTestMapper.saveBatch(openTestList);
sqlSession.commit();
stopWatch.stop();
log.info("mapper save batch:"+stopWatch.getTotalTimeMillis());
}finally{
sqlSession.close();
}
}
2af6df90-fe8a-11ed-90ce-dac502259ad0.png

耗时只有 2275 毫秒,性能比 mybatis-plus 自带的 saveBatch 好了 26 倍!

这时,我又突然回想起以前直接用 JDBC 批量保存的接口,那都到这份上了,顺带也跑跑看!

1000条数据用 JDBC executeBatch 插入

@Test
voidJDBCSaveBatch()throwsSQLException{
SqlSessionsqlSession=sqlSessionFactory.openSession();
Connectionconnection=sqlSession.getConnection();
connection.setAutoCommit(false);

Stringsql="insertintoopen_test(a,b,c,d,e,f,g,h,i,j,k)values(?,?,?,?,?,?,?,?,?,?,?)";
PreparedStatementstatement=connection.prepareStatement(sql);
try{
for(inti=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();
}
StopWatchstopWatch=newStopWatch();
stopWatch.start("JDBCsavebatch");
statement.executeBatch();
connection.commit();
stopWatch.stop();
log.info("JDBC save batch:"+stopWatch.getTotalTimeMillis());
}finally{
statement.close();
sqlSession.close();
}
}
2afea46e-fe8a-11ed-90ce-dac502259ad0.png

耗时是 55663 毫秒,所以 JDBC executeBatch 的性能跟 mybatis-plus 的 saveBatch 一样(底层一样)。

综上所述,拼接 sql 的方式实现批量保存效率最佳。

但是我又不太甘心,总感觉应该有什么别的法子,然后我就继续跟着 mybatis-plus 的源码 debug 了一下,跟到了 mysql 的驱动,突然发现有个 if 里面的条件有点显眼:

2b05fd7c-fe8a-11ed-90ce-dac502259ad0.png

就是这个叫 rewriteBatchedStatements 的玩意,从名字来看是要重写批操作的 Statement,前面batchHasPlainStatements 已经是 false,取反肯定是 true,所以只要这参数是 true 就会进行一波操作。

我看了下默认是 false。

2b0cc0b2-fe8a-11ed-90ce-dac502259ad0.png

同时我也上网查了下 rewriteBatchedStatements 参数,好家伙,好像有用!我直接将 jdbcurl 加上了这个参数:

2b13cef2-fe8a-11ed-90ce-dac502259ad0.png

然后继续跑了下 mybatis-plus 自带的 saveBatch,果然性能大大提高,跟拼接 SQL 差不多!

2b1b2bac-fe8a-11ed-90ce-dac502259ad0.png

顺带我也跑了下 JDBC 的 executeBatch ,果然也提高了。

2b21ec58-fe8a-11ed-90ce-dac502259ad0.png

然后我继续 debug ,来探探 rewriteBatchedStatements 究竟是怎么 rewrite 的!

如果这个参数是 true,则会执行下面的方法且直接返回:

2b30f07c-fe8a-11ed-90ce-dac502259ad0.png

看下 executeBatchedInserts 究竟干了什么:

2b37bbfa-fe8a-11ed-90ce-dac502259ad0.png

看到上面我圈出来的代码没,好像已经有点感觉了,继续往下 debug。

果然!sql 语句被 rewrite了:

2b3e7e7c-fe8a-11ed-90ce-dac502259ad0.png

对插入而言,所谓的 rewrite 其实就是将一批插入拼接成 insert into xxx values (a),(b),(c)...这样一条语句的形式然后执行,这样一来跟拼接 sql 的效果是一样的。

那为什么默认不给这个参数设置为 true 呢?

原来是这样的:

  1. 如果批量语句中的某些语句失败,则默认重写会导致所有语句都失败。
  2. 批量语句的某些语句参数不一样,则默认重写会使得查询缓存未命中。

看起来影响不大,所以我给我的项目设置上了这个参数!

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

最后

稍微总结下我粗略的对比(虽然粗略,但实验结果符合原理层面的理解),如果你想更准确地实验,可以使用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 要注意一次拼接的数量,分批处理。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源码
    +关注

    关注

    8

    文章

    639

    浏览量

    29185
  • 接口处理
    +关注

    关注

    0

    文章

    3

    浏览量

    6411
  • mybatis
    +关注

    关注

    0

    文章

    60

    浏览量

    6709

原文标题:调优 MyBatis 25 倍性能

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    MCF8316A指南

    电子发烧友网站提供《MCF8316A指南.pdf》资料免费下载
    发表于 11-20 17:21 0次下载
    MCF8316A<b class='flag-5'>调</b><b class='flag-5'>优</b>指南

    MCT8316A指南

    电子发烧友网站提供《MCT8316A指南.pdf》资料免费下载
    发表于 11-13 13:49 0次下载
    MCT8316A<b class='flag-5'>调</b><b class='flag-5'>优</b>指南

    MCT8315A指南

    电子发烧友网站提供《MCT8315A指南.pdf》资料免费下载
    发表于 11-12 14:14 0次下载
    MCT8315A<b class='flag-5'>调</b><b class='flag-5'>优</b>指南

    MMC DLL

    电子发烧友网站提供《MMC DLL.pdf》资料免费下载
    发表于 10-11 11:48 0次下载
    MMC DLL<b class='flag-5'>调</b><b class='flag-5'>优</b>

    TDA3xx ISS和调试基础设施

    电子发烧友网站提供《TDA3xx ISS和调试基础设施.pdf》资料免费下载
    发表于 10-11 10:16 0次下载
    TDA3xx ISS<b class='flag-5'>调</b><b class='flag-5'>优</b>和调试基础设施

    大数据从业者必知必会的Hive SQL技巧

    大数据从业者必知必会的Hive SQL技巧 摘要 :在大数据领域中,Hive SQL被广泛应用于数据仓库的数据查询和分析。然而,由于数据量庞大和复杂的查询需求,Hive SQL查询的性能往往
    的头像 发表于 09-24 13:30 238次阅读

    智能,使步进电机安静而高效地运行

    电子发烧友网站提供《智能,使步进电机安静而高效地运行.pdf》资料免费下载
    发表于 09-24 11:08 1次下载
    智能<b class='flag-5'>调</b><b class='flag-5'>优</b>,使步进电机安静而高效地运行

    MMC SW算法

    电子发烧友网站提供《MMC SW算法.pdf》资料免费下载
    发表于 09-20 11:14 0次下载
    MMC SW<b class='flag-5'>调</b><b class='flag-5'>优</b>算法

    如何DS160PR410实现出色的信号完整性

    电子发烧友网站提供《如何DS160PR410实现出色的信号完整性.pdf》资料免费下载
    发表于 09-20 09:39 6次下载
    如何<b class='flag-5'>调</b><b class='flag-5'>优</b>DS160PR410实现出色的信号完整性

    TAS58xx系列通用指南

    电子发烧友网站提供《TAS58xx系列通用指南.pdf》资料免费下载
    发表于 09-14 10:49 0次下载
    TAS58xx系列通用<b class='flag-5'>调</b><b class='flag-5'>优</b>指南

    AM6xA ISP指南

    电子发烧友网站提供《AM6xA ISP指南.pdf》资料免费下载
    发表于 09-07 09:52 0次下载
    AM6xA ISP<b class='flag-5'>调</b><b class='flag-5'>优</b>指南

    OSPI控制器PHY算法

    电子发烧友网站提供《OSPI控制器PHY算法.pdf》资料免费下载
    发表于 08-30 11:12 0次下载
    OSPI控制器PHY<b class='flag-5'>调</b><b class='flag-5'>优</b>算法

    深度解析JVM实践应用

    Tomcat自身的是针对conf/server.xml中的几个参数的设置。首先是对这几个参数的含义要有深刻而清楚的理解。
    的头像 发表于 04-01 10:24 457次阅读
    深度解析JVM<b class='flag-5'>调</b><b class='flag-5'>优</b>实践应用

    鸿蒙开发实战:【性能组件】

    性能组件包含系统和应用框架,旨在为开发者提供一套性能
    的头像 发表于 03-13 15:12 435次阅读
    鸿蒙开发实战:【<b class='flag-5'>性能</b><b class='flag-5'>调</b><b class='flag-5'>优</b>组件】

    优于10参数模型!微软发布Orca 2 LLM

    微软发布 Orca 2 LLM,这是 Llama 2 的一个版本,性能与包含 10 参数的模型相当,甚至更好。
    的头像 发表于 12-26 14:23 627次阅读