我一直遇到RDBMS用户认为执行查询的一秒钟几乎快的情况。 最近,在此堆栈溢出问题中:
Hibernate SQL In子句使CPU使用率达到100%
发布者最初的问题是,为什么类似的查询在SQL Server Management Studio中执行时在一秒钟内执行,而(看似)同一查询从Hibernate执行时却在60秒内执行。 该查询类似于以下内容:
select Student_Id
from Student_Table
where Roll_No in ('A101','A102','A103',.....'A250');
这种差异可能有很多原因。 最有可能的是,某个地方还隐藏着一个Hibernate N + 1问题 。 但是,这里最引人注目的信息是:
请不要相信1秒很快。
数据库的运行速度非常快,即使在性能中等的服务器上,上述查询也几乎不会立即执行。 即使在笔记本电脑上! Markus Winand明确指出,在所有性能问题的80%中,您要做的就是添加缺失的索引 。 在这里也是如此!
发布者的原始表仅包含两个索引:
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)
);
有一个实现PRIMARY KEY
的索引,还有一个用于UNIQUE
约束的索引,但是这两个索引并不是非常有用,因为查询谓词基于Roll_No
过滤,而Roll_No
只是UNIQUE
约束的第二列。 在大约800万行上执行上述查询时,我对UNIQUE
索引进行了索引扫描,查询在三秒钟内运行:
此“索引扫描”操作根本不好。 我实际上正在遍历所有索引,以在每个索引叶节点中找到所有适用的Roll_No
值。 在“ 使用索引卢克”中有关级联索引的页面中对此进行了很好的解释。
解决方案
但是好消息是,SQL Server Management Studio为您提供了即时调整建议。 只需右键单击执行计划,然后选择“缺少索引详细信息…”即可获得以下建议:
/*
Missing Index Details from SQLQuery1.sql -
LUKAS-ENVY\SQLEXPRESS.test
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>]
ON [dbo].[student_table] ([Roll_No])
INCLUDE ([Student_Id])
GO
*/
这并不一定意味着上面的索引是所有查询的最佳选择,但是您使用Roll_No
上的谓词进行查询的事实应该足以表明您至少应该在该Roll_No
上具有索引柱。 这里最简单的索引就是:
CREATE INDEX i_student_roll_no
ON student_table (Roll_No);
有了该索引,我们现在将获得一个“索引查找”操作,该操作将立即运行:
覆盖指数
在这种特殊情况下, 弗拉德·米哈尔西娅(Vlad Mihalcea)在其回答中建议的“覆盖指数”可能是适当的。 例如:
CREATE INDEX i_student_roll_no
ON student_table (Roll_No, Student_Id);
覆盖索引的优点是查询执行所需的所有信息已包含在索引中 。 在这种特殊情况下,这是正确的,但由于以下情况也可能很危险:
- 覆盖索引需要更多空间,如果表已经很大,则空间可能成为问题
- 只要查询不使用其他列(例如,用于投影,计算,其他过滤,排序等),覆盖索引也只会增加值。 在这个简单的示例中可能不是这种情况,在不久的将来可能会Swift改变
因此,在这种情况下,覆盖索引不应成为您的默认选择。 最好保守一些,只在索引中添加为过滤添加立即值的那些列。
结论
我想一次又一次地重复:
不要以为一秒快
事实上:
不要以为,任何超出2-3ms为快!
除非您要进行繁重的报告或批处理,否则处理时间显然可能需要更长的时间,因此像这样的简单查询绝不会比Instant慢。 在大多数情况下,您可以通过添加索引来达到这种速度。
在旁注
显然,上述问题的发布者还存在其他问题。 以上内容都无法解释Hibernate和“普通SQL”之间的执行速度差异。 但是,这里再次传达的信息是,如果您的“普通SQL”已经花费了超过一秒钟的时间,那么您就可以解决问题。
翻译自: https://www.javacodegeeks.com/2015/04/do-not-think-that-one-second-is-fast-for-query-execution.html