SpringBoot项目中mybatis执行sql很慢的排查改造过程(Interceptor插件、fetchSize、隐式转换等)

news2024/11/16 23:35:56

刚入职公司,就发现公司项目跑sql特别慢,差不多一万条数据插入到数据库要5秒以上(没有听错,就是这个速度),查询修改删除也是特别慢。直到22年年底实在是受不了了,我就去排查了一下。
用的是Oracle数据库,mybatismybatis plus,其中mybatis是引入的平台的依赖。平台封装了一些工具和插件。

做个对照试验

首先为了做对照试验,自己新建了一个SpringBoot项目T,里面引入了官方的mybatis。在自己项目中序列化一万条数据搞成文件,然后在T项目中反序列再插入到数据库。只花了100毫秒(一年了,具体我忘了,反正特别快),从5秒往上到现在这个速度,简直相差太大了。
然后又在T项目中引入了mybatis plus,使用mp的批量保存,速度也是100毫秒以内。接着我又把平台的mybatis依赖引入,重新插入数据,好家伙,单位成了秒。所以这下我就确定是平台的这个mybatis依赖有问题了。其实最开始百度了很多,用了很多种方法来写sql,但是效果都不是特别好.

mybatis Interceptor拦截器排查

排查自定义的mybatis Interceptor拦截器插件1

正好之前开通了平台代码的git权限,就去看了平台的代码,发现好多自定义的拦截器插件,还有一些其他的类,所以大概就是这些地方有问题了。
想到之前每次执行sql的时候,控制台都会输出一堆sql日志,还有执行时间,大概批量保存100条就会输出100条日志。后面我用arthas去看这个日志插件的执行速度,单条的话不算慢,但是量级一大,这个就很慢了。这个日志插件实现思路和【Mybatis】MybatisSqlInterceptor Interceptor 拦截器打印完整的sql语句这篇文章差不多,就是多了对不同厂商数据库连接池的处理。后面我关闭了这个插件,发现保存速度快了将近一秒。
正常的批量保存之所以快,是因为底层复用了PreparedStatement对象,一次批量保存多条数据,会先编译sql,后面都是发送占位符?的实际值就行了,但是调试发现用了平台的这个日志插件之后,会导致每次都生成一个新的PreparedStatement对象来执行,这相当于每插入一条数据都要编译一次sql。但是有些场景有需要用到sql日志(不是debug级别),所以我就想了其他办法来输出日志,具体参考这篇文章mybatis自定义日志实现,换成我这种的之后,测试发现对插入数据的速度影响不大。

排查自定义的mybatis Interceptor拦截器插件2

在移除掉平台的日志插件之后,速度快了一秒,但是还是很慢,然后接着排查。用了arthas的一些命令比如watchtrace等(安利一个插件arthas idea,真的好用),发现一个叫空集合处理的插件每次都会被调用,而且速度还不快。打开这个类发现里面是有两个处理,一是给没有是否删除条件的sql加上IS_DELETED,二是给有是否删除条件的sql检查值是否正确(这个是因为之前改过是否删除的枚举,为了兼容旧代码)。因为我们这边都是带了是否删除的标识,而且全部都是新的枚举,所以就跟平台协调能否去掉这个插件,最后是平台那边把他们那边的代码全部加上了这个标识,然后给这个插件加上了一个开关。拉新依赖之后,我这边直接关了,然后又关闭了一些其他不需要的插件(另外的插件其实没用调用,只是我们系统里面没有用到)。关闭这个空集合插件之后,重新保存,速度直接到了1秒多一点,相比之前快了很多。虽然说相比正常的还是很慢,但是没时间排查了。

查询很慢排查改造

fetchSize配置

保存的速度已经正常了,但是查询的速度还是异常的慢。但是因为项目比较忙,就搁置了。大概过了半年,正好安排优化报表项目,就顺带研究了一下查询为什么那么慢。
查询一万条数据,他要好几秒(数据量少很慢,数据量大更慢,想一秒查出来几乎不可能),但是我去DataGrip里面跑这条sql,几十毫秒,查询执行计划,发现这条sql是走了索引的。
后面去查了一下平台的mybatis默认配置,是没有配置fetchSize大小的,所以就很慢,
fetchSize的作用,文心一言

JDBC中的fetchSize是一个重要的配置参数,它决定了每次从数据库中检索并传输到客户端的记录数。以下是对fetchSize的详细解释和配置方法:
一、fetchSize的作用
内存管理:通过控制每次从数据库检索的记录数,fetchSize有助于减少客户端的内存占用,特别是在处理大量数据时。如果fetchSize设置得过大,可能会导致内存溢出;如果设置得过小,则可能增加网络往返次数,影响性能。
性能优化:合理设置fetchSize可以在内存使用和性能之间找到平衡点。对于需要快速响应的应用,较小的fetchSize可能更合适;而对于可以容忍一定延迟的应用,较大的fetchSize可能有助于减少网络开销。

后面我在mapper文件的select标签中里面加上了fetchSize="600",相较于之前速度果然变的特别快,但是和DataGrip里面执行的速度还是相差挺多的
在这里插入图片描述
添加fetchSize有三种方式

  1. xml中在select标签里面加fetchSize属性
  2. mybatis注解方式写查询sql的可以加上@Options注解,配置fetchSize属性
  3. mybatis plus可以通过mybatis-plus.configuration.default-fetch-size属性来配置默认的,这个针对于mp自有的方法
jdbcType是否配置测试

在小组的项目中测试了一些查询语句,发现了一个问题,配置了jdbcType的和没有配置jdbcType的sql查询速度相差很大(之前我只有部分用IDEA的mybatis插件生成的sql脚本加了这些,其他人代码都没加),加了jdbcType的sql执行速度只比Data Grip里面执行慢一点点,几乎可以忽略不记。

做对照试验

在T项目中,同样的查询sql(xml方式),使用的平台mybatis依赖,添加jdbcType和不加jdbcType效果等同于小组的项目。
然后去除了平台的mybatis依赖,再次运行,不加的速度和加的一样快了。到这里我已经有点麻木了。
但是起码发现加上了速度会变快,最后项目组的项目xml里面的sql全部加上了这个jdbcType,mp的也加上了,通过@TableFieldjdbcType属性指定了
mp的还可以通过@TableName注解的两个属性来配置这个,这样就不用在每个类属性上配置了,具体可以去官网看一下
在这里插入图片描述

平台代码导致的赋值方法调用错误

本以为加上了这个,CRUD语句的速度都能变正常,但是他总能给我惊喜,用自定义sql方式跑速度都很快,用mp的批量删除跑速度也很快,但用mp的selectXXX系列方法,速度竟然和最开始一样,慢的离谱,秒级以上。
到这我已经想不到还有哪里有问题了,于是乎开始不停的debugmybatis还有mybatis plus的源码,一层套一层,看的人都麻了。后面发现一个问题,自己在xml里面写的sql,里面参数a如果指定了jdbcType,那么在MybatisDefaultParameterHandlersetParameters方法里面是可以拿的到,他就会调用StringTypeHandler(设置了VARCHAR的情况下)来设置占位符的值,实际上就是调用PreparedStatement实现类的setString方法。
但是我如果用mp的查询方法(实体类中已经通过注解方式指定),他这里拿到的竟然是个null,接下来他就调用了一个CustomNStringTypeHandler类来设置占位符的值,数据库中的字段是VARCHAR2类型,这个类里面实际调用了setNString,而setNString对应的数据库类型应该是NVARCHAR。所以,就导致了隐式转换,他就不会走索引,隐式转换加上不走索引,执行语句就很慢,特别是查询和删除,数据量一大,不走索引执行SQL应该知道那速度得有多慢吧。mybatis plus查询方法jdbcType为什么null的下一篇再说

隐式转换是啥?文心一言:

数据库隐式转换,也称为自动转换,是指在执行SQL语句时,数据库管理系统(DBMS)自动进行的数据类型转换。这种转换通常发生在数据类型不匹配但需要进行比较、计算或赋值等操作时。不同数据库系统(如MySQL、Oracle等)中的隐式转换机制可能有所不同,但基本原理相似。以下是对数据库隐式转换的详细解析:
一、隐式转换的场景
字符串与数字的混合比较:
当字符串与数字进行比较时,数据库会尝试将字符串转换为数字(如果可能)。例如,在MySQL中,SELECT * FROM table WHERE column = ‘123’; 如果column是数字类型,MySQL会尝试将字符串’123’转换为数字123进行比较。
插入操作中的类型转换:
当尝试将字符串插入到数字类型的列时,数据库会尝试将字符串转换为相应的数字类型。反之亦然,如果尝试将数字插入到字符串类型的列中,数据库也会进行隐式转换。

数据库隐式转换是指在不显式指定数据类型转换的情况下,数据库系统根据上下文自动进行数据类型转换的过程。这种转换通常发生在SQL查询中,当操作符两边的数据类型不一致时,数据库会尝试将它们转换为一种共同的数据类型以便进行比较或计算。
然而,隐式转换有时会导致数据库查询不使用索引,从而影响查询性能。这主要是因为索引是基于特定数据类型的列创建的,当查询条件中的数据类型与索引列的数据类型不匹配时,数据库可能无法直接利用索引来加速查询。以下是几个导致隐式转换不走索引的常见原因:

在这里插入图片描述
接着就去看了一下这个CustomNStringTypeHandler类,这个类实现了BaseTypeHandler类。嗯~,又是平台的类,类上面的注解

@MappedJdbcTypes(
    value = {JdbcType.NVARCHAR},
    includeNullJdbcType = true
)
@Component

这个意思就是说,如果jdbcTypeNVARCHAR类型的,就会调用这个类来进行占位符赋值,重点是这个includeNullJdbcType = true,如果jdbcTypenull的时候,也会使用这个。这就能解释的通为什么xml里面没加jdbcType就会很慢。因为数据库里面是VARCHAR2类型,然后又因为没有配置jdbcType,所以导致调用了这个类的赋值方法。
后面就去找平台沟通说这个代码有问题,平台说当初加上个是为了解决部分中文乱码问题(VARCHAR2有些中文字符存放的话会乱码,要用NVARCHAR),后面平台是把includeNullJdbcType = true给去掉了,我当时是建议直接删除这个类,因为mybatis那边是自带的。
之所以前面会发生乱码其实是同样的原因,之前没有这个类,然后写的sql里面没有配置jdbcType,所以导致他字符串类型的值默认使用了PS实现类的setString方法赋值,而数据库中又是NVARCHAR类型。
后面项目中引入了平台新的mybatis依赖,CRUD的速度都正常了。

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

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

相关文章

大模型之二十八-语音识别Whisper进阶

在上一篇博客大模型之二十七-语音识别Whisper实例浅析中遗留了几个问题,这里来看一下前两个问题。 1.如果不是Huggingface上可以下载的数据该怎么办? 2.上面的代码是可以训练了,但是训练的时候loss真的会和我们预期一致吗?比如如下…

【netty系列-08】深入Netty组件底层原理和基本实现

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

第一个go程序

package main import "fmt" func main(){fmt.Println("Hello World") }hello.go所在目录 运行go程序

美团代付多模版三合一源码 附教程

简介 美团代付多模版三合一源码 附教程 界面

这一轮医疗数字化,沃可趣以医疗专业人员交流成长为中心

沃可趣看见医疗行业人员需求痛点,量身打造数字服务平台,促进知识分享,赋能活动组织。 从电子病历的普及到远程医疗的兴起,从人工智能辅助诊断到大数据在医疗管理中的应用,科技进步正在大力推动医疗领域的发展。然而&a…

Ubuntu系统本地搭建WordPress网站并一键发布内网站点至公网实战

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

嵌入式技术文件、学习资料、在线工具、学习网站、技术论坛,非常全面的分享~~~

一、数据手册查询及下载网站 1、【ALLDATASHEET 自称是最大的在线电子元件数据的搜索引擎】ALLDATASHEETCN.COM - 电子元件和半导体及其他半导体的数据表搜索网站。电子元件和半导体, 集成电路, 二极管, 三端双向可控硅 和其他半导体的https://www.alldatasheetcn.com/ 2、【…

defineProps、defineEmits、 defineExpose的TS写法

小满视频 1 defineProps&#xff1a;父向子传递数据 作用&#xff1a;父组件向子组件传递数据 1.1 传递纯类型参数的方式来声明 父组件中的代码&#xff1a; 父组件App.vue <template><div><span>传递给子组件的响应式数据&#xff1a;</span>&l…

【循环顺序队的实现】

1.队列的逻辑结构 与 抽象数据类型定义 先进先出的线性表 在顺序队列中&#xff0c;我们使用头指针front指向队首元素&#xff1b;用尾指针rear指向队尾元素的下一个位置&#xff08;当然这里的指针是用下标模拟出来的&#xff09; 同时顺序队列中的元素当然是用数组来存储的 …

【系统架构设计】嵌入式系统设计(1)

【系统架构设计】嵌入式系统设计&#xff08;1&#xff09; 嵌入式系统概论嵌入式系统的组成硬件嵌入式处理器总线存储器I/O 设备与接口 软件 嵌入式开发平台与调试环境交叉平台开发环境交叉编译环境调试 嵌入式网络系统嵌入式数据库管理系统实时系统与嵌入式操作系统嵌入式系统…

【Qt笔记】QToolButton控件详解

目录 一、前言 二、QToolButton的基本特性 2.1 图标和文本 2.2 自动提升 2.3 下拉菜单 2.4 工具提示 2.5 弹出模式 三、高级功能 3.1 自定义大小与形状 3.2 检查框与单选按钮 3.3 动画效果 四、常用方法与信号槽 常用方法 信号槽 五、实际应用示例 说明 六、总…

ESP32 CYD 使用 LVGL 在屏幕上显示图像 | Random Nerd Tutorials

在本指南中&#xff0c;你将学习如何使用LVGL在ESP32 Cheap Yellow Display (CYD) 板上处理和加载图像。ESP32将使用Arduino IDE进行编程。 对ESP32 Cheap Yellow Display不熟悉&#xff1f; 从这里开始&#xff1a;开始使用ESP32 Cheap Yellow Display Board – CYD (ESP32-2…

线性代数 第三讲 线性相关无关 线性表示

线性代数 第三讲 线性相关无关 线性表示 文章目录 线性代数 第三讲 线性相关无关 线性表示1.向量运算1.线性相关与线性无关1.1 线性相关与线性无关基本概念 2.线性表示&#xff08;线性组合&#xff09;3.线性相关无关与线性表示的定理大总结3.1 向量β可由向量组线性表出的同义…

心觉:潜意识显化很简单,只是很多人想复杂了

很多人知道潜意识的力量很大&#xff0c;是意识力量的30000倍以上 也知道该怎么显化自己的潜意识 但是就是做不到 这就像很多肥胖的人知道运动可以减肥 知道减肥之后就可以穿漂亮的衣服 知道减肥之后自己有多帅多美 但是就是迈不开腿 根本原因是你的潜意识和意识上的认知不…

RenderMan v26.2更新内容!云渲染平台支持新版本

皮克斯的最新RenderMan v26.2版本带来了一系列激动人心的新特性和改进&#xff0c;进一步巩固了其在高端渲染领域的领导地位&#xff0c;为艺术家们提供了更丰富的创意工具和更流畅的工作流程。作为老牌的云渲染农场&#xff0c;瑞云依然支持新版本的使用。 RenderMan v26.2更新…

移动端视频编辑SDK解决方案,关键帧曲线塑造生动效果

美摄科技&#xff0c;作为移动视频编辑技术的领航者&#xff0c;携其革命性的移动端视频编辑SDK解决方案&#xff0c;正以前所未有的创新力&#xff0c;为视频创作者们开启了一扇通往无限创意的大门。 重塑视频编辑体验&#xff0c;让创意触手可及 美摄科技的移动端视频编辑S…

公网信息泄露监测(网盘、暗网、搜索引擎、文档平台)思路分享

一、背景 众测项目中白帽可能会提交一些信息泄露漏洞&#xff0c;同时甲方可会收到一些白帽提交的公网信息泄露文件漏洞&#xff0c;例如百度网盘被员工分享某些文件或者某些包含敏感信息的文件可以通过如谷歌、百度等搜索引擎通过特定语法搜索到。为了可以及时发现泄露的文件…

九泰智库 | 医械周刊- Vol.54

⚖️ 法规动态 国家药监局综合司发布医疗器械管理法草案征求意见稿 国家药监局综合司发布了《中华人民共和国医疗器械管理法&#xff08;草案征求意见稿&#xff09;》&#xff0c;公开征求意见&#xff0c;以加强医疗器械的管理并推动产业高质量发展。该草案共十一章190条&a…

深入解析财务报表:掌握重要财务指标的技巧

一、概述 财务报表中有大量信息&#xff0c;如果我们在分析时缺乏明确的方向或忽视了重点&#xff0c;就很容易在繁杂的数据中迷失方向。本文将深入探讨财务报表中的几个重要指标&#xff0c;帮助大家更有针对性地理解这些内容&#xff0c;包括如何分析资产负债率、解读净资产…

基于python的web框架 Flask 入门基础知识【1】

Flask是一个轻量级的可定制框架&#xff0c;使用Python语言编写&#xff0c;较其他同类型框架更为灵活、轻便、安全且容易上手。 目录 一、项目环境搭配以及安装运行 1.下载安装 2.最小的应用 3.运行应用 4.运行结果 4.1 外部可见的服务器 二、路由 三、http请求 3.1静…