我最近一直在读关于clustered index和non-clustered index是如何工作的。我的理解很简单(如果错误,请纠正我):
支持clustered和non-clustered index的数据结构是B-Tree。
clustered index:根据索引列(或键)对数据进行物理排序。每个table只能有一个clustered index。如果创建表时没有指定index,SQL服务器将自动在primary key column上创建clustered index。
问题1:由于数据是根据索引进行物理排序的,所以这里不需要额外的空间。这是正确的吗?那么当我删除我创建的索引时会发生什么呢?
non-clustered index:在non-clustered indexes中,树的leaf-node包含列值和指向数据库中实际行的指针(行定位器)。这里需要额外的空间来将这个non-clustered index table物理地存储在磁盘上。但是,一个不受non-clustered Indexes.的数量限制。
问题2:这是否意味着对非聚集索引列的查询不会导致排序数据?
问题3:这里有一个额外的关联查找,可以使用叶节点上的指针定位实际的行数据。与聚集索引相比,这会有多大的性能差异?
练习:
考虑雇员表:
现在我创建了一个Employee表(在Employee上创建了一个默认的聚集索引)。
此表上的两个常见查询仅在"年龄"和"薪资"列上发生。为了简单起见,假设表不经常更新
例如:
号
问题4:构建索引的最佳方法是什么,以便这两个列上的查询具有类似的性能。如果我有关于年龄的聚集索引,年龄列上的查询会更快,但是薪水列上的查询会更慢。
问题5:在一个相关的注释中,我反复看到索引(集群和非集群)应该在具有唯一约束的列上创建。为什么?如果不这样做会发生什么?
非常感谢你我读到的文章如下:
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
群集与非群集
聚集索引和非聚集索引实际上是什么意思?
聚集索引和非聚集索引有什么区别?
数据库索引如何工作?
- 您标记了这个问题mysql,但您的问题意味着您在询问有关Microsoft SQL Server的问题。是哪一个?这两种产品都提供聚集索引和非聚集索引,但内部细节可能略有不同。请澄清,如有必要,请编辑标签。
- @Billkarwin:我不是在问有关Microsoft SQL Server的问题。我希望这是一个普遍的问题。在MySQL和Microsoft之间,索引的内部实现可能有所不同。但我对它如何工作的概念/想法很感兴趣。我不确定问题的哪一部分指定了Microsoft SQL Server。如果是,请编辑它。我是这里的初学者,所以我可能在不知情的情况下互换了术语。谢谢!
我不知道Microsoft SQL Server的内部结构,但我可以回答MySQL,这是您为您的问题标记的。对于其他实现,细节可能有所不同。
Q1。对,聚集索引不需要额外的空间。
如果删除聚集索引会发生什么情况?MySQL的InnoDB引擎总是使用主键(或第一个非空的唯一键)作为聚集索引。如果定义一个没有主键的表,或者删除现有表的主键,InnoDB会为聚集索引生成一个内部人工键。此内部键没有可引用它的逻辑列。
Q2。不保证使用非聚集索引的查询返回的行顺序。实际上,这是访问行的顺序。如果需要按特定顺序返回行,则应在查询中使用ORDER BY。如果优化器可以推断所需的顺序与它访问行的顺序相同(索引顺序,无论是按聚集索引还是非聚集索引),那么它可以跳过排序步骤。
Q3。InnoDB非聚集索引的叶中没有指向相应行的指针,它具有主键的值。因此,在非聚集索引中的查找实际上是两个B树搜索,第一个是查找非聚集索引的叶,然后在聚集索引中进行第二个搜索。
这是单个B树搜索成本的两倍(或多或少),因此InnoDB有一个称为自适应哈希索引的额外功能。频繁搜索的值缓存在ahi中,下次查询搜索缓存值时,它可以执行O(1)查找。在ahi缓存中,它会直接找到指向聚集索引叶的指针,因此它会在一部分时间内消除两个B树搜索。
这可以提高总性能的程度取决于搜索以前搜索过的相同值的频率。根据我的经验,散列搜索与非散列搜索的比率通常为1:2左右。
Q4。构造索引以满足需要优化的查询。通常,聚集索引是主键或唯一键,至少在InnoDB的情况下,这是必需的。age和salary都不可能是唯一的。
你可能真的喜欢我的演示,如何设计索引。
Q5。当您声明一个唯一约束时,InnoDB会自动创建一个索引。如果没有约束的索引,就不能有约束。如果没有索引,插入值时引擎如何确保唯一性?它需要在整个表中搜索该列中的重复值。索引有助于提高唯一性检查的效率。
- 谢谢你的解释。关于问题3:您提到过将进行两次B树搜索,但为了找到正确的行,我将进行三次块读取(每个块可能有10行到100行,具体取决于块大小)。所以在理论上,即使我认为我有主键的ID,我也需要阅读整个块,跳过直到找到感兴趣的ID。这是正确的吗?
- 如果你有YouTube或者你的演讲视频,那就太好了。我在看幻灯片,它们真是太棒了!!
- 对的。例如,InnoDB将所有内容存储在统一大小的页面中(默认为16KB)。一页中可以容纳一定数量的行。但是一旦它将页面加载到内存中,搜索页面的开销就可以忽略不计。从磁盘加载页面的I/O大约要贵10万倍。
- 我的演讲似乎是由Zendcon在2012年的时候录制的。YouTube.com/观看?V=ELR7-RDU9XU
- 谢谢你的视频。很快就会看的。关于Q4和Q5)。如果在我的表中,只有主键是唯一的,那么聚集索引只能在pk上工作(这意味着我不能在任何其他列上拥有聚集索引)。是否也适用于非聚集索引)。这看起来是一个严重的限制,因为与select name from employee e where e.name="Mike"相比,select name from Employee e where e.id =434534这样的事实查询是罕见的(没有用处)。
- 如果您想利用聚集索引的性能优势,那么可以使用name作为主键。或者像(name, emp_id)这样的复合键。主键不必是单列,也不必是整数。或者您可能使用了一个不同的RDBMS,它没有MySQL的限制,而是允许您定义一个非主键列作为聚集索引。
- 让我们在聊天中继续讨论。
- 所以假设我有一个以自动递增整数作为主键的表,一个名称(它不是唯一的),以及一个与之对应的值。我需要按非唯一名称字符串(第2列)搜索表。如果在此列上形成非聚集索引,按此列搜索会更快吗?如果是,费用是多少?如果不是这样的话,那我该如何使它更快呢?一些信息-与主键不同,添加的进一步数据将具有不总是递增的第二列名称。
- @在attitudemonger中,按索引搜索比不按索引搜索要快。这样做的好处是表中的行越多。如果数据增加或不增加,则没有区别,DBMS软件负责按排序顺序插入索引。
- 谢谢比尔。另一个问题-假设同一个表没有主键。有两列-一列是序列号,一列是URL。序列号可以重复,但URL不会。我将需要同时按序列号(如获取给定序列号的所有URL)和URL(如果有URL,请查找)进行搜索。那么,为这个表创建两个索引有意义吗?一个是URL上的唯一索引,另一个是序列号上的非唯一索引?
- @为获得最佳性能,请创建一个包含两列的索引。对于这个答案来说,这是一个偏离主题的问题。去看我的演示"如何设计索引,真的"youtube.com/watch?V=ELR7-RDU9XU
对于SQL Server
如果聚集索引不是唯一的,则仅需要Q1额外空间。SQL Server将在内部向非唯一聚集索引添加一个4字节的uniquifier。这是因为它在非聚集索引中将集群键用作rowid。
非聚集索引可以按顺序读取。这可能有助于您指定订单的查询。它还可能使合并联接具有吸引力。它还可以帮助进行范围查询(x
col)。
当使用非聚集索引时,q3 sql server会执行额外的"书签查找"。但是,只有当它需要一个不在索引中的列时,才需要这样做。还要注意,您可以在索引的叶级中额外添加cx1(0)列。如果一个索引可以在不进行额外查找的情况下使用,则称为覆盖索引。
如果需要查找书签,只需扫描整个聚集索引,就可以更快地完成行的高百分比查找。级别取决于行大小、键大小等,但5%的行是典型的截止值。
Q4如果应用程序中最重要的事情是尽可能快地进行这两个查询,那么可以在这两个查询上创建覆盖索引:
注意,您不必特别包括集群键,因为非集群索引将其作为行指针。
Q5这对于集群密钥比非集群密钥更重要,因为它是唯一的。然而,真正的问题是索引对于您的查询是否是选择性的。想象一个bit值的索引。除非数据的分布非常倾斜,否则这样的索引不太可能用于任何事情。
有关Uniquifier的详细信息。想象一下,你和一个非唯一的年龄聚集指数,和一个非聚集的工资指数。假设您有以下行:
1 2 3
| age | salary | uniqifier
20 | 1000 | 1
20 | 2000 | 2 |
号
然后,工资索引将定位这样的行
1 2
| 1000 -> 20, 1
2000 -> 20, 2 |
假设您运行了查询select * from employee where salary = 1000,优化器选择使用薪资索引。然后它将从索引查找中找到对(20,1),然后在主数据中查找该值。
- 努力帮助这里。你能详细说明一下你对第一季度的观点吗?关于q2)只有在指定ORDER BY的情况下,对非聚集索引的select查询才会产生排序顺序。(从上面回答)。在聚集索引上,默认情况下对其进行排序。所以在非聚集索引的情况下有一个额外的"排序"过程,对吗?所以范围操作(age < 30 and age > 60在非聚集索引中是无效的。请解释一下
- +1很好地举例说明微软与MySQL的区别。索引实现不是通用的。实际上,ansi/iso sql标准根本没有提到索引,所以所有实现都是对sql的供应商扩展!
- 非聚集索引仍然是B树,因此可以按顺序读取索引页。想象一下,如果你想知道有多少人在10到20岁之间。在btree中会找到10,然后按顺序遍历,直到到达20,在经过时计算每一行。
- 问题1)SQL Server添加的唯一性如何帮助定位该行?例如,在上面的示例中,我创建了一个关于年龄的聚集索引(它不是唯一的)。现在,当我执行查询时,select * from employee where age=20;由于有许多20岁的员工,它如何检索所有数据
- 最后,我还提供了一些关于Uniqifier的更多信息。
- 很好的例子。谢谢。当我查询聚集索引(您提供的示例中的年龄)时,select * from employee where age=20。B-树搜索将一直持续到叶节点。现在在叶节点,有两行,年龄=20。两者都会被退回吗?
- 是的,两个都会退还。