MyBatis查询巨慢,排查发现是N+1问题

一个列表查询接口,20条数据要3秒。

查了半天,发现是MyBatis的N+1问题。

改了一行配置,从3秒优化到50毫秒。


问题现象

接口:查询订单列表,每个订单要显示用户名

实体类

@Data public class Order { private Long id; private Long userId; private String orderNo; private BigDecimal amount; private User user; // 关联用户 }

Mapper

<resultMap id="orderResultMap" type="Order"> <id column="id" property="id"/> <result column="user_id" property="userId"/> <result column="order_no" property="orderNo"/> <result column="amount" property="amount"/> <association property="user" column="user_id" select="selectUserById"/> </resultMap> <select id="selectOrderList" resultMap="orderResultMap"> SELECT * FROM orders WHERE status = 1 LIMIT 20 </select> <select id="selectUserById" resultType="User"> SELECT * FROM users WHERE id = #{userId} </select>

问题:查询20条订单,居然执行了21条SQL!


什么是N+1问题

第1条SQL:查询订单列表(返回20条) 第2条SQL:查询第1个订单的用户 第3条SQL:查询第2个订单的用户 ... 第21条SQL:查询第20个订单的用户

1次查询订单 + N次查询用户 = N+1次查询

每条SQL都有网络开销,20条订单就要21次数据库交互,当然慢。


如何发现N+1问题

方法一:开启SQL日志

mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

看到一堆重复的SELECT * FROM users WHERE id = ?就是了。

方法二:用druid监控

spring: datasource: druid: stat-view-servlet: enabled: true

访问/druid看SQL执行次数。

方法三:Arthas监控

# 监控SQL执行 watch com.mysql.cj.jdbc.StatementImpl execute "{params,returnObj}" -x 2

解决方案

方案一:改用JOIN查询(推荐)

一条SQL搞定:

<resultMap id="orderResultMap" type="Order"> <id column="id" property="id"/> <result column="user_id" property="userId"/> <result column="order_no" property="orderNo"/> <result column="amount" property="amount"/> <association property="user" javaType="User"> <id column="user_id" property="id"/> <result column="user_name" property="name"/> </association> </resultMap> <select id="selectOrderList" resultMap="orderResultMap"> SELECT o.id, o.user_id, o.order_no, o.amount, u.name as user_name FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE o.status = 1 LIMIT 20 </select>

效果:1条SQL,50ms搞定。

方案二:开启懒加载 + 批量查询

如果不想改SQL,可以开启懒加载和批量查询:

mybatis: configuration: lazy-loading-enabled: true aggressive-lazy-loading: false default-executor-type: batch

但这个方案不如JOIN彻底。

方案三:手动批量查询

public List<Order> getOrderList() { // 1. 查询订单 List<Order> orders = orderMapper.selectOrderList(); // 2. 收集userId Set<Long> userIds = orders.stream() .map(Order::getUserId) .collect(Collectors.toSet()); // 3. 批量查询用户 List<User> users = userMapper.selectByIds(userIds); Map<Long, User> userMap = users.stream() .collect(Collectors.toMap(User::getId, u -> u)); // 4. 组装数据 orders.forEach(order -> { order.setUser(userMap.get(order.getUserId())); }); return orders; }

SQL

<select id="selectByIds" resultType="User"> SELECT * FROM users WHERE id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </select>

效果:2条SQL,比N+1好很多。


不同方案对比

方案SQL数量复杂度适用场景
JOIN查询1简单关联
批量查询2复杂关联
懒加载N+1很少访问关联数据

collection也会有N+1

<!-- 查询用户及其订单列表 --> <resultMap id="userResultMap" type="User"> <id column="id" property="id"/> <collection property="orders" column="id" select="selectOrdersByUserId"/> </resultMap>

同样的问题:查10个用户,会执行11条SQL。

解决

<resultMap id="userResultMap" type="User"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="orders" ofType="Order"> <id column="order_id" property="id"/> <result column="order_no" property="orderNo"/> </collection> </resultMap> <select id="selectUserWithOrders" resultMap="userResultMap"> SELECT u.id, u.name, o.id as order_id, o.order_no FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.status = 1 </select>

MyBatis-Plus方案

如果用MyBatis-Plus,可以用@TableField注解:

@Data @TableName("orders") public class Order { private Long id; private Long userId; @TableField(exist = false) // 非数据库字段 private User user; }

然后在Service层手动组装:

public List<Order> getOrderList() { List<Order> orders = orderMapper.selectList(wrapper); // 批量查询用户 Set<Long> userIds = orders.stream() .map(Order::getUserId) .collect(Collectors.toSet()); Map<Long, User> userMap = userService.listByIds(userIds) .stream() .collect(Collectors.toMap(User::getId, u -> u)); orders.forEach(o -> o.setUser(userMap.get(o.getUserId()))); return orders; }

性能对比

测试数据:100条订单

方案SQL数量耗时
N+1(原始)1013200ms
JOIN查询145ms
批量查询260ms

提升:70倍!


远程排查经验

有次生产环境接口响应变慢,我在外面用星空组网连到公司内网,打开druid监控一看,一个接口执行了500多条SQL。

典型的N+1问题,改成JOIN查询立马解决。

远程能直接看监控、看日志,排查效率高很多。


总结

场景推荐方案
简单一对一关联JOIN查询
复杂多表关联批量查询
一对多关联JOIN或批量
很少用关联数据懒加载

避免N+1的原则

  1. 不要在resultMap里用select属性
  2. 关联查询优先用JOIN
  3. 必须分开查就用批量查询
  4. 开启SQL日志及时发现问题

一句话:看到association/collection里有select属性,基本就是N+1。

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

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

相关文章

StructBERT部署案例:用户反馈情绪分析教程

StructBERT部署案例&#xff1a;用户反馈情绪分析教程 1. 引言&#xff1a;中文情感分析的现实价值 在当今数字化服务快速发展的背景下&#xff0c;企业每天都会收到来自社交媒体、客服系统、电商平台等渠道的海量用户反馈。如何高效地理解这些文本背后的情绪倾向&#xff0c…

AI辅助代码审计:5分钟找到漏洞的云端工作站

AI辅助代码审计&#xff1a;5分钟找到漏洞的云端工作站 引言&#xff1a;为什么开发团队需要AI代码审计&#xff1f; 每次软件发版前&#xff0c;开发团队最头疼的就是代码审计。传统人工审计就像用放大镜逐行检查——效率低、容易漏判&#xff0c;还特别依赖工程师经验。我曾…

1小时1块玩AI:2024最新云端GPU服务横评

1小时1块玩AI&#xff1a;2024最新云端GPU服务横评 引言&#xff1a;为什么你需要云端GPU服务&#xff1f; 作为一名技术博主&#xff0c;我深知测评各类AI工具和平台的痛点。自购测试设备不仅成本高昂&#xff08;一块RTX 4090显卡就要上万元&#xff09;&#xff0c;而且随…

情感分析系统日志分析:StructBERT运维实战

情感分析系统日志分析&#xff1a;StructBERT运维实战 1. 引言&#xff1a;中文情感分析的现实需求与挑战 在当今数字化服务快速发展的背景下&#xff0c;用户反馈、评论、客服对话等非结构化文本数据呈爆炸式增长。如何从海量中文文本中自动识别情绪倾向&#xff0c;成为企业…

智能监控系统DIY教程:200元预算玩转AI异常识别

智能监控系统DIY教程&#xff1a;200元预算玩转AI异常识别 1. 为什么选择云端AI监控方案&#xff1f; 农场主老王最近很头疼&#xff1a;仓库总有人偷饲料&#xff0c;装了几个普通摄像头只能事后查录像&#xff0c;根本防不住。专业安防系统动辄上万元&#xff0c;而树莓派跑…

中文情感分析部署:StructBERT+Flask方案

中文情感分析部署&#xff1a;StructBERTFlask方案 1. 背景与应用场景 在当前自然语言处理&#xff08;NLP&#xff09;的实际落地中&#xff0c;中文情感分析已成为企业洞察用户反馈、监控舆情、优化客服系统的重要技术手段。无论是电商平台的商品评论、社交媒体的用户发言&…

AI算力自由攻略:按需付费+镜像市场,个人也能玩转大模型

AI算力自由攻略&#xff1a;按需付费镜像市场&#xff0c;个人也能玩转大模型 1. 为什么你需要算力自由&#xff1f; 作为独立开发者&#xff0c;你是否经常遇到这些困境&#xff1a; 想跑个Stable Diffusion生成设计图&#xff0c;但自己的显卡显存不足尝试微调LLaMA模型时…

情感分析API

情感分析API&#xff1a;基于StructBERT的中文情感识别系统&#xff08;WebUI API&#xff09; 1. 背景与需求&#xff1a;为什么需要中文情感分析&#xff1f; 在社交媒体、电商评论、用户反馈等场景中&#xff0c;海量的中文文本数据蕴含着丰富的情绪信息。如何自动识别这…

实体行为分析5大模型横评:云端2小时完成,成本不到10块

实体行为分析5大模型横评&#xff1a;云端2小时完成&#xff0c;成本不到10块 引言&#xff1a;为什么企业需要标准化行为分析测试&#xff1f; 作为企业架构师&#xff0c;你是否遇到过这样的困扰&#xff1a;想对比不同AI安全模型的效果&#xff0c;却发现本地测试环境差异…

计算机网络期末复习——第3章:运输层 Part Two

目录3.4可靠数据传输(RDT)原理3.4.1 构造可靠数据传输协议3.4.2 流水线可靠数据传输协议3.4.3 GBN3.4.4 SR3.5面向连接的运输&#xff1a;TCP3.5.1 TCP连接3.5.2 TCP报文段结构3.5.3 往返时间的估计与超时3.5.4 可靠数据传输3.5.5 流量控制3.5.6 TCP连接管理3.6 拥塞控制方法3.…

Linux中《socket编程》

目录认识源IP地址和目的IP地址认识端口号端口号(port)是传输层协议的内容.端口号范围划分理解 "端口号" 和 "进程 ID"理解源端口号和目的端口号理解 socket传输层的典型代表认识UDP协议认识TCP协议网络字节序字节序网络数据流socket编程接口常见APIsockadd…

跨平台AI视觉开发:一套代码云端部署,支持Windows/Linux

跨平台AI视觉开发&#xff1a;一套代码云端部署&#xff0c;支持Windows/Linux 引言 在AI视觉开发领域&#xff0c;最让人头疼的问题莫过于开发环境和部署环境的不一致。想象一下&#xff1a;你在Windows电脑上开发的AI模型&#xff0c;到了客户的Linux服务器上就跑不起来&am…

StructBERT WebUI开发教程:打造交互式情感分析平台

StructBERT WebUI开发教程&#xff1a;打造交互式情感分析平台 1. 引言 1.1 中文情感分析的现实需求 在当今信息爆炸的时代&#xff0c;用户每天在社交媒体、电商平台、评论区等场景中产生海量中文文本。如何从这些非结构化语言中快速提取情绪倾向&#xff0c;成为企业洞察用…

怕浪费钱?大模型按需付费指南:1小时1块,用完即停

怕浪费钱&#xff1f;大模型按需付费指南&#xff1a;1小时1块&#xff0c;用完即停 1. 为什么创业者需要按需付费的大模型服务 作为创业者&#xff0c;当你考虑用AI优化客服系统时&#xff0c;最头疼的往往是前期投入成本。传统云服务通常要求包月付费&#xff0c;动辄几千元…

中文情感分析WebUI开发:StructBERT轻量级性能优化

中文情感分析WebUI开发&#xff1a;StructBERT轻量级性能优化 1. 背景与需求&#xff1a;中文情感分析的现实价值 在社交媒体、电商评论、客服对话等场景中&#xff0c;用户生成内容&#xff08;UGC&#xff09;呈爆炸式增长。如何从海量中文文本中自动识别情绪倾向&#xff…

StructBERT实战教程:客服对话情感分析系统搭建

StructBERT实战教程&#xff1a;客服对话情感分析系统搭建 1. 引言 1.1 中文情感分析的现实需求 在当前以用户体验为核心的服务体系中&#xff0c;中文情感分析已成为智能客服、舆情监控、产品反馈挖掘等场景的关键技术。尤其是在电商、金融、电信等行业&#xff0c;每天产生…

马斯克与黄仁勋分道扬镳?200 亿押注 xAI 大模型

Meta 以数十亿美元收购中国团队创立的 AI 智能体公司 Manus&#xff0c;这一史上第三大收购案刚官宣便陷入全球监管漩涡&#xff0c;中美欧多国已将其纳入审查清单&#xff0c;核心围绕技术出口、数据出境、反垄断等关键议题&#xff0c;成为 AI 产业跨境并购合规化的标志性事件…

中文情感分析模型比较:StructBERT vs BERT

中文情感分析模型比较&#xff1a;StructBERT vs BERT 1. 引言&#xff1a;中文情感分析的技术背景与挑战 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;情感分析&#xff08;Sentiment Analysis&#xff09;是理解用户情绪、挖掘舆情信息的核心任务之一。尤其…

深度测评9个AI论文平台,专科生搞定毕业论文必备!

深度测评9个AI论文平台&#xff0c;专科生搞定毕业论文必备&#xff01; AI 工具如何助力专科生高效完成毕业论文 在当前的学术环境中&#xff0c;越来越多的学生开始借助 AI 工具来提升论文写作效率。对于专科生而言&#xff0c;撰写一篇结构严谨、内容充实的毕业论文不仅是对…

中文文本情感分析:StructBERT模型应用案例

中文文本情感分析&#xff1a;StructBERT模型应用案例 1. 引言&#xff1a;中文情感分析的现实需求与挑战 随着社交媒体、电商平台和用户评论系统的普及&#xff0c;中文文本数据呈爆炸式增长。如何从海量非结构化文本中提取有价值的情绪信息&#xff0c;成为企业洞察用户反馈…