架构师技能9-深入mybatis:Creating a new SqlSession到查询语句耗时特别长
开篇语录:以架构师的能力标准去分析每个问题,过后由表及里分析问题的本质,复盘总结经验,并把总结内容记录下来。当你解决各种各样的问题,也就积累了丰富的解决问题的经验,解决问题的能力也将自然得到极大的提升。
励志做架构师的撸码人,认知很重要,可以订阅:架构设计专栏
撸码人平时大多数时间都在撸码或者撸码的路上,很少关注框架的一些底层原理,当出现问题时没能力第一时间解决问题,出现问题后不去层层剖析问题产生的原因,后续也就可能无法避免或者绕开同类的问题。因此不要单纯做Ctrl+c和Ctrl+V,而是一边仰望星空(目标规划),一边脚踏实地去分析每个问题。
一、背景
我们最近在使用mybatis执行批量数据插入,数据插入非常慢:每批次5000条数据大概耗时在3~4分钟左右。架构师的职责之一是疑难技术点攻关:要主动积极解决系统出现的问题,过后由表及里分析问题的本质,复盘总结经验,并把总结内容记录下来分享给团队,确保后续如何智慧地绕开同类问题。
以下是排查问题的过程和思路:
二、定位问题
1、全面打印日志:
日志是排查问题的第一手资料。
logback设置mybatis相关日志打印:
<logger name="org.mybatis.spring" level="DEBUG"/> <logger name="org.apache.ibatis" level="DEBUG"/> <!-- <logger name="java.sql.PreparedStatement" level="DEBUG"/>--> <!-- <logger name="java.sql.Statement" level="DEBUG"/>--> <!-- <logger name="java.sql.Connection" level="DEBUG"/>--> <!-- <logger name="java.sql.ResultSet" level="DEBUG"/>-->
2、初步定位问题
发现Creating a new SqlSession到查询语句耗时特别长:
3、初步排查问题:
有几个可能的原因导致创建新的SqlSession到查询语句耗时特别长:
1. 数据库连接池问题:如果连接池中没有空余的连接,则创建新的SqlSession时需要等待连接释放。可以通过增加连接池大小或者优化查询语句等方式来缓解该问题。
排除连接池问题:我们连接池比较大,通过mysql show processlist查看几乎没有慢查询的连接。
2. 网络延迟问题:如果数据库服务器和应用服务器之间的网络延迟较大,则创建新的SqlSession时会受到影响。可以通过优化网络配置或者将数据库服务器和应用服务器放在同一台机器上等方式来缓解该问题。
排除网络问题:ping mysql地址,耗时都在0.5ms左右。
3. 查询语句过于复杂:如果查询语句过于复杂,则会导致查询时间较长。可以通过优化查询语句或者增加索引等方式来缓解该问题。
排除复杂sql问题:简单insert 语句,通过mysql show processlist查看没有慢查询的连接。
<insert id="batchInsertDuplicate">INSERT INTO b_table_#{day} (<include refid="selectAllColumn"/>) VALUES<foreach collection="list" item="item" index="index" separator=",">(#{item.id ,jdbcType=VARCHAR },#{item.sn ,jdbcType=VARCHAR },#{item.ip ,jdbcType=VARCHAR },.....#{item.createTime ,jdbcType=TIMESTAMP },#{item.updateTime ,jdbcType=TIMESTAMP })</foreach>ON DUPLICATE KEY UPDATE`ip`=VALUES(`ip`),`updateTime`=VALUES(`updateTime`)
</insert>
4. 数据库服务器性能问题:如果数据库服务器性能较低,则创建新的SqlSession时会受到影响。可以通过优化数据库服务器配置或者升级硬件等方式来缓解该问题。
排除数据库服务器性能问题:mysql是8核16G,通过mysql show processlist查看没有慢查询的连接。
5. 应用服务器性能问题:如果应用服务器性能较低,则创建新的SqlSession时会受到影响。可以通过优化应用服务器配置或者升级硬件等方式来缓解该问题。
暂时无法确定:top查看cpu占用比较高90%,可能原因是mybatis框架处理sql语句引起cpu飙高。
三、定位cpu飙高耗时的方法
1、优化代码:
5000条改为500条批量插入,查看每个线程的耗时依然很高,说明是mybatis框架处理sql语句耗cpu。
top -H -p 18912
2、使用arthas定位:
curl -O https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar pid
使用dashboard可以查看每个线程耗cpu情况,和top -H 差不多:
3、trace方法耗时:
trace com.xxxx.class xxxmethod
逐步跟进去:
最后定位方法
org.apache.ibatis.parsing.GenericTokenParser:parse耗时:
查看 org.apache.ibatis.parsing.GenericTokenParser:parse的源码:
public String parse(String text) {StringBuilder builder = new StringBuilder();if (text != null) {String after = text;int start = text.indexOf(this.openToken);for(int end = text.indexOf(this.closeToken); start > -1; end = after.indexOf(this.closeToken)) {String before;if (end <= start) {if (end <= -1) {break;}before = after.substring(0, end);builder.append(before);builder.append(this.closeToken);after = after.substring(end + this.closeToken.length());} else {before = after.substring(0, start);String content = after.substring(start + this.openToken.length(), end);String substitution;if (start > 0 && text.charAt(start - 1) == '\\') {before = before.substring(0, before.length() - 1);substitution = this.openToken + content + this.closeToken;} else {substitution = this.handler.handleToken(content);}builder.append(before);builder.append(substitution);after = after.substring(end + this.closeToken.length());}start = after.indexOf(this.openToken);}builder.append(after);}return builder.toString();}
主要作用是对 SQL 进行解析,对转义字符进行特殊处理(删除反斜杠)并处理相关的参数(${}),如sql需要解析的标志${name} 替换为实际的文本。我们可以使用一个例子说明:
final Map<String,Object> mapper = new HashMap<String, Object>();
mapper.put("name", "张三");
mapper.put("pwd", "123456");//先初始化一个handler
TokenHandler handler = new TokenHandler() {@Overridepublic String handleToken(String content) {System.out.println(content);return (String) mapper.get(content);}};
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
System.out.println("************" + parser.parse("用户:${name},你的密码是:${pwd}"));
通过源码发现,如果mapper定义的字段很多,for遍历条数比较大(下面红色部分):
<insert id="batchInsertDuplicate">
INSERT INTO b_table_#{day} (
<include refid="selectAllColumn"/>
) VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id ,jdbcType=VARCHAR },
#{item.sn ,jdbcType=VARCHAR },
#{item.ip ,jdbcType=VARCHAR },
.....
#{item.createTime ,jdbcType=TIMESTAMP },
#{item.updateTime ,jdbcType=TIMESTAMP }
)
</foreach>
ON DUPLICATE KEY UPDATE
`ip`=VALUES(`ip`),
`updateTime`=VALUES(`updateTime`)
</insert>
需要解析耗时较久,由于都是字符串遍历,特别耗CPU,因此可以看到cpu飙升很高。
四、解决
解决:直接执行原声sql。
不使用mapper方式拼接sql,而是手动拼接好sql,使用JdbcTemplate执行原声sql。
相关文章:

架构师技能9-深入mybatis:Creating a new SqlSession到查询语句耗时特别长
开篇语录:以架构师的能力标准去分析每个问题,过后由表及里分析问题的本质,复盘总结经验,并把总结内容记录下来。当你解决各种各样的问题,也就积累了丰富的解决问题的经验,解决问题的能力也将自然得到极大的…...

【JavaEE】_HTTP请求报头header
目录 1. Host 2. Content-Length与Content-Type 2.1 Content-Length 2.2 Content-Type 3. User-Agent(UA) 4. Referer 5. Cookie header的整体格式是“键值对”结构,一行是一个键值对,这些键值对都是HTTP定义好的、有特殊含…...

随想录刷题笔记 —二叉树篇10 450删除二叉搜索树节点 669修剪二叉搜索树 108有序数组转换为二叉搜索树
450删除二叉搜索树节点 删除结点分为2种情况: 1.结点的孩子只有一个或没有,则直接用孩子或空替代 2.结点的孩子有两个,用左孩子替代,将左孩子的右孩子移到结点右子树的最左结点 解法一:递归 class Solution {publ…...

Docker基础篇(二)
docker run -d docker run -d 容器名或容器ID docker run -d 后台生成容器,并退出容器(除容器中在运行脚本) docker run -it 交互生成容器 docker run -d centos /bin/sh -c “while true; do echo zen; sleep 2;done” 查看容器中的进程…...

时序数据库TimescaleDB,实战部署全攻略
📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】!😜&am…...

Gson 库的使用
Gson 是由 Google 开发的一个流行的 Java 库,用于处理 JSON 数据的序列化和反序列化。它提供了简单易用的 API,使得在 Java 应用程序中操作 JSON 数据变得非常方便。 以下是 Gson 库的一些主要特点和用法 简单易用 Gson 提供了一个简单而直观的 API,使得在 Java 应用程序中…...

Java Swing游戏开发学习1
不使用游戏引擎,只使用Java SDK开发游戏的学习。 游戏原理 图片来自RyiSnow视频讲解 原理结合实际代码 public class GamePanel extends Jpanel implements Runnable {...run(){}// 详情看下图... }项目结构 运行效果 代码code 在我的下载里面可以找到&#x…...

Stable Diffusion 模型的概念、类型、下载、安装、使用
本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 大家好,我是水滴~~ 我们在《Stable Diffusion WebUI 界面介绍》 时,第一个就讲到了 Stable Diffusion 模型,那么这个模型是什么?该从哪儿下载&…...

Go 1.22 对 net/http 包的路由增强功能详解
目录 方法匹配(Method Matching) 通配符(Wildcards) 路径前缀匹配 优先规则 兼容性 API 变更 小结 参考资料 Go 1.22 版本对 net/http 包的路由功能进行了增强,引入了方法匹配(method matching&…...

【安卓基础3】Activity(一)
🏆作者简介:|康有为| ,大四在读,目前在小米安卓实习,毕业入职 🏆本文收录于 安卓学习大全,欢迎关注 🏆安卓学习资料推荐: 视频:b站搜动脑学院 视频链接 &…...

SpringBoot基于JWT的token做登录认证
背景 我们在基于Session做登录认证的时候,会有一些问题,因为Session存储到服务器端,然后通过客户端的Cookie进行匹配,如果正确,则通过认证,否则不通过认证。这在简单的系统中可以这么使用,并且…...

[ 2024春节 Flink打卡 ] -- Paimon
2024,游子未归乡。工作需要,flink coding。觉知此事要躬行,未休,特记 Flink 社区希望能够将 Flink 的 Streaming 实时计算能力和 Lakehouse 新架构优势进一步结合,推出新一代的 Streaming Lakehouse 技术,…...

计算机网络——14CDN
CDN 视频流化服务和CDN:上下文 视频流量:占据着互连网大部分的带宽 Netflix,YouTube:占据37%,16%的下行流量 挑战:规模性-如何服务~1B用户? 单个超级服务器无法提供服务(为什么&am…...

Docker技术仓库
数据卷 为什么用数据卷? 宿主机无法直接访问容器中的文件容器中的文件没有持久化,导致容器删除后,文件数据也随之消失容器之间也无法直接访问互相的文件 为解决这些问题,docker加入了数据卷机制,能很好解决上面问题…...

Kotlin学习 6
1.接口 interface Movable {var maxSpeed: Intvar wheels: Intfun move(movable: Movable): String}class Car(var name: String, override var wheels: Int 4, _maxSpeed: Int) : Movable {override var maxSpeed: Int _maxSpeedget() fieldset(value) {field value}overr…...

⭐北邮复试刷题LCR 052. 递增顺序搜索树__DFS (力扣119经典题变种挑战)
LCR 052. 递增顺序搜索树 给你一棵二叉搜索树,请 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。 示例 1: 输入:root [5,…...

获取discord上自己创建的服务器的服务器ID、频道ID以及discord的登录token(用于第三方登录)
在服务器图标上右键点击-》复制服务器ID 在频道上右键点击-》复制频道ID F12->手机模式-》application-》local storage-》填写过滤条件【token】 我开发的chatgpt网站: https://chat.xutongbao.top...

图纸透明加密:保护机械图纸安全的新方法
随着信息技术的不断发展,机械制造行业对于图纸安全的需求越来越高。机械图纸是企业的核心竞争力之一,泄露可能导致严重的商业损失和技术风险。为了解决这一问题,图纸透明加密成为了一种新的保护机械图纸安全的方法。本文将介绍图纸透明加密的…...

基于springboot + vue实现的前后端分离-酒店管理系统
项目介绍 基于springboot vue实现的酒店管理系统一共有酒店管理员和用户这两种角色。 管理员功能 登录:管理员可以通过登录功能进入系统,确保只有授权人员可以访问系统。用户管理:管理员可以添加、编辑和删除酒店的用户,包括前…...

79.SpringBoot的核心注解
一、SpringBoot的核心注解 SpringBootApplication注解:这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合,这三个注解是:SpringBootConfiguration:这个注解实际就是一个Configuration,表示启…...

MATLAB 导出可编辑的eps格式图像
任务描述:部分期刊要求提交可编辑的eps格式图像,方便美工编辑对图像进行美化 我试了直接print或者在figure窗口导出,发现导出的文件放到Adobe AI中并不能编辑,经Google找到解决办法: %EPS exportgraphics(gcf,myVect…...

四问带你搞懂 I3C
大家都知道 I2C ,它的全称是 Inter Integrated Circuit ,那 I3C 又是什么? I3C 是 MIPI (Mobile Industry Processor Interface)移动产业处理器接口联盟推出的,全称是 Improved Inter Integrated Circuit &…...

fastjson解析自定义get方法导致空指针问题
背景 为了在日志中把出入参打印出来,以便验证链路和排查问题,在日志中将入参用fastjson格式化成字符串输出,结果遇到了NPE。 问题复现 示例代码 public static void main(String[] args) {OrganizationId orgId new OrganizationId();N…...

github新手用法详解
GitHub是一个非常强大的版本控制工具,它为程序员提供了一个便捷的方式来管理代码、协作开发和参与开源项目。但对于新手来说,可能会觉得GitHub的使用有些复杂。因此,本篇文章将详细介绍GitHub的基本用法,帮助新手快速上手并充分利…...

MAC电脑系统清理空间免费版软件CleanMyMac X2024
大家好,我是那个总是被苹果电脑“内存已满”提示搞得焦头烂额的专业博主。如果你也像我一样,在使用Mac时经常遭遇卡顿、慢吞吞的情况,那么今天的Mac清理空间妙招分享绝对适合你! CleanMyMac X全新版下载如下: https://wm.makedi…...

notepad++运行python闪一下就没啦
问题:Notepad直接快捷键运行Python代码,出现闪一下就没了 解决措施: ①点击菜单运行(Run) --> 运行(Run)弹出的对话框 ②把 cmd /k python "$(FULL_CURRENT_PATH)" & ECHO. & PAUSE & EXIT 粘贴进入这个对话框内 ③点击保存&a…...

基于springboot+vue的课程答疑系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...

【工具类】非 sudo 运行 docker
非 root 运行 docker 命令 sudo groupadd docker sudo usermod -aG docker $USER newgrp docker sudo chown root:docker /var/run/docker.sock sudo chown "$USER":"$USER" /home/"$USER"/.docker -R sudo chmod grwx "$HOME/.docker&quo…...

力扣49.字母异位词分组
题目描述: 49. 字母异位词分组 难度 中等 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs ["eat", "tea"…...

git操作--->在远程删除了某个分支,但本地使用git branch -r的时候还是会显示某个分支存在是什么原因
💕又迷糊了哈哈,以为自己命令执行错了,结果可能是缓存的原因:💕 😂如果你发现使用 git branch -r 命令显示了一个远程没有的分支,这可能是由以下几个原因造成的:😂 缓存…...