架构师技能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() {
	@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。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【生活】浅浅记录

各位小伙伴们好鸭&#xff0c;今天不是技术文章&#xff0c;浅浅记录一下最近几个月的收获&#x1f60a; 新的一年&#xff0c;一起努力&#xff0c;加油加油&#xff01;

2024年【安全员-A证】免费试题及安全员-A证作业模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-A证免费试题参考答案及安全员-A证考试试题解析是安全生产模拟考试一点通题库老师及安全员-A证操作证已考过的学员汇总&#xff0c;相对有效帮助安全员-A证作业模拟考试学员顺利通过考试。 1、【多选题】 《陕西…

黑色金属冶炼5G智能工厂数字孪生可视化管控系统,推进金属冶炼行业数字化转型

黑色金属冶炼5G智能工厂数字孪生可视化管控系统&#xff0c;推进金属冶炼行业数字化转型。随着科技的不断发展&#xff0c;数字化转型已经成为各行各业发展的必然趋势。金属冶炼行业作为传统工业的重要组成部分&#xff0c;也面临着数字化转型的挑战和机遇。为了推进金属冶炼行…

Vue知识学习

Vue 是什么&#xff1f; 概念&#xff1a;Vue 是一个用于构建用户界面的渐进式框架 Vue 的两种使用方式: ① Vue 核心包开发 场景:局部 模块改造 ② Vue 核心包& Vue插件工程化开发 场景:整站开发 创建Vue 实例&#xff0c;初始化渲染的核心步骤: 1.准备容器 2.引包(官…

这两招,让你轻松俘获客户心

面向政府的数字化解决方案作为睿鸿数字应用的一个分支&#xff0c;在充分借鉴政府项目中积累的丰富经验的基础上&#xff0c;积极开发更多领域通用的标准化产品。 2023年&#xff0c;睿鸿推出了一系列创新的数字应用产品&#xff0c;包括动态表单系统、统一集成门户、统一通信中…

【MySQL】如何理解索引(高频面试点)

一、前言 首先这个博客会介绍一些关于MySQL中索引的基本内容以及一些基本的语法&#xff0c;当然里面也会有些常见的面试题的解答。 二、关于索引 1、概念 索引是一种能够帮助MySQL高效的去磁盘检索数据的一种数据结构。在MySQL的Innodb存储引擎中呢&#xff0c;采用的是B树的…

如何开发通过蓝牙技术实现灯光智能调节的小程序

近年来&#xff0c;随着智能家居市场日渐兴起&#xff0c;人们对于家居生活品质的需求不断提升。在这一背景下&#xff0c;蓝牙智能调节小程序的开发能够为客户提供更便捷、智能化的LED灯光调节方案。需求方想要实现通过蓝牙开发实现LED灯光亮度和颜色的智能调节&#xff0c;同…

NOW 闹个元宵?与亚信安慧AntDB一起猜灯谜,抽奖品

关于亚信安慧AntDB数据库 AntDB数据库始于2008年&#xff0c;在运营商的核心系统上&#xff0c;服务国内24个省市自治区的数亿用户&#xff0c;具备高性能、弹性扩展、高可靠等产品特性&#xff0c;峰值每秒可处理百万笔通信核心交易&#xff0c;保障系统持续稳定运行超十年&a…

关于使用Mxnet GPU版本运行DeepAR报错解决方案

1.引言 我们经常使用GPU来训练和部署神经网络&#xff0c;因为与CPU相比&#xff0c;它提供了更多的计算能力。在本教程中&#xff0c;我们将介绍如何将GPU与MXNet GluonTS一起使用。 首先&#xff0c;确保您的机器中至少有一个Nvidia GPU&#xff0c;并正确安装了CUDA以及CUDN…

SORA技术报告快速解读——浅谈其AIGC积累的技术底蕴

SORA技术报告解读 文章目录 概要SORA整体概要关键性的技术方案解析1. 视觉类型的特征嵌入和处理-video encoder1.1 压缩视频的特征网络模型是什么&#xff1f;1.2 如何处理不同分辨率的训练和推理问题&#xff1f;2 Scaling transformers 扩散模型3. 生成解码器 小结 概要 最…

深度学习基础(三)循环神经网络(RNN)

之前的章节我们初步介绍了卷积神经网络&#xff08;CNN&#xff09;&#xff1a; 深度学习基础&#xff08;二&#xff09;卷积神经网络&#xff08;CNN&#xff09;-CSDN博客文章浏览阅读2次。卷积神经网络&#xff08;CNN&#xff09;的应用领域广泛&#xff0c;尤其在图像处…

跟着野火学FreeRTOS:第二段(事件组)

在小节里面介绍了二进制信号量&#xff0c;计数信号量&#xff0c;互斥量和递归互斥量等功能&#xff0c;其中二进制信号量和计数信号量&#xff08;也包括队列&#xff09;常用于任务和任务之间以及任务和中断之间的同步&#xff0c;她们具有以下属性&#xff1a; 当等待的事…

Sora----打破虚实之间的最后一根枷锁----这扇门的背后是人类文明的晟阳还是最后的余晖

目录 一.Sora出道即巅峰 二.为何说Sora是该领域的巨头 三.Sora无敌的背后究竟有怎样先进的处理技术 1.Spacetime Latent Patches 潜变量时空碎片&#xff0c;建构视觉语言系统 2.扩散模型与Diffusion Transformer&#xff0c;组合成强大的信息提取器 3.DiT应用于潜变量时…

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

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

隐藏饿了么el-select组件的el-select-dropdown部分,只使用el-select的显示框

隐藏饿了么el-select组件的el-select-dropdown部分,只使用el-select的显示框 问题: 由于el-select组件的el-select-dropdown部分是自动插入在最外层Body上的&#xff0c;所以在当前组件的scoped中让el-select-dropdown组件display:none不会生效所以需要&#xff1a; :popper-…

ZS Associates致盛咨询是什么公司?排名怎么样?

随着商业化时代的加速演进&#xff0c;咨询公司在企业发展中的“智囊团”角色愈发突显。对于医药企业来说&#xff0c;一个优秀的咨询团队不仅可以帮助推动整体战略转型及内部改革&#xff0c;还对药品研发、营销起到优化促进作用。 那什么样的咨询企业可称之为优秀的咨询企业…

【Java EE初阶二十】http的简单理解(一)

1. 初识http HTTP 最新的版本应该是 HTTP/3.0&#xff0c;目前大规模使用的版本 HTTP/1.1&#xff1b; 下面来简单说明一下使用 HTTP 协议的场景: 1、浏览器打开网站 (基本上) 2、手机 APP 访问对应的服务器 (大概率) 前面的 TCP与UDP 和http不同&#xff0c;HTTP 的报文格式&a…

【方法】PDF如何与其它格式文件互相转换?

在工作上&#xff0c;有时候我们需要把PDF文件转换成其他格式的文件&#xff0c;比如Word、PPT、jpg等&#xff0c;或者是其他格式文件转换成PDF&#xff0c;那具体要如何操作呢&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 想把PDF文件转换成其他格式文件&#xff0c…

GoLand 相关

goland 下载依赖 go mod tidy&#xff1a;保持依赖整洁 go mod tidy 命令的作用是清理未使用的依赖&#xff0c;并更新 go.mod 以及 go.sum 文件。 go mod tidy 和 go mod vendor 两个命令是维护项目依赖不可或缺的工具。go mod tidy 确保了项目的 go.mod 文件精简且准确&…

掌握这几个技巧,才敢称为Jenkins大神!

01 Performance插件兼容性问题 自由风格项目中&#xff0c;有使用 Performance 插件收集构建产物&#xff0c;但是截至到目前最新版本&#xff08;Jenkins v2.298&#xff0c;Performance&#xff1a;v3.19&#xff09;&#xff0c;此插件和Jenkins都存在有兼容性问题&#xf…