使用索引列观察到的 SQL 查询性能之间的差异#
在考虑该级别的索引类型或其他详细信息之前,您是否查看过execution plan 以查找您要比较的查询? “SQL 计划”告诉您您的查询方法是否使用了您为优化而添加的索引,或者它们是否不比原始的、未优化的设计更好。
以下讨论通过几个关键概念来解释原始帖子中解释的观察结果:
- 使用
LIKE 和BETWEEN 子句的查询是否受益于列索引?
- 在所有其他条件相同的情况下,哪一个真的表现更好? (即更快)
预测:所提供示例的记录集看起来非常小。即使有索引并且它正在执行计划中使用,基于堆的表扫描(即,逐个遍历所有记录)和使用索引的计划之间可能没有速度差异以某种方式组织记录。至于问题 (2),query plan caching 上的扩展讨论提出了观察到每个 SQL 运算符的结果之间存在差异的原因。
对原帖中示例的评论:
第二个示例查询根本不涉及列RightPath。
使用索引可能并不总是意味着更快、更有效的查询。索引并不总是 = 更快的查询性能。
初步讨论:如何设置和比较 SQL 查询方法##
我使用 MySQL 数据库来说明一些仍应扩展到 MSSQL RDBMS 环境的概念。将向您证明“慢”与“快”查询响应的关键指标包括很多因素,这些因素最初可以通过查询EXECUTION PLAN 来识别。在某些情况下甚至没有使用索引。
设置我使用的测试环境(在 MySQL 中):
CREATE TABLE Nodes
(
IDNode int auto_increment primary key,
LeftPath varchar(20),
RightPath varchar(30)
);
INSERT INTO Nodes (LeftPath, RightPath)
VALUES
('1', '1Z'),
('1.2', '1.2Z'),
('1.3', '1.3Z'),
('1.2.4', '1.2.4Z'),
('5', '5Z'),
('5.6', '5.6Z');
COMMIT;
CREATE TABLE NodesWIndx
(
IDNode int auto_increment primary key,
LeftPath varchar(20),
RightPath varchar(30)
);
CREATE INDEX NodesIndx_Ix1 ON NodesWIndx(LeftPath);
CREATE INDEX NodesIndx_Ix2 ON NodesWIndx(RightPath);
INSERT INTO NodesWIndx (LeftPath, RightPath)
VALUES
('1', '1Z'),
('1.2', '1.2Z'),
('1.3', '1.3Z'),
('1.2.4', '1.2.4Z'),
('5', '5Z'),
('5.6', '5.6Z');
COMMIT;
在索引列上使用 WHERE 和 LIKE 限制查询表
您的第一个查询是使用您放置在其上的索引。放置在字符串类型列上的非指定索引(例如您的示例)将从左到右工作,如下所示:
-- Querying a Table WITH an Index
SELECT * FROM NodesWIndx WHERE LeftPath LIKE '1%'
| IDNODE | LEFTPATH | RIGHTPATH |
|--------|----------|-----------|
| 1 | 1 | 1Z |
| 2 | 1.2 | 1.2Z |
| 3 | 1.3 | 1.3Z |
| 4 | 1.2.4 | 1.2.4Z |
查询执行计划和索引利用率
请注意,此查询中的计划显示使用表创建的索引NodesIndx_Ix1 用于帮助查找具有与查询条件匹配的LeftPath 列值的记录。
在非索引列上使用 WHERE 和 LIKE 限制查询表
这是针对相似表和数据的相同查询,但过滤列上没有索引:
-- Querying a Table WITHOUT an Index
SELECT * FROM Nodes WHERE LeftPath LIKE '1%'
| IDNODE | LEFTPATH | RIGHTPATH |
|--------|----------|-----------|
| 1 | 1 | 1Z |
| 2 | 1.2 | 1.2Z |
| 3 | 1.3 | 1.3Z |
| 4 | 1.2.4 | 1.2.4Z |
查询执行计划和索引利用率
在这种情况下,计划显示没有使用索引来帮助提供 SQL 查询结果。
在索引列上使用 BETWEEN 查询表
这是针对相似表和数据的相同查询,但过滤列上没有索引:
-- Querying a Table Using BETWEEN with an Index
SELECT * FROM Nodes WHERE LeftPath BETWEEN '1' and '1Z'
| IDNODE | LEFTPATH | RIGHTPATH |
|--------|----------|-----------|
| 1 | 1 | 1Z |
| 2 | 1.2 | 1.2Z |
| 3 | 1.3 | 1.3Z |
| 4 | 1.2.4 | 1.2.4Z |
查询执行计划和索引利用率
带有BETWEEN 子句的查询似乎也使用了为WHERE 条件中使用的列创建的索引。
结论和建议
在使用 LIKE 或 BETWEEN 运算符的查询之间观察到的性能飞跃可能是缓存前一个请求的查询执行计划的结果。
每当尝试执行查询时,查询管道都会查找其查询计划缓存,以查看确切的查询是否已编译并可用。
More on SQL Server Query Plan Caching
至少在 MySQL 示例中更简单的执行计划信息中,两个查询都使用了相同的索引优化(possible_keys 值)以及其他剩余的配置文件值。
索引是否有所作为?
索引并不总能提供可预测的性能改进。 In addition, the type of index created (e.g. In MSSQL: Unique, Clustered and Non-Clustered, etc.) should be chosen appropriately to match the kind of data that is queried (and the distribution of its values) or else the RDBMS will忽略索引。
我在选择索引候选者时发现了a useful discussion on best practices。本文最有用的提示是:
从索引中获得的大多数索引性能改进都体现在数据量更大的情况下。
到底有多大?在Microsoft SQL Server article about best practices 中设置表索引以获得性能提升时,作者仅对DML 和SELECT 运行测试记录集为一百万或更多的操作,以便在性能上产生显着且可测量的差异。
我也许可以用 SQL Server 示例更新一些讨论,但现在,无论您正在查看的 RDBMS 是什么,检查执行计划的概念都保持不变。一些 RDBMS 平台的计划比其他平台更详细,但在分析 SQL 查询以进行优化时,它们引导开发人员朝着相同的方向发展。