Mybatis Plus很好,但也有坑!

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

dd8d0efcfc7e7ebc8121a416ddc1eff0.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

来源:waynblog


最近在开发一个后台发送消息的功能时,由于需要给多个用户发送消息,于是使用了 mybatis plus 提供的 saveBatch() 方法,在测试环境测试通过上预发布后,测试反应发送消息接口很慢得等 5、6 秒,于是我就登录线上环境查看执行日志,发现是 mybatis plus 提供的 saveBatch() 方法执行很慢导致,于是也就有了本篇文章。

mybatis plus 是一个流行的 ORM 框架,它基于 mybatis,提供了很多便利的功能,比如代码生成器、通用 CRUD、分页插件、乐观锁插件等。它可以让我们更方便地操作数据库,减少重复的代码,提高开发效率。

案发现场还原

/**
 * 先保存通知消息,在批量保存用户通知记录
 */
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveNotice(Notify notify, String receiveUserIds) {
    long begin = System.currentTimeMillis();
    notify.setCreateTime(new Date());
    notify.setCreateBy(ShiroUtil.getSessionUid());
    if (notify.getPublishTime() == null) {
        notify.setPublishTime(new Date());
    }
    boolean insert = save(notify);
    List<NotifyRecord> collect = new ArrayList<>();
    List<String> receiveUserList = fillNotifyRecordList(notify, receiveUserIds, collect);
    notifyRecordService.saveBatch(collect);
    long end = System.currentTimeMillis();
    System.out.println(end - begin);
    ...
    return insert;
}

/**
 * 根据用户id,组装用户通知记录集合,返回200条记录
 */
public List<String> fillNotifyRecordList(Notify notify, String receiveUserIds, List<NotifyRecord> collect) {
    List<String> noticeRecordList = new ArrayList<>(200);
    ...
    // 组将两百条用户通知记录
    return noticeRecordList;
}

如上代码,我有一个 saveNotice() 方法用于保存通知消息以及用户通知记录。执行逻辑如下,

  1. 保存通知消息

  2. 根据用户 id,组装用户通知记录集合,返回 200 条用户通知记录

  3. 批量保存用户通知记录集合

前两步骤耗时都很少,我们直接看第三步操作耗时,结合 sql 执行日志,如下,

-- slow sql 5542 millis. INSERT INTO oa_notify_record  ( notifyId, receiveUserId, receiveUserName, isRead,  createTime )  VALUES  ( ?, ?, ?, ?,  ? )[225,"fcd90fe3990e505d07c90a238f75e9c1","niuwawa",false,"2023-10-30 23:54:04"]
5681

再结合 mybatis free log 插件打印完整 sql 如下图,

adb6047d3e314f045a73616e7dd6daf3.png

可以看出,我们批量保存用户通知记录是一条记录一条进行保存得,已经可以猜测就是批量插入方法导致得耗时较高。

这里使用得是 mybatis log free 插件,它可以自动帮我们在控制台打印完整得 mybatis sql 语句。有需要可以在 idea 插件中心搜索 mybatis log free 下载安装。

结合 saveBatch() 底层源码也能够看出,mybatis plus 对于批量操作是通过 for 循环执行保存操作得,源码如下图,

fb5dd78b8db4a9bf0edcd1fa8cc2e84e.png

到这里我们也就知道了在测试环境执行较快得原因,因为在测试环境需要批量保存得用户通知记录比较少,只有几条记录,所以很快。但是上预发布后,由于预发布中需要批量保存得用户通知记录比较多达到了数百条,所以执行较慢,耗时达到了 5、6 秒之久。

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

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

解决方法

到这里,也就是本文得重点所在了,那怎么解决这个问题嘞?如何既利用 mybatis plus 提供得便携性,也能够解决批量操作耗时较高得问题。

其实解决方法很简单,只需要在 jdbcurl 上添加 rewriteBatchedStatements=true 参数即可解决这个问题。

MySQL 的 JDBC 连接的 url 中要加 rewriteBatchedStatements 参数,并保证 5.1.13 以上版本的驱动,才能实现高性能的批量插入。

MySQL JDBC 驱动在默认情况下会无视 executeBatch()语句,把我们期望批量执行的一组 sql 语句拆散,一条一条地发给 MySQL 数据库,批量插入实际上是单条插入,直接造成较低的性能。只有把 rewriteBatchedStatements 参数置为 true, 驱动才会帮你批量执行 SQL。另外这个选项对 INSERT/UPDATE/DELETE 都有效。

rewriteBatchedStatements=true 的意思是,当你在 Java 程序中使用批量插入/修改/删除(batching)时,MySQL JDBC 驱动程序将尝试重新编写(rewrite)你的 SQL 语句,以便更有效地执行这些批量插入操作。

OK,在我们给 jdbcurl 上添加了参数后,看看效果,如下图,

0c2ac034c8df346053153e37f40aaf88.png

可以看到 jdbcurl 添加了 rewriteBatchedStatements=true 参数后,批量操作的执行耗时已经只有 200 毫秒,自此也就解决了 mybatis plus 提供的 saveBatch() 方法执行耗时较高得问题。

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

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

总结

mybatis plus 给开发人员带来了很多便利,但是其中也有一些坑点,比如上文所提到得批量操作耗时问题,如果不注意的话,就有可能调入坑里,各位开发同学可以检查自己或者公司项目中 jdbcurl 是否缺失 rewriteBatchedStatements=true 参数,加以改正,避免重复掉入这个坑里。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

4e4ea8713f0575cc877ac16c5f79da2e.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

ba8d69f456fa518fc82c2771114fae32.png

db137100be1b03270be08c618f7c5aac.png80b1450dc77463704979a4ccef5d587b.png817adfbfbb7704608acf11099226be0c.png76812a0b26cccd98601875cb728a0398.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值