Jay*_*ram 19 java sql-server performance hibernate ejb
在我正在使用的java应用程序中SQL server and Hibernate3 with EJB.当我尝试执行select查询时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)
);
Run Code Online (Sandbox Code Playgroud)
该表包含大约1000万条记录.我的疑问是
select Student_Id from student_table where Roll_No in ('A101','A102','A103',.....'A250');
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
问题是什么原因造成的.如果通过管理工作室运行相同的查询,则检索结果时不会出现任何峰值,并在1秒内检索结果.有什么办法吗
Edit1: 我的hibernate生成的查询是,
select this_.Student_Id as y0_ from student_table this_ where this_.Roll_No in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Run Code Online (Sandbox Code Playgroud)
Edit2: 我的执行计划这是在索引roll_no之后
CREATE INDEX i_student_roll_no ON student_table (Roll_No)
Run Code Online (Sandbox Code Playgroud)
,
您从控制台运行的查询很容易缓存,这就是响应瞬间的原因.如果查看查询,您将看到所有参数都嵌入在查询中,因此查询计划程序可以检测到没有变化,并且所有执行将始终转到相同的计划和相同的缓存结果.
使用Hibernate运行的查询,即使它是本机查询,它使用a PreparedStatement和参数在查询执行时绑定并引用索引上最好的作者之一:
那与绑定参数有什么关系?
DB2,Oracle和SQL Server的共享执行计划高速缓存使用文字SQL字符串的哈希值作为高速缓存的键.如果SQL包含随每次执行而变化的文字值,则找不到缓存计划.
占位符(绑定参数)统一语句,以便在使用不同的值执行时SQL字符串相同 - 从而提高缓存命中率.
要解决此问题,您需要在(Roll_No,Student_Id)列上添加索引,以便查询成为仅索引扫描.
SQL Server的默认簇索引,这限制了你以每桌一个聚集索引,所以你可能要关闭此表为heap table,而不是着力仅索引扫描.
要回答"为什么它通过休眠缓慢"的问题,您需要查看运行hibernate代码时服务器使用的实际执行计划,而不是服务器在从SSMS运行查询时使用的执行计划.包含在问题中的执行计划的屏幕截图与运行hibernate代码时的实际计划不同.一旦你有了这个计划,你可以将它与你从SSMS获得的计划进行比较,差异将很可能解释为什么它在一个案例中很慢而在另一个案例中很快.
Erland Sommarskog有一篇非常好的文章,专注于所谓的"参数嗅探",这可能是这里出现问题的原因,但不太可能.这篇文章对我们有用的是他解释了如何从缓存中提取执行计划以供检查.
没有这些信息你只能猜测.一个猜测是你传递的参数为nvarchar,但索引字段Roll_No是varchar,所以不使用索引.服务器将您的varchar列转换nvarchar为比较,这意味着无法使用索引=>它很慢并且转换可能是高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)
);
Run Code Online (Sandbox Code Playgroud)
确保有索引表RollNumbers上Roll_No.确保有索引表student_table上Roll_No.首先将INSERT100K值导入RollNumbers然后在主查询中使用它们:
SELECT Student_Id
FROM
student_table
INNER JOIN RollNumbers ON RollNumbers.Roll_No = student_table.Roll_No
Run Code Online (Sandbox Code Playgroud)
根据整个系统,RollNumbers表可以是永久表,临时表或表变量.
小智 4
检查查询中使用的所有字段的休眠级别的数据类型,并确保它与您的表定义匹配。像 hibernate 这样的框架使用 Unicode 支持的数据类型(例如 nvarchar)。尝试更改任一侧的数据类型。
或者,您可以在连接字符串中添加名为 sendStringParametersAsUnicode 的参数。它将强制 hibernate 使用 varchar 而不是 nvarchar。
请尝试一下并告诉我们!
| 归档时间: |
|
| 查看次数: |
3436 次 |
| 最近记录: |