java执行sql慢 navicat不慢 见鬼了

大家好,我是烤鸭:

   有点意思的问题,代码提示接口超时(10s+),接口逻辑很简单,就一个sql查询。本来也想是sql慢,可是拿sql去Navicat执行下,一点不慢(50ms)。

环境

DB:SqlServer

连接池:Druid

ORM:Mybatis

猜想

是刚好那个时段有其他操作造成的阻塞?

有特定参数造成的大量回表?

用了三方插件有bug?skywalking?pagehelper?

尝试复现

这个问题只在生产环境出现,即便是把生产数据备份到测试库,也不能复现。

可能生产库和测试库版本和配置不一样。

原sql

<if test="title != null and title!=''">AND vv.title  LIKE CONCAT(CONCAT('%', #{title}), '%')
</if>

同事有人说把参数写死试试,原来的sql类似这样。不慢了,什么原理。

<if test="title != null and title!=''">AND vv.title  LIKE CONCAT(CONCAT('%', 'abc'), '%')
</if>

再把#换成$,还是很快。大概就知道要看哪块的源码了。

<if test="title != null and title!=''">AND vv.title  LIKE CONCAT(CONCAT('%', '${title}'), '%')
</if>

关于mybatis的#和$,这篇文章写的挺好的,就不再重复了。

https://blog.csdn.net/weixin_43401380/article/details/122504003

源码分析

先说下sql执行的大致流程。

ORM框架(Mybatis做完动态sql之后) —> 连接池插件(Druid) —> JDBC —> DB(Sqlserver)。

下图是以查询方法为例。

在这里插入图片描述

接着我们看下源码:

SQLServerPreparedStatement.doPrepExec(执行RPC请求,构建请求sql,以及是否需要参数等)

final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerException {resetForReexecute();// ...boolean hasExistingTypeDefinitions = preparedTypeDefinitions != null;boolean hasNewTypeDefinitions = true;if (!encryptionMetadataIsRetrieved) {// 动态sql变量初始化hasNewTypeDefinitions = buildPreparedStrings(inOutParam, false);}// ...String dbName = connection.getSCatalog();boolean needsPrepare = true;// Retry execution if existing handle could not be re-used.for (int attempt = 1; attempt <= 2; ++attempt) {try {// 构建 TDSWriter ,指令为TDS的RPCTDSWriter tdsWriter = command.startRequest(TDS.PKT_RPC);// PrepExec 执行needsPrepare = doPrepExec(tdsWriter, inOutParam, hasNewTypeDefinitions, hasExistingTypeDefinitions);// 结果监听ensureExecuteResultsReader(command.startResponse(getIsResponseBufferingAdaptive()));startResults();getNextResult();}catch (SQLException e) {// ...}break;}       
}

SQLServerPreparedStatement.buildPreparedStrings(

sql替换,把 ? 转换成 @p1,@p2 这种,比如 mybatis 的转换后是这样的。

AND vv.title  LIKE CONCAT(CONCAT('%', @p1), '%')

/*** Determines whether the statement needs to be reprepared based on a change in any of the type definitions of any of the parameters due to* changes in scale, length, etc., and, if so, sets the new type definition string.*/
private boolean buildPreparedStrings(Parameter[] params,boolean renewDefinition) throws SQLServerException {String newTypeDefinitions = buildParamTypeDefinitions(params, renewDefinition);if (null != preparedTypeDefinitions && newTypeDefinitions.equals(preparedTypeDefinitions))return false;   preparedTypeDefinitions = newTypeDefinitions;/* Replace the parameter marker '?' with the param numbers @p1, @p2 etc */preparedSQL = connection.replaceParameterMarkers(userSQL, params, bReturnValueSyntax);if (bRequestedGeneratedKeys)preparedSQL = preparedSQL + identityQuery;return true;
}

SQLServerPreparedStatement.doPrepExec(调用写SQL和写参数方法)

private boolean doPrepExec(TDSWriter tdsWriter,Parameter[] params,boolean hasNewTypeDefinitions,boolean hasExistingTypeDefinitions) throws SQLServerException {boolean needsPrepare = (hasNewTypeDefinitions && hasExistingTypeDefinitions) || !hasPreparedStatementHandle();// ...else {// Move overhead of needing to do prepare & unprepare to only use cases that need more than one execution.// First execution, use sp_executesql, optimizing for asumption we will not re-use statement.if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCall() && !isExecutedAtLeastOnce) {// 第一次执行的时候buildExecSQLParams(tdsWriter);isExecutedAtLeastOnce = true;}// Second execution, use prepared statements since we seem to be re-using it.else if(needsPrepare)// 写入sqlbuildPrepExecParams(tdsWriter);elsebuildExecParams(tdsWriter);}// 写入参数sendParamsByRPC(tdsWriter, params);return needsPrepare;
}

SQLServerPreparedStatement.buildPrepExecParams(通过TCP(TDS协议),写入SQL和参数定义)

private void buildPrepExecParams(TDSWriter tdsWriter) throws SQLServerException {if (getStatementLogger().isLoggable(java.util.logging.Level.FINE))getStatementLogger().fine(toString() + ": calling sp_prepexec: PreparedHandle:" + getPreparedStatementHandle() + ", SQL:" + preparedSQL);// ...tdsWriter.writeShort((short) 0xFFFF); // procedure name length -> use ProcIDs// 执行方法是 SP_PREPEXECtdsWriter.writeShort(TDS.PROCID_SP_PREPEXEC);tdsWriter.writeByte((byte) 0);  // RPC procedure option 1tdsWriter.writeByte((byte) 0);  // RPC procedure option 2// <prepared handle>// IN (reprepare): Old handle to unprepare before repreparing// OUT: The newly prepared handletdsWriter.writeRPCInt(null, getPreparedStatementHandle(), true);resetPrepStmtHandle();// <formal parameter defn> IN,写入参数定义,比如上面的title就是 @P0 NVARCHAR(4000)tdsWriter.writeRPCStringUnicode((preparedTypeDefinitions.length() > 0) ? preparedTypeDefinitions : null);// <stmt> IN,写入SQLtdsWriter.writeRPCStringUnicode(preparedSQL);
}

在这里插入图片描述

在这里插入图片描述

DTVExecuteOp.execute(执行实际参数的赋值)

void execute(DTV dtv,String strValue) throws SQLServerException {if (null != strValue && strValue.length() > DataTypes.SHORT_VARTYPE_MAX_CHARS)dtv.setJdbcType(JDBCType.LONGNVARCHAR);// 实际的参数setTypeDefinition(dtv);
}

在这里插入图片描述

原因猜想

分析了一大顿源码,并没有找到问题所在,同样的SQL在程序执行隔一天就不慢了,不清楚是不是还有别的什么因素影响执行计划。

  1. 连接规范不同:像数据库client的实现规范有JDBC(针对java语言的)或者ODBC,我们Java服务程序用的JDBC,像navicat这些软件用的是ODBC。导致同样的SQL执行计划不同。
  2. SqlServer-Client 版本问题,不同版本的client也有不同的优化。
  3. sp_prepare | sp_execute 带参数执行 和 sp_executesql,生成的执行计划不同。

没找到答案,问题竟然自己消失了,先蹲个点,如果再出现就再研究一下。

总结

当出现这个问题的时候,不要迷,先想想这个题干成立么。

navicat 执行很快,java程序很慢,两边执行的是同一个sql么?
原本在navicat 执行的是(不慢)

SELECT TOP 15 id FROM test where tile like '%测试%'

实际执行的应该是这个 sp_prepare 和 sp_execute的语句(待验证,由于问题消失,没法验证了)

declare @N int
exec sp_prepare @n output,N'@p1 NVARCHAR (4000)',N'SELECT TOP 15 id FROM test where tile like ''%'' + @p1 +''%'' '
exec sp_execute @n,'aaa' --@n就是sp_prepare返回的句柄,使用sp_execute来通过这个句柄来传递参数

如果执行这个 navicat 也变慢了,那就可以推论是 sp_prepare 这种方式导致的。

看了源码还是很难定位问题,越难的问题,解决起来越有意思,等再出现记录一下。

相关文章

JDBC和ODBC区别:
https://www.php.cn/mysql-tutorials-414951.html

DTS协议分析:
https://www.docin.com/p-98157348.html

Sp_prepare | sp_execute 介绍:

https://www.cnblogs.com/gered/p/14648626.html

EXEC和sp_executesql使用介绍:

https://blog.csdn.net/neweastsun/article/details/40019439

SQLServer执行动态SQL:

https://www.gxlcms.com/mssql-350221.html

SQLServer执行计划:

https://www.jianshu.com/p/172a345fee95

常见问题

必须声明标量变量:

https://blog.csdn.net/dxnn520/article/details/17304573

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

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

相关文章

windows docker mongodb

大家好&#xff0c;我是烤鸭&#xff1a; 今天翻博客&#xff0c;发现4年前的一篇草稿&#xff0c;抽空给完善下。原本草稿写的是linux下mongo使用&#xff0c;还有java的一些api&#xff0c;现在就用容器实现下。 容器部署 官方网站&#xff1a; https://www.mongodb.com/ w…

Is the byte array a result of corresponding serialization for DefaultDeserializer

大家好&#xff0c;我是烤鸭&#xff1a; 问题记录&#xff0c;上线之后懵逼的问题。只能回滚?每次都是上线来暴击&#xff0c;不然多查查文章也不至于这么被动。 报错日志 org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nes…

容器环境 springcloud gateway grafana prometheus采集集成与问题

容器环境 springcloud gateway grafana prometheus采集集成与问题 大家好&#xff0c;我是烤鸭&#xff1a; 记录下网关上容器后&#xff0c;监控升级的过程。 原来的方式 grafana 和 prometheus 网上教程很多&#xff0c;就不细写了。 没上容器之前&#xff0c;可以在…

easyui Combotree 怎么加载数据 支持多选

1、开发环境vs2012 mvc4 c# 2、HTML前端代码 <% Page Language"C#" AutoEventWireup"true" CodeBehind"DataGridTest.aspx.cs" Inherits"MvcAppTest.DataGridTest" %><!DOCTYPE html><html xmlns"http://www.w3.…

idea 错误: 找不到或无法加载主类(汇总贴)

大家好&#xff0c;我是烤鸭&#xff1a; 现在是采坑实录。 idea 错误: 找不到或无法加载主类 xxx.xxx.xxxxx JDK环境&#xff0c;maven项目还是ee还是web项目&#xff0c;是否都正常。 如果是用idea打开的话,在源码目录上点击右键,然后找到Mark directory as->source ro…

爬虫,关于 video 标签 src 带有blob:http的 一些想法

大家好&#xff0c;我是烤鸭&#xff1a; 之前玩爬虫的时候&#xff0c;看到过video标签中src属性引入的blob:http:xxxx&#xff0c;当时没找到解决思路&#xff0c;今天又遇到类似问题&#xff0c;就试着找了一下。 这是有人问过 https://vimeo.com/ 这个网站的视频怎么下载。…

Epson打印机连接wifi

环境 Epson L3153 打印机联通无线光猫 背景 最近家里的联通宽带不太稳定&#xff0c;经常断网。今天打了联通客服电话&#xff0c;师傅上门来&#xff0c;说可能是光猫用的时间太长了&#xff0c;换了一个新的联通光猫&#xff0c;问题解决。 wifi的名称是 CU_Y3ft 和 CU_Y3…

[vue] vue使用v-for遍历对象时,是按什么顺序遍历的?如何保证顺序?

[vue] vue使用v-for遍历对象时&#xff0c;是按什么顺序遍历的&#xff1f;如何保证顺序&#xff1f; image 1、会先判断是否有iterator接口&#xff0c;如果有循环执行next()方法 2、没有iterator的情况下&#xff0c;会调用Object.keys()方法&#xff0c;在不同浏览器中&…

一个下载Google code源码的 绿色、迷你工具 MiniSVN v1.0

一个下载Google code源码的 绿色、迷你工具 MiniSVN v1.0 想下载Google code中的源码 不需要装各种软件&#xff0c;只需要一个小工具即可&#xff01; 如何使用&#xff1f; 访问一个谷歌开源项目网站&#xff0c;比如&#xff1a;https://code.google.com/p/deguang-ticke…

Visual.Assist.X.V10.7.1940的汉化破解补丁

Visual Assist X V10.7.1940的汉化破解补丁&#xff0c;方便各位朋友。 从该版本开始&#xff0c;本博客会跟踪升级&#xff0c;请留意。 关于菜单的汉化请看&#xff1a;http://blog.csdn.net/afu45/article/details/9145293 汉化方法 先安装英文原版软件。下载地址&#…

此时无足够的可用内存,无法满足操作的预期要求,可能是由于虚拟地址随便造成的。请稍候重试。 .

vs提示“此时无足够的可用内存&#xff0c;无法满足操作的预期要求&#xff0c;可能是由于虚拟地址随便造成的。请稍候重试。” 下载下面的补丁就可以了 . 下载地址&#xff1a; http://download.csdn.net/detail/afu45/4053280

Visual.Assist.X 菜单汉化

在Visual Studio 2010下 使用 Visual.Assist.X.V10.7.1940汉化破解补丁 之后&#xff0c; 细心的朋友会发现&#xff1a;Visual Studio的菜单内还是英文的。其实这个也是可以汉化的&#xff0c;方法如下&#xff1a; 在Microsoft Visual Studio的菜单上右键单击 -》 选择最后…

金士顿 8G u盘 红色 量产记录

找了好多教程&#xff0c;只有这个成功了&#xff0c;记录下来 逻辑盘符 : H:\ 此分区容量: 7.1G 设备ID : VID 0951 PID 1642 设备序列号: 001CC0EC348CBBB170000294 设备版本 : PMAP 设备制造商: Kingston 设备型号 : DT 101 G2 当…

(十九)hashlib模块

hashlib模块用于加密相关的操作&#xff0c;3.x里代替了md5模块和sha模块&#xff0c;主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 &#xff0c;MD5 算法 注意&#xff1a;md5和sha256算法都是单向加密&#xff0c;即明文加密为密文后&#xff0c;密文无法再解密回明文 适合…

2019 CCPC-Wannafly Winter Camp Div2 Day1

补题进度 7/11 J 夺宝奇兵 范围较小&#xff0c;直接枚举靠多少票赢即可&#xff0c;不够的票从小到大买 #include<bits/stdc.h> #define ll long long const int maxn 1005; using namespace std; struct node {ll id,val;int pos; }p[maxn],P[maxn]; bool vis[maxn]; …

在Airtest中如何使用无线模式控制手机

在Airtest中如何使用无线模式控制手机 在使用Airtest超快速开发App爬虫文章的最后&#xff0c;我们留了一个尾巴&#xff1a;如何启动Airtest的无线模式&#xff0c;不用USB线就能控制手机&#xff1f; 本文将会讲到具体的做法。做法分为两种&#xff1a;第一种是在Airtest的ID…

tab页

图片&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title></title> <style type"text/css"> /* 上边tab */ html {font-size: 14px }body {margin: 0;padding:…

JMeter 性能测试实例

一、性能测试分类&#xff1a; 1、基准测试 2、并发测试 3、负载测试 4、压力测试 1、基准测试&#xff1a; 也是单用户测试&#xff0c;测试环境确定以后&#xff0c;对业务模型中的重要业务做单独的测试&#xff0c;获取单用户运行时的各项性能指标&#xff0c;为多用户并发测…

Django学习之十一:真正理解Django的路由分发和反解url原理

目录 URL Dispatcher简介模式概念对比URLPattern 与 URLResolver (多态的体现)构建子路由几种方式反解url算法逻辑URL Dispatcher 简介 django的url dispatcher 设计是基于一个url mapper来工作的。 这个url mapper主要用在两个方向&#xff1a; url 匹配到 视图通过提供的标识…

Unable to locate tools.jar

初使用ant的时候&#xff0c;打开cmd&#xff0c;使用ant -version查看ant版本以测试ant是否能正常工作&#xff0c; 我先前是已经将ant的bin目录添加进入环境变量中了&#xff0c;后来运行中报了这么一个错误&#xff1a; 解决办法就是将C:\Program Files (x86)\Java\jdk1.6.…