MyBatis Plus SQL性能分析:从日志到优化的全流程实战指南

引言

在Java开发的江湖里,MyBatis Plus(MP)早已是“效率利器”——它用极简的API封装了CRUD操作,让开发者从重复的SQL编写中解放出来。但随着项目数据量从“万级”跃升至“十万级”“百万级”,一个尴尬的现实逐渐浮现:曾经跑得飞快的MP应用,开始频繁出现接口超时、数据库压力骤增的情况。问题根源往往不在MP本身,而在那些“隐形的SQL”——它们可能因索引缺失、查询冗余或分页逻辑欠妥,在数据洪流中沦为性能瓶颈。今天,我们就来聊聊如何用MP的特性,精准定位并解决这些SQL性能问题。


一、定位性能问题:先学会“看日志”和“读执行计划”

很多同学遇到慢查询第一反应是“是不是MP的问题?”——其实MP本身只是ORM框架,SQL性能的核心还是在数据库本身的执行效率。所以第一步,得先搞清楚“MP到底生成了什么SQL?执行了多久?”

1. 开启MP的SQL日志:让“隐形SQL”现形

MP的日志配置非常简单,但90%的新手可能没配置对。在application.yml里加上这几行,开发环境直接能看到完整的SQL执行过程:

mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL(开发环境必备)global-config:db-config:logic-delete-field: logic_delete_field # 逻辑删除字段(自动过滤已删数据,避免全表扫描)

输出示例
控制台会打印类似这样的信息:

==>  Preparing: SELECT id,name,age FROM user WHERE age > ? AND logic_delete_field = 0  
==> Parameters: 18(Integer)  
<==      Total: 10  

重点关注Preparing里的SQL语句和Parameters里的参数,这是后续分析的基础。

2. 数据库层面:用EXPLAIN“解剖”SQL

光看MP日志还不够,必须结合数据库的执行计划。以MySQL为例,拿到MP生成的SQL后,在Navicat或命令行执行EXPLAIN + SQL语句,重点关注4个指标:

指标含义优化目标
type访问类型(性能从好到差:system > const > eq_ref > ref > range > index > ALL)至少达到range,避免ALL(全表扫描)
key实际使用的索引不为NULL(未命中索引)
rowsMySQL估计要扫描的行数数值越小越好
Extra额外信息(如Using filesort文件排序、Using temporary临时表)避免这两种情况

举个栗子
假设MP生成了SELECT * FROM user WHERE username LIKE '%张三%',执行EXPLAIN后发现type=ALLkey=NULL,这说明:

  • 没有给username字段加索引(左模糊LIKE '%xxx'无法用普通索引);
  • 全表扫描导致性能极差(数据量10万时,扫描时间可能飙升到秒级)。

3. 慢查询日志:抓出“隐形杀手”

除了实时日志,一定要开启数据库的慢查询日志,记录执行时间超过阈值的SQL。以MySQL为例,在my.cnf里配置:

slow_query_log = 1                  # 开启慢查询
slow_query_log_file = /var/log/mysql/slow.log  # 日志路径
long_query_time = 1                 # 超过1秒的SQL记录
log_queries_not_using_indexes = 1   # 记录未使用索引的SQL(关键!)

重启MySQL后,所有“慢SQL”都会被记录下来,结合MP日志的时间戳,就能精准定位到具体是哪个业务接口触发了问题SQL。


二、MP常见的5大SQL性能“坑”

通过日志和执行计划分析后,你会发现MP的性能问题大多源于使用不当,而不是框架本身的bug。以下是最常见的5类问题,看看你中过几个?

1. 全表扫描:索引白加了?

典型场景

  • 对高频查询字段(如agecreate_time)未加索引;
  • LIKE '%关键词%'做模糊查询(左模糊无法用普通索引);
  • 逻辑删除未生效(未配置@TableLogic,导致查询时仍然扫描已删除数据)。

优化建议

  • 对高频查询、排序、分组的字段(如create_timestatus)添加索引;
  • 左模糊查询(LIKE '%xxx')尽量改用全文索引(MySQL 5.6+支持FULLTEXT)或搜索引擎(如Elasticsearch);
  • 配置@TableLogic标记逻辑删除字段,MP会自动过滤已删数据,避免全表扫描。

2. N+1查询:循环调用数据库,能不慢吗?

典型场景
查询主表(如用户表)后,循环调用接口查询关联表(如订单表):

// 错误示范:1次查用户,N次查订单(总查询次数=1+N)
List<User> users = userMapper.selectList(wrapper);
users.forEach(user -> {List<Order> orders = orderMapper.selectByUserId(user.getId()); // 循环调用
});

问题
数据量1000时,总查询次数是1001次!数据库连接池被占满,响应时间飙升。

优化方案

  • 批量查询:先查所有用户ID,再用selectBatchIds一次性查订单:
    List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
    List<Order> orders = orderMapper.selectBatchIds(userIds); // 1次查询搞定
    
  • 懒加载:用@TableField(select = false)标记不需要立即查询的关联字段,需要时手动调用;
  • MyBatis二级缓存:对高频读、低频写的数据(如字典表)启用缓存(需注意缓存一致性)。

3. 批量操作:旧版本MP的“隐形杀手”

典型场景
saveBatch插入1万条数据,发现耗时5秒+。查日志发现MP生成了1万条INSERT语句!

原因
MP 3.5.0之前,默认的批量插入实现是通过foreach拼接多条INSERT语句(非数据库原生的批量插入),网络IO和事务提交次数暴增。

优化方案

  • 升级MP到3.5.0+:默认支持INSERT INTO user (name,age) VALUES (a),(b),(c)的原生批量插入;
  • 手动分批处理:数据量极大时(如10万条),按每1000条分批插入:
    List<List<User>> batches = ListUtils.partition(userList, 1000); // 每批1000条
    batches.forEach(batch -> userMapper.saveBatch(batch));
    

4. 分页查询:LIMIT 100000,20 等于“自杀”

典型场景
做后台管理系统时,用户翻到第100页,SQL是SELECT * FROM user LIMIT 100000,20。数据库需要扫描前100020行,耗时随数据量线性增长。

问题
LIMIT offset, size的本质是“先扫描offset+size行,再丢弃前offset行”,数据量大时效率极低。

优化方案

  • 小数据量分页:直接用MP的Page对象,底层会自动处理;
  • 大数据量分页:改用WHERE id > lastId LIMIT size(需主键有序):
    // 假设上一页最后一条记录的ID是lastId
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("id", lastId).last("LIMIT 20"); // 直接拼接LIMIT语句
    IPage<User> page = userMapper.selectPage(new Page<>(current, size), wrapper);
    

5. 冗余查询:SELECT * 害人不浅

典型场景
userMapper.selectById(1L)查询用户,但表里有avatar(大图片)、remark(长文本)等字段,结果返回了几十KB的数据,内存和网络都被占满。

问题
SELECT *会查询所有字段,包括大字段和不必要的字段,增加网络传输和内存消耗。

优化方案

  • 指定查询字段:用QueryWrapper.select()明确需要的字段:
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.select("id", "name", "age"); // 只查这三个字段
    User user = userMapper.selectOne(wrapper);
    
  • DTO投影:定义只包含需要字段的DTO类,避免实体类膨胀:
    @Data
    public class UserDTO {private Long id;private String name;private Integer age;
    }
    // 查询结果直接映射到DTO
    List<UserDTO> dtos = userMapper.selectList(wrapper);
    

三、总结:MP性能优化的“三板斧”

通过上面的案例,我们可以总结出MP SQL性能优化的核心思路:

1. 日志是“眼睛”,EXPLAIN是“放大镜”

开启MP日志,结合EXPLAIN分析执行计划,90%的性能问题都能定位到索引或SQL写法上。

2. 索引不是万能的,但不加索引是万万不能

高频查询字段一定要加索引,左模糊、全表扫描这类“坑”能避则避。

3. 批量和分页要“讲究”

批量操作用3.5.0+的新特性,分页大数据量时用主键范围查询,避免LIMIT offset, size

最后想和大家说:MP是工具,SQL性能的核心还是在开发者对业务的理解和数据库的掌握。多动手分析日志、执行计划,多测试不同场景下的性能表现,才能让项目“快人一步”!

你在开发中遇到过哪些MP的SQL性能问题?欢迎在评论区分享你的踩坑经历和解决方案~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/89800.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

备忘录设计模式

备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为设计模式&#xff0c;用于捕获对象的内部状态并在需要时恢复该状态&#xff0c;同时不破坏对象的封装性。它适用于需要实现撤销/重做、历史记录或状态快照的场景。核心组件Originator&#xff08;原发器&#xff0…

【世纪龙科技】智能网联汽车环境感知系统教学难题的创新实践​

在职业院校智能网联汽车专业教学中&#xff0c;环境感知系统的教学长期面临三大核心挑战&#xff1a;设备成本高昂导致实训资源不足、抽象原理难以直观呈现、传统教学模式难以满足产业需求。如何让学生在有限的教学条件下&#xff0c;深入理解激光雷达、毫米波雷达等核心部件的…

ES vs Milvus vs PG vector :LLM时代的向量数据库选型指南

互联网时代&#xff0c;关系型数据库为王。相应的&#xff0c;我们的检索方式也是精确匹配查询为主——查找特定的用户ID、商品编号或订单状态。但AI时代&#xff0c;语义检索成为常态&#xff0c;向量数据库成为搜索推荐系统&#xff0c;大模型RAG落地&#xff0c;自动驾驶数据…

磁盘阵列技术的功能与分类

磁盘阵列技术 磁盘阵列是由多台磁盘存储器组成的一个快速、大容量、高可靠的外存子系统。现在常见的磁盘阵列称为廉价冗余磁盘阵列&#xff08;Redundant Array of Independent Disk,RAID)。目前&#xff0c;常见的 RAID 如下所示。 廉价冗余磁盘阵列 RAID级别 RAID-0是一种不具…

SpringMVC核心注解:@RequestMapping详解

概述RequestMapping是SpringMVC中最核心的注解之一&#xff0c;用于将HTTP请求映射到MVC和REST控制器的处理方法上。基本功能RequestMapping主要用于&#xff1a;映射URL到控制器类或方法定义请求方法类型&#xff08;GET、POST等&#xff09;定义请求参数、请求头等条件使用位…

【杂谈】硬件工程师怎么用好AI工具做失效分析

最近被派到国外出差了&#xff0c;工作任务比较重&#xff0c;所以更新的频率比较低。但在出差工作的过程中&#xff0c;我发现在失效分析时&#xff0c;有相当多的时间做的是比较重复的工作。比如失效分析肯定要一些证据如图片、视频。当我们做多台设备的失效分析时&#xff0…

MyBatis详解以及在IDEA中的开发

MyBatis概述 MyBatis是一个优秀的持久层框架&#xff0c;它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的过程。 核心特点 优势&#xff1a; SQL语句与Java代码分离&#xff0c;便于维护支持动态SQL&#xff0c;灵活性…

LangGraph教程6:LangGraph工作流人机交互

文章目录 Human-in-the-loop(人机交互) interrupt Warning Human-in-the-loop(人机交互) 人机交互(或称“在循环中”)工作流将人类输入整合到自动化过程中,在关键阶段允许决策、验证或修正。这在基于 LLM 的应用中尤其有用,因为基础模型可能会产生偶尔的不准确性。在合规、…

Linux部署Milvus数据库及Attu UI工具完全指南

一、准备工作1.1 环境要求操作系统&#xff1a;Ubuntu 20.04/Debian 11/CentOS 7硬件配置&#xff1a;至少8GB内存&#xff0c;4核CPU&#xff0c;50GB磁盘空间网络要求&#xff1a;可访问互联网&#xff08;用于拉取Docker镜像&#xff09;1.2 安装Docker和Docker Compose1.2.…

开疆智能Profinet转ModbusTCP网关连接康耐视InSight相机案例

相机配置&#xff1a;硬件连接部分可以查询我的博客&#xff1a;点击 这里不做说明。在电子表格视图下&#xff0c;点击菜单 “传感器–网络设置”&#xff1a;选择工业协议&#xff0c;如图。保存作业&#xff0c;并按照提示重启相机。3. 相机的控制/状态字&#xff1a;上图中…

BERT技术架构

### **一、整体定位&#xff1a;纯编码器架构**#### **核心设计思想**> **预训练微调**&#xff1a;> 1. **预训练**&#xff1a;在海量无标签文本上学习通用语言规律> 2. **微调**&#xff1a;用少量标注数据适配具体任务&#xff08;如分类/问答&#xff09;> **…

Python+ArcGIS+AI蒸散发与GPP估算|Penman-Monteith模型|FLUXNET数据处理|多源产品融合|专业科研绘图与可视化等

结合Python编程与ArcGIS工具&#xff0c;通过AI辅助方法实现蒸散发与植被总初级生产力估算。学习国际流行的Penman-Monteith模型&#xff0c;掌握数据获取、处理、分析和可视化全流程&#xff0c;培养生态水文与双碳领域的实践应用能力。通过DeepSeek、豆包等AI工具辅助代码编写…

elasticsearch+logstash+kibana+filebeat实现niginx日志收集(未过滤日志内容)

单点部署 环境准备 基于Rocky9虚拟机&#xff0c;内存大小为4G yum -y install lrzsz useradd elkf passwd elkf#密码随意su - elk rz 导入包&#xff0c;笔者导使用版本为7.17.8下载地址&#xff1a;https://www.elastic.co/downloads/past-releases/ tar -xf elasticsearch-7…

hadoop 集群问题处理

1.1.JournalNode 的作用在 HDFS HA 配置中&#xff0c;为了实现两个 NameNode 之间的状态同步和故障自动切换&#xff0c;Hadoop 使用了一组 JournalNode 来管理共享的编辑日志。具体来说&#xff0c;JournalNode 的主要职责包括&#xff1a;共享编辑日志&#xff1a;JournalNo…

LeetCode--46.全排列

解题思路&#xff1a;1.获取信息&#xff1a;给定一个不含重复数字的数组&#xff0c;返回所有可能的全排列&#xff0c;可以按任意顺序返回提示信息&#xff1a;1 < nums.length < 6-10 < nums[i] < 102.分析题目&#xff1a;要获取到所有可能的全排列我们每次会从…

云徙科技----一面(全栈开发)

一、公司是做什么业务的&#xff1f;二、介绍一下自己会用的&#xff0c;熟悉的技术栈&#xff1f;三、“在 Spring 应用中&#xff0c;当你发起一个 RESTful API 请求时&#xff08;例如 GET /api/users/1&#xff09;&#xff0c;计算机系统是如何知道这个请求的&#xff1f;…

我是怎么设计一个订单号生成策略的(库存系统)

我是怎么设计一个订单号生成策略的&#xff08;库存系统&#xff09;一、背景 最近我在做一套自研的库存管理系统&#xff0c;其中有一个看似简单、实则很关键的功能&#xff1a;订单号生成策略。 订单号不仅要全局唯一&#xff0c;还要有一定的可读性和业务含义&#xff0c;比…

问津集 #1:Rethinking The Compaction Policies in LSM-trees

文章目录引言正文结束语引言 陪女朋友出门&#xff0c;我大概有两个小时左右的空闲时间&#xff0c;遂带上电脑&#xff0c;翻了下论文列表&#xff0c;选择了这篇文章做一个简读。 因为这一年负责时序系统的存储引擎和计算引擎演进&#xff0c;而Compaction又是串联读写的核心…

数据产品结构:从数据接入到可视化的完整架构指南

在数据驱动决策的时代&#xff0c;一套高效的数据产品结构是企业挖掘数据价值的基础。无论是巨头企业自建的完整体系&#xff0c;还是中小企业依赖的第三方工具&#xff0c;其核心逻辑都是实现 “数据从产生到呈现” 的全链路管理。本文将拆解数据产品的五层架构&#xff0c;对…

python学智能算法(二十三)|SVM-几何距离

引言 前序学习文章中&#xff0c;已经探究了电荷超平面的距离计算方法&#xff0c;相关文章为点与超平面的距离。 在这片文章中&#xff0c;我们了解到计算距离的公式&#xff1a; Fmin⁡i1...myi(w⋅xib)F\min_{i1...m}y_{i}(w\cdot x_{i}b)Fi1...mmin​yi​(w⋅xi​b) 计算…