架构师技能9-深入mybatis:Creating a new SqlSession到查询语句耗时特别长

news2024/11/15 15:40:36

开篇语录:以架构师的能力标准去分析每个问题,过后由表及里分析问题的本质,复盘总结经验,并把总结内容记录下来。当你解决各种各样的问题,也就积累了丰富的解决问题的经验,解决问题的能力也将自然得到极大的提升。

励志做架构师的撸码人,认知很重要,可以订阅:架构设计专栏

      撸码人平时大多数时间都在撸码或者撸码的路上,很少关注框架的一些底层原理,当出现问题时没能力第一时间解决问题,出现问题后不去层层剖析问题产生的原因,后续也就可能无法避免或者绕开同类的问题。因此不要单纯做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() {
	@Override
	public 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。

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

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

相关文章

【Python_Zebra斑马打印机编程学习笔记(一)】实现标贴预览的两种方式

实现标贴预览的两种方式 实现标贴预览的两种方式前言一、调用 Labelary Online ZPL Viewer API 方法实现标贴预览功能1、Labelary Online ZPL Viewer API 案例介绍2、生成 PNG 格式3、Parameters 二、通过 zpl 的 label.preview() 方法实现标贴预览功能1、实现步骤2、代码示例 …

每日五道java面试题之spring篇(三)

目录&#xff1a; 第一题 ApplicationContext和BeanFactory有什么区别&#xff1f;第二题 Spring中的事务是如何实现的&#xff1f;第三题 Spring中什么时候Transactional会失效&#xff1f;第四题 Spring容器启动流程是怎样的&#xff1f;第五题 Spring Boot、Spring MVC 和 S…

__proto__和protype的区别

概述&#xff1a; prototype 函数静态属性&#xff0c;非实例属性,所有实例都可以继承它 __proto__ 实例属性&#xff0c;指向实例的原型对象&#xff0c;原型对象包括构造函数和protype属性 替代 现代浏览器中可以使用Object.getPrototypeOf()来替代__proto__来获取原型对象 …

主流的开发语言和开发环境介绍

个人浅见&#xff0c;不喜勿喷&#xff0c;谢谢 软件开发是一个涉及多个方面的复杂过程&#xff0c;其中包括选择合适的编程语言和开发环境。编程语言是软件开发的核心&#xff0c;它定义了程序员用来编写指令的语法和规则。而开发环境则提供了编写、测试和调试代码的工具和平台…

SQL面试题及答案

介绍 在快节奏的数据管理和信息技术世界中,导航和操作结构化数据的能力是一项非常重要的技能。SQL,即结构化查询语言,是关系数据库的基石,掌握这种语言的专业人员的需求量很大。SQL 面试在科技行业很常见,潜在的候选人会接受测试以展示他们的知识和解决问题的能力。为了帮…

【Spring】常见问题总结

目录 1. 什么是 Spring 框架? 2. 列举一些重要的Spring模块&#xff1f; 3. RestController vs Controller 4. Spring IOC & AOP 4.1 谈谈自己对于 Spring IoC 和 AOP 的理解 IoC AOP 4.2 Spring AOP 和 AspectJ AOP 有什么区别&#xff1f; 5. Spring bean 5.1…

OceanMind海睿思助力企业“数据入表”之价值实现与成本计量

2023年8月21日&#xff0c;财政部印发《企业数据资源相关会计处理暂行规定》&#xff08;以下简称《暂行规定》&#xff09;&#xff0c;明确了数据资产会计处理适用的范围、准则、列示和披露要求。 《暂行规定》是规范企业数据资产会计处理指导性和引领性的制度文件&#xff…

微信小程序 --- wx.request网络请求封装

网络请求封装 网络请求模块难度较大&#xff0c;如果学习起来感觉吃力&#xff0c;可以直接学习 [请求封装-使用 npm 包发送请求] 以后的模块 01. 为什么要封装 wx.request 小程序大多数 API 都是异步 API&#xff0c;如 wx.request()&#xff0c;wx.login() 等。这类 API 接口…

探索无限:Sora与AI视频模型的技术革命 - 开创未来视觉艺术的新篇章

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

解决两个MySQL5.7报错

目录 1.启动不了MySQL&#xff0c;报错缺少MSVCR120.dll去官网下载vcredist_x64.exe运行安装进入管理员CMD 2.本地计算机 上的 mysql 服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止&#xff0c;Fatal error: Can‘t open and lock privilege tables: Table ‘…

网络电视盒子哪个品牌好?达人强推口碑电视盒子推荐

电视盒子使用时容易卡顿、死机&#xff0c;广告植入过多&#xff0c;系统操作复杂&#xff0c;散热差&#xff0c;这是很多电视盒子都存在的问题&#xff0c;网络电视盒子哪个品牌好是大家都在讨论的话题&#xff0c;我这次要分享的是用户评价最好的五款电视盒子推荐给不懂如何…

操作系统-吸烟者问题

文章目录 概述问题分析如何实现小结 概述 操作系统中的吸烟者问题是一个经典的同步问题&#xff0c;它涉及到进程间的同步和互斥。 在这个问题中&#xff0c;有三个抽烟者进程和一个供应者进程。每个抽烟者需要三种材料&#xff1a;烟草、纸和胶水来完成吸烟的操作。三个抽烟…

http协议基础与Apache的简单介绍

一、相关介绍&#xff1a; 互联网&#xff1a;是网络的网络&#xff0c;是所有类型网络的母集因特网&#xff1a;世界上最大的互联网网络。即因特网概念从属于互联网概念。习惯上&#xff0c;大家把连接在因特网上的计算机都成为主机。万维网&#xff1a;WWW&#xff08;world…

Stable Diffusion 模型分享:AstrAnime(Astr动画)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五 下载地址 模型介绍 AstrAnime 是一个动漫模型&#xff0c;画风色彩鲜明&#xff0c;擅长绘制漂亮的小姐姐。 条目内容类型大模型…

vue中实现拖拽排序功能

npm i vuedraggable <template><div class"app-container"><!-- <div :class"canEdit ? dargBtn-lock el-icon-unlock : dargBtn-lock el-icon-lock" click"removeEvent()">{{ canEdit ? 调整 : 锁定 }}</div> --&…

Windows传文件到Linux系统(拖拽方式)

1、概述? 在平时开发过程中,可能会遇到如下场景: 在自己的Window系统中安装了VMware虚拟机,并在里面安装了Linux系统(Centos,Ubuntu,redhat等)。这个时候我们期望将window中的文件传递到Linux中,传递的过程比较的麻烦。 这个时候我们就可以借助相关工具直接拖拽完成。 …

ubuntu内核卸载重装

目录 问题1.问题复现2.可以正常启动的方式 保存快照卸载有问题的内核重装最新内核参考资料 问题 1.问题复现 ubuntu开机出现如下画面,启动不能正常启动 2.可以正常启动的方式 使用其他内核可以正常工作 保存快照 在解决之前保存快照,防止破坏时恢复 卸载有问题的内核…

Gitea提交代码自动触发Jenkins构建版本

提交代码自动触发Jenkins构建版本 1. 下载Generic Webhook Trigger 2. 配置Generic Webhook Trigger http://JENKINS_URL/generic-webhook-trigger/invoke?tokenruoyi-ui-8978456465 http://192.168.0.136:8090 为jenkisn地址&#xff0c;/generic-webhook-trigger/invoke?…

【selenium】执行 Javascript 脚本 滚动、元素的特殊操作等

某些特殊情况下&#xff0c;使用selenium的api无法操作页面元素&#xff0c;点击、滚动实现的某些功能&#xff0c;可以考虑通过执行js来完成。 为什么不用js写自动化&#xff1f;——selenium第一版是js写的&#xff0c;但js兼容性存在问题&#xff0c;所以引入webdriver 现在…

【C语言-期末项目/项目实践】-贪吃蛇(两万五千字详解,手把手教程博文,期末答辩神助手)

重要&#xff1a;游戏逻辑设计图 目录 1.游戏背景 2.项目效果展示 3.项目实现要求 4.技能要求 5.技术要点 6.Win32 API介绍 6.1Win32API 6.2控制台程序 6.3控制台屏幕上的坐标COORD 6.4GetStdHandle 6.5GetConsoleCursorInfo 6.5.1 CONSOLE_CURSOR_ INFO 6.6 Se…