我遇到了 SQL Server 查询性能问题,希望能从大家那里得到一些见解。
问题陈述:
我有一个 SQL 查询,当直接在 SQL Server Management Studio (SSMS) 中执行时,性能良好(约 6 毫秒),在执行计划中显示“索引查找”。但是,当使用 JDBC 从我的 Java 应用程序执行相同的查询时,执行计划显示“索引扫描”,并且查询的执行时间明显更长(约 1 秒)。
我尝试过的:
- 确保所涉及的表和索引的统计信息是最新的。
- 比较 SSMS 和 Java 应用程序之间的执行上下文设置。
- 将 SQL 添加到 Stored prod 中,然后它工作正常,进行索引查找。
- 删除了计划 ID,清除了缓存。
问题:
- 是什么导致了查询性能的这种差异?
- 是否有我可能错过的调试步骤?
计划何时从 Java 执行此操作:https://www.brentozar.com/pastetheplan/ ?id=SkWWezsya
计划何时从 SSMS 执行此操作:https://www.brentozar.com/pastetheplan/ ?id=HJZPefokp
任何见解或建议将不胜感激。
您问题中的查询计划显示 SSMS 参数数据类型为 ,
varchar(8000)
而 JDBC 参数类型为nvarchar(4000)
。SSMS 计划能够使用高效的查找运算符,因为varchar
参数类型与列类型匹配,但根据数据类型优先规则varchar
将列隐式转换为nvarchar
JDBC 计划中的列会导致不可控制的表达式,需要扫描。我要补充的是,对于 Windows 排序规则来说,隐式转换不会造成太大问题,因为 SQL Server 可以使用GetRangeThroughConvert强制使用范围搜索谓词来强制控制表达式。
您可以通过在连接字符串中
varchar
指定sendStringParametersAsUnicode=false从 JDBC 应用程序传递参数。JDBC 文档的相关摘录:如果还需要传递 Unicode 参数,请使用以下
setN
方法SQLServerPreparedStatement
:关于:
存储过程参数显然被定义为
varchar
。在这种情况下,应用程序提供的参数值在执行过程之前nvarchar
隐式转换为过程定义的varchar
参数。然后,proc 执行计划使用该参数而不进行任何转换,从而允许索引查找。varchar
值得一提的是,具有正确参数类型的存储过程可以帮助缓解客户端应用程序传递的不正确参数类型的问题,因为这些过程是使用 proc 参数定义而不是客户端提供的参数定义进行编译和优化的。
是的,根据 @DanGuzman 和 @DenisRubashkin 的评论,您有一个 nvarchar 参数。
您将不得不强制您的客户端以 varchar 形式发送。
我在 .NET/Entity Framework/LINQ 中经常看到这种情况。它很阴险,因为它在小型开发/测试/UAT 数据集中有效,但性能问题在生产工作负载和数据集中显现出来。
使用 AddWithValue 或类似方法添加的参数就可以了。Native unicode == nvarchar not varchar 并且 ANSI SQL 要求对表值进行转换,而不是对索引进行扫描而不是正确查找的参数:(
Java 计划中的 XML 确认从 VARCHAR 到 NVARCHAR 的隐式转换正在影响计划: