Hibernate SQL In子句使CPU使用率达到100%

时间:2015-04-23 05:02:13

标签: java sql-server performance hibernate ejb

在我的java应用程序中,我正在使用SQL server and Hibernate3 with EJB。当我尝试执行选择查询with In clause时,DB服务器CPU使用率达到100%。但是当我尝试在SQL management studio中运行相同的查询时,查询运行时没有任何CPU峰值。应用服务器和DB服务器是两台不同的机器。我的表有以下架构,

CREATE TABLE student_table (
       Student_Id BIGINT NOT NULL IDENTITY
     , Class_Id BIGINT NOT NULL
     , Student_First_Name VARCHAR(100) NOT NULL
     , Student_Last_Name VARCHAR(100)
     , Roll_No VARCHAR(100) NOT NULL
     , PRIMARY KEY (Student_Id)
     , CONSTRAINT UK_StudentUnique_1 UNIQUE  (Class_Id, Roll_No)
);

该表包含大约1000万条记录。我的问题是

select Student_Id from student_table where Roll_No in ('A101','A102','A103',.....'A250');

In子句包含250个值,当我尝试在SQL管理工作室中运行上面的查询时,结果在1秒内被检索到没有任何CPU峰值。但是当我试图通过休眠运行相同的查询时,CPU峰值达到100%大约60秒,并且结果在大约60秒内被检索到。 hibernate查询是

Criteria studentCriteria = session.createCriteria(StudentTO.class);
studentCriteria.add(Restrictions.in("rollNo", rollNoLists)); //rollNoLists is an Arraylist contains 250 Strings
studentCriteria.setProjection(Projections.projectionList().add(Projections.property("studentId")));
List<Long> studentIds = new ArrayList<Long>();
List<Long> results = (ArrayList<Long>) studentCriteria.list();
if (results != null && results.size() > 0) {
   studentIds.addAll(results);
}
return studentIds;

问题是什么原因造成的。如果通过管理工作室运行相同的查询,则检索结果时不会出现任何峰值,并在1秒内检索结果。任何解决方案???

EDIT1: 我的hibernate生成的查询是,

select this_.Student_Id as y0_ from student_table this_ where this_.Roll_No in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

EDIT2: 我的执行计划 这是在索引roll_no

之后
CREATE INDEX i_student_roll_no ON student_table (Roll_No) 

My execution plan

7 个答案:

答案 0 :(得分:6)

您从控制台运行的查询很容易缓存,这就是响应瞬间的原因。如果查看查询,您会看到所有参数都嵌入在查询中,因此查询计划程序可以检测到没有变化,所有执行将始终转到同一计划和相同的缓存结果

使用Hibernate运行的查询,即使它是本机查询,它使用PreparedStatement并且参数在查询执行时和to quote one of the best author on indexing绑定:

  

与绑定参数有什么关系?

     

DB2,Oracle和SQL Server的共享执行计划高速缓存使用文字SQL字符串的哈希值作为高速缓存的键。缓存   如果SQL包含随之变化的文字值,则找不到计划   每次执行。

     

占位符(绑定参数)统一语句,以便在使用不同的值执行时SQL字符串相同 - 因此,   提高缓存命中率。

要解决此问题,您需要在(Roll_NoStudent_Id)列上添加索引,以便查询成为仅索引扫描。

SQL Server默认为cluster indexes,这限制了每个表的一个聚簇索引,因此您可能希望turn this table into a heap table instead并专注于仅索引扫描。

答案 1 :(得分:6)

要回答“为什么它通过休眠很慢”的问题,您需要查看运行hibernate代码时服务器使用的实际执行计划,而不是服务器在运行查询时使用的执行计划。 SSMS。包含在问题中的执行计划的屏幕截图与运行hibernate代码时的实际计划不同。一旦你有了这个计划,你可以将它与你从SSMS获得的计划进行比较,差异将很可能解释为什么它在一个案例中很慢而在另一个案例中很快。

Erland Sommarskog有一篇非常好的文章,专注于所谓的“参数嗅探”,这可能是这里出现问题的原因,但不太可能。这篇文章对我们有用的是他解释了如何extract execution plan从缓存中进行检查。

如果没有这些信息,您只能猜测。一个猜测是您将参数传递为nvarchar,但索引字段Roll_Novarchar,因此不使用索引。服务器会将您的varchar列转换为nvarchar进行比较,这意味着无法使用索引=&gt;它很慢,转换可能是高CPU使用率的原因。 http://sqlperformance.com/2013/04/t-sql-queries/implicit-conversion-costs https://www.sqlskills.com/blogs/jonathan/implicit-conversions-that-cause-index-scans/

这不是您的问题的答案,而是问题的可能解决方案。而不是将250个单独的参数传递给IN子句的查询,而是使用table-valued parameter并将您的值作为表传递。在查询中使用JOIN而不是IN。在您发表评论之后,您将拥有100K参数(这意味着您要运行400次查询),这一点尤为明显。事实上,即使对于表值参数,100K也有点太多了,所以我考虑使用一个永久或临时的帮助表,它将保持这些Roll_No 具有正确的索引。主要问题是JOIN。像这样:

CREATE TABLE RollNumbers (
     Roll_No VARCHAR(100) NOT NULL
     ,PRIMARY KEY (Roll_No)
);

确保RollNumbers上的表格Roll_No中有索引。确保student_table上的表Roll_No中有索引。首先将INSERT 100K值转换为RollNumbers,然后在主查询中使用它们:

SELECT Student_Id 
FROM
    student_table
    INNER JOIN RollNumbers ON RollNumbers.Roll_No = student_table.Roll_No

根据整个系统,RollNumbers表可以是永久表,临时表或表变量。

答案 2 :(得分:4)

检查查询中使用的所有字段的休眠级别的数据类型,并确保它与您的表定义匹配。像hibernate这样的框架使用Unicode支持的数据类型(例如nvarchar)。尝试更改任何一方的数据类型。

或者,您可以在连接字符串中添加名为sendStringParametersAsUnicode的参数。它将强制hibernate使用varchar而不是nvarchar。

试一试,让我们知道!

答案 3 :(得分:4)

你可能会想到这一点,因为你的慢查询需要60秒,你的快速&#34;查询需要1秒实际上很快。不是这种情况。这种执行速度差异使您无法理解这里的实际问题。

另一个问题(可能不是实际问题)

如果您在Roll_No上有索引,那么您正在运行的非常简单的查询类型应该在不到一毫秒的时间内返回结果,无论您是否使用绑定变量或内联值。

我只是假设你没有任何索引与你表中约束产生的索引分开。那么,你应该在Roll_No上添加一个简单的索引:

CREATE INDEX i_student_roll_no ON student_table (Roll_No);

或者您可以在上面的索引中添加一个额外的列,以使其成为&#34;覆盖索引&#34;对于此查询(as explained by Vlad

CREATE INDEX i_student_roll_no2 ON student_table (Roll_No, Student_Id);

这会使这个特定查询更快,因为执行计划不需要再次访问磁盘以从表中获取Student_Id。信息已经包含在索引中。但是,请谨慎使用覆盖索引:

  1. 占用更多空间,特别是像您这样的中等大小的桌子
  2. 只要您的查询仅限于真正涵盖的列,就可以很好地工作,这在您的情况下不太可能保持这种状态。
  3. 如何使用SQL Server Management Studio识别它?

    在SQL Server Management Studio中实际上是一个非常好的功能。当您打开执行计划(您应该)时,您将获得有关查询的其他信息:

    SQL Server Management Studio execution plans

    右键单击该信息,然后选择&#34;缺少索引详细信息...&#34;获得类似于这个的信息:

    /*
    Missing Index Details from SQLQuery1.sql - 
      LUKAS-ENVY\SQLEXPRESS.test (LUKAS-ENVY\Lukas (52))
    The Query Processor estimates that implementing the 
      following index could improve the query cost by 87.5035%.
    */
    
    /*
    USE [test]
    GO
    CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
    ON [dbo].[student_table] ([Roll_No])
    INCLUDE ([Student_Id])
    GO
    */
    

    为什么SQL Server Mgmt Studio与Hibernate之间存在差异?

    您的原始问题尚未得到解答。也许,额外的索引修复它,但也许你没有提供所有的信息。你可以:

    • 绑定可变偷看问题
    • Hibernate中的N + 1个问题(包含那么多行)

答案 4 :(得分:1)

当执行查询不是来自hibernate时,看起来好像没有所有记录,但是在你的代码中,查询执行中的所有记录都被放入集合中。

答案 5 :(得分:1)

通过运行SQL事件探查器来确定问题非常容易。在两种情况下,您将确切地看到正在对数据库执行的SQL语句。

http://www.codeproject.com/Articles/21371/SQL-Server-Profiler-Step-by-Step

答案 6 :(得分:1)

我只是从this post

中指出LBushkin的这部分内容
  

其次,当使用IN或OR时,可变数量为   参数,您导致数据库必须重新解析查询   并在每次参数更改时重建执行计划。建造   查询的执行计划可能是一个昂贵的步骤。最   数据库缓存它们使用的运行查询的执行计划   EXACT查询文本作为键。如果您执行类似的查询,但使用   谓词中的不同参数值 - 您很有可能   导致数据库花费大量时间进行解析和   建立执行计划。这就是绑定变量强烈的原因   建议作为确保最佳查询性能的一种方法。

因此,您可以尝试绑定变量以防止每次都运行执行计划

Bind Variables Usage (Parameterized Queries in SQL Server)