SQL中基于聚集索引和非聚集索引优化查询?
Posted
技术标签:
【中文标题】SQL中基于聚集索引和非聚集索引优化查询?【英文标题】:Optimizing queries based on clustered and non-clustered indexes in SQL? 【发布时间】:2014-11-07 01:06:06 【问题描述】:我最近一直在阅读clustered index
和non-clustered index
的工作原理。我的理解简单来说(如果有错请纠正我):
支持clustered
和non-clustered index
的数据结构是B-Tree
Clustered Index
:根据索引列(或键)对数据进行物理排序。每个table
只能有一个clustered Index
。如果创建表时没有指定index
,SQL
服务器会自动在primary key column
上创建一个clustered Index
。
Q1:由于数据是根据索引进行物理排序的,所以这里不需要额外的空间。它是否正确?那么当我删除我创建的索引时会发生什么?
Non-clustered Index
:在non-clustered indexes
中,树的leaf-node
包含列值和指向数据库中实际行的指针(行定位符)。这里需要额外的空间来将此non-clustered index table
物理存储在磁盘上。但是,一个不受non-clustered Indexes.
数量的限制
Q2:是不是表示对非聚集索引列的查询不会得到排序后的数据?
Q3:这里有一个额外的查找关联,以使用叶节点处的指针定位实际的行数据。与聚集索引相比,这会有多大的性能差异?
练习:
考虑一个 Employee 表:
CREATE TABLE Employee
(
PersonID int PRIMARY KEY,
Name varchar(255),
age int,
salary int
);
现在我创建了一个员工表(创建了员工的默认聚集索引)。
此表的两个频繁查询仅发生在年龄和薪水列上。为了简单起见, 让我们假设该表不经常更新
例如:
select * from employee where age > XXX;
select * from employee where salary > XXXX and salary < YYYY;
Q4 :构建索引的最佳方法是什么,以便对这两个列的查询具有相似的性能。如果我对年龄有聚集索引,年龄列上的查询会更快,但比薪水列上的速度会慢。
Q5:在相关说明中,我反复看到应该在具有唯一约束的列上创建索引(集群和非集群)。这是为什么?如果不这样做会怎样?
非常感谢 我读的帖子在这里:
http://javarevisited.blogspot.com/2013/08/difference-between-clustered-index-and-nonclustered-index-sql-server-database.html
http://msdn.microsoft.com/en-us/library/ms190457.aspx
Clustered vs Non-Clustered
What do Clustered and Non clustered index actually mean?
What are the differences between a clustered and a non-clustered index?
How does database indexing work?
【问题讨论】:
您已标记此问题mysql,但您的问题暗示您询问的是 Microsoft SQL Server。它是哪一个?两种产品都提供聚集索引和非聚集索引,但内部细节可能略有不同。您能否澄清一下,如有必要,请编辑标签? @BillKarwin:我不是在询问 Microsoft SQl 服务器。我希望这是一个普遍的问题。 mysql 和 Microsoft 的索引的内部实现可能不同。但我对它如何工作的概念/想法很感兴趣。如果是这样,我不确定问题的哪一部分指定了 Microsoft SQL 服务器,请编辑它。我是这里的初学者,所以我可能在不知不觉中互换了术语。谢谢! 【参考方案1】:我不了解 Microsoft SQL Server 的内部结构,但我可以回答 MySQL,您已标记为您的问题。其他实现的细节可能会有所不同。
Q1。是的,聚集索引不需要额外的空间。
如果删除聚集索引会发生什么? MySQL 的 InnoDB 引擎总是使用主键(或第一个非空唯一键)作为聚集索引。如果定义没有主键的表,或者删除现有表的主键,InnoDB generates an internal artificial key for the clustered index。此内部键没有可引用它的逻辑列。
Q2.不保证使用非聚集索引的查询返回的行顺序。实际上,它是访问行的顺序。如果您需要按特定顺序返回行,则应在查询中使用 ORDER BY
。如果优化器可以推断出您想要的顺序与它访问行的顺序相同(索引顺序,无论是聚集索引还是非聚集索引),那么它可以跳过排序步骤。
Q3. InnoDB 非聚集索引在索引的叶子上没有指向相应行的指针,它具有主键的值。所以在非聚集索引中查找实际上是两次 B 树搜索,第一次查找非聚集索引的叶子,然后在聚集索引中进行第二次搜索。
这是单个 B 树搜索成本的两倍(或多或少),因此 InnoDB 有一个称为 Adaptive Hash Index 的额外功能。经常搜索的值被缓存在 AHI 中,下次查询搜索缓存的值时,它可以进行 O(1) 查找。在 AHI 缓存中,它直接找到指向聚集索引的叶子的指针,因此它在部分时间消除了 both B 树搜索。
这对整体性能的提升程度取决于您搜索之前搜索过的相同值的频率。根据我的经验,哈希搜索与非哈希搜索的比例通常约为 1:2。
Q4. 构建索引以服务于您需要优化的查询。通常,聚集索引是主键或唯一键,至少在 InnoDB 的情况下,这是必需的。 age
和 salary
都不是唯一的。
你可能会喜欢我的演讲,How to Design Indexes, Really。
Q5. 当你声明一个唯一约束时,InnoDB 会自动创建一个索引。如果没有为它存在的索引,您就不能拥有该约束。如果没有索引,插入值时引擎如何确保唯一性?它需要在整个表中搜索该列中的重复值。该索引有助于提高唯一性检查的效率。
【讨论】:
感谢您的精彩解释。关于 Q3:您提到将进行两次 b 树搜索,但要找到正确的行,我将读取三个块(每个块可能有 10 行到 100 行,具体取决于块大小)。所以理论上,即使我有主键的 ID,我也需要阅读整个块,跳过直到找到感兴趣的 ID。这是正确的吗? 如果您有 youtube 或任何有关您演讲的视频演示,那就太好了。我在看幻灯片,它们太棒了!! 正确。例如,InnoDB 将所有内容存储在统一大小的页面中(默认为 16KB)。一些行数适合一页。但是一旦它将页面加载到内存中,搜索它的开销就可以忽略不计了。从磁盘加载页面的 I/O 大约要贵 100,000 倍。 2012 年我的演讲似乎是 ZendCon 录制的。youtube.com/watch?v=ELR7-RdU9XU @AttitudeMonger,按索引搜索比不按索引搜索要快。表格中的行越多,好处就越大。数据是否增加无关紧要,DBMS 软件会负责按排序顺序插入索引。【参考方案2】:对于 SQL Server
Q1 如果聚集索引不是唯一的,则只需要额外的空间。 SQL Server 将在内部向非唯一聚集索引添加一个 4 字节的唯一标识符。这是因为它在非聚集索引中使用集群键作为 rowid。
Q2可以按顺序读取非聚集索引。这可能有助于您指定订单的查询。它还可能使合并连接具有吸引力。它还有助于范围查询(x col)。
Q3 在使用非聚集索引时,SQL Server 会执行额外的“书签查找”。但是,这仅在它需要不在索引中的列时。另请注意,您可以在叶级索引中include
额外列。如果一个索引可以在没有额外查找的情况下使用,则称为覆盖索引。
如果需要书签查找,它不会占用很高比例的行,直到它更快地扫描整个聚集索引。级别取决于行大小、键大小等。但 5% 的行是典型的截断。
Q4如果您的应用程序中最重要的事情是尽可能快地进行这两个查询,您可以为它们创建覆盖索引:
create index IX_1 on employee (age) include (name, salary);
create index IX_2 on employee (salary) include (name, age);
请注意,您不必特别包含集群键,因为非聚集索引将它作为行指针。
Q5由于唯一性,这对于集群键比非集群键更重要。但真正的问题是索引是否对您的查询具有选择性。想象一下bit
值的索引。除非数据的分布非常倾斜,否则这样的索引不太可能用于任何事情。
有关唯一性的更多信息。想象一下,你和一个关于年龄的非唯一聚集索引和一个关于薪水的非聚集索引。假设您有以下几行:
age | salary | uniqifier
20 | 1000 | 1
20 | 2000 | 2
那么薪水索引会像这样定位行
1000 -> 20, 1
2000 -> 20, 2
假设您运行查询select * from employee where salary = 1000
,优化器选择使用薪水索引。然后它将从索引查找中找到 (20, 1) 对,然后在主数据中查找该值。
【讨论】:
感谢您在这里提供帮助。您能否详细说明您对 Q1 的看法。关于 Q2)仅当我指定ORDER BY
时,对非聚集索引的选择查询才会产生排序顺序。 (从上面所以回答)。在聚集索引上,默认情况下是排序的。所以在非聚集索引的情况下有一个额外的“排序”过程正确吗?因此范围操作 (age < 30 and age > 60
) 在非聚集索引中将无效。请解释
+1 很好地举例说明了 Microsoft 与 MySQL 的不同之处。索引实现不是通用的。事实上,ANSI/ISO SQL 标准根本没有提到索引,所以所有的实现都是对 SQL 的供应商扩展!
非聚集索引仍然是b-tree,所以可以按顺序读取索引页。想象一下,如果您想知道有多少人的年龄在 10 到 20 岁之间。您会在 btree 中找到 10 个,然后依次遍历直到达到 20 个,并在您经过时计算每一行。
Q1) SQL 服务器添加的唯一性如何帮助定位该行?例如,在上面的示例中,我创建了一个关于年龄的聚集索引(它不是唯一的)。现在当我执行查询时,select * from employee where age=20;
因为有很多 20 岁的员工,它如何检索所有数据
我在最后放了一些关于 uniqifier 的更多信息。以上是关于SQL中基于聚集索引和非聚集索引优化查询?的主要内容,如果未能解决你的问题,请参考以下文章
MySQL中怎样创建聚集索引和非聚集索引,求创建这两种索引的SQL语句。谢谢