外行人的数据库索引

问题描述 投票:-1回答:2

多年来,我已经构建了许多简单的数据库,并且大多数时候每个表中的记录数量都是几百个。微小的数据库。

最近,我有一个包含大约20列的表。但它的记录增长到了500k。

我注意到查询时间非常慢。 20秒左右可以获取100条记录。所以我决定研究索引,我试图用最简单的术语来理解。

如果你有一个类似我的表,有成千上万的行,它包含的所有行都是ID列的索引,可以肯定地说,当你需要提高简单查询的速度时,你只需在列上创建一个索引即可经常用于识别记录?

ID companyName email firstname lastname phonenumber recordtype

如果我们经常通过ID查询记录,那么如果我们对公司列执行相同操作,我们将在ID列上有一个索引。

如果我们通过companyName和recordtype确定了记录的查询,我们会为这两列创建一个特定的索引作为聚簇索引吗?或者专门针对这两列的索引。

我试图在这里使用熟悉的概念为自己非常基础,因为我一直在努力理解我在网上阅读的很多文章,因为我的查询似乎永远需要这是一种提高简单表结构查询速度的常用方法?

sql sql-server indexing
2个回答
1
投票

Layman的条款:将索引与街道目录背面的索引相同。有两种方法可以在街道目录中查找地址。第一种方法是从网格A1中的地图1开始,并在地图上搜索每个网格,直到最后一段时间后,您将在某个具有地址的页面上遇到网格(假设您很勤奋。)。或者,您可以按目的地按街道名称按字母顺序查找目录后面的地址参考,并引用页面和网格参考编号。第一种方法将平均进行n / 2次搜索以找到位置(其中n是街道参考的数量 - 认为行)。使用索引将使二进制拆分或其他技术能够非常快速地在记录中进行归零。

与指数进行权衡。它们存储了额外的空间,并且在保存记录时会有开销。所以,如果你有很多写作,他们可以减慢你的速度。聚簇索引避免了额外的空间和额外的查找步骤,因为实际数据是按索引定义的顺序存储的。


1
投票

由于您将问题标记为sql-server,我将从该框架中回答。其他DBMS应该类似地工作。

群集与非群集

在SQL Server中,首先要理解的是聚簇索引和非聚簇索引之间的区别。

非聚集索引基本上从索引列中获取数据,按指定对它们进行排序(按列升序或降序),并包含指向数据引用的实际表行的指针。在SQL Server中,您可以包含实际未编入索引的列;这些列不用于对数据进行排序,而是与指向行的指针一起存储。这些索引与表本身是分开的,因此从表中复制数据。

聚簇索引不与表分开;它定义了表中数据的组织方式。如果表具有聚簇索引,则数据将按索引指定的顺序存储。

当基础表具有聚簇索引时,任何非聚集索引都将使用聚簇索引中的列作为指向每一行的指针。这意味着这些列会自动包含在每个非聚集索引中。

聚簇索引会影响表中的插入。每个插入必须在索引列确定的正确位置。如果表在IDENTITY列上建立索引,则每个新行将在最后一行之后,并且所有新行都将添加到表的末尾。另一方面,如果数据是(例如)客户名称的索引,那么每行可能需要写入不同的位置;这可能会导致页面拆分,因为数据库必须为表分配新页面,并定义它与其他页面的匹配方式,所以需要更长的时间。

如何使用索引

数据库通常使用索引来查找与特定数据集匹配的行。在以下查询中:

SELECT cust_id, cust_name, address, city, state, zip, phone
  FROM customer
 WHERE cust_name = 'John Smith'
   AND state = 'OH'
;

我们试图找到与特定statecust_name匹配的行。

数据库引擎可以使用索引,其中:

  • 索引中的前两列是cust_namestate(或statecust_name);
  • 索引中的第一列是state;要么
  • 索引中的第一列是cust_name

如果有一个索引,我们要查找的所有列都是索引列(没有列,我们不想在我们要查找的最后一列之前列出),那么SQL Server可能会使用该索引来查找有问题的记录。

为什么索引中列的顺序很重要?因为这就是索引中数据的存储方式。如果在statecust_name上有一个索引,那么我们找到state ='OH'的第一行;然后,在'OH'行中,我们找到cust_name ='John Smith'的第一行。我们知道从那里到statecust_name变化的所有行都是有效的考虑因素。

如果指数在statecitycust_name上,那么我们可以找到state ='OH'的第一行;然而,从那里找到第一排cust_name ='John Smith'就会找到当前城市的第一个'John Smith'(比如'Akron')。 '辛辛那提','克利夫兰','哥伦布','代顿'等可能会有更多'约翰史密斯';我们必须检查整个城市列表才能找到所有城市。

SQL Server可以在搜索中使用两个单独的索引;但是,它必须单独使用它们。假设我们有一个以state开头的索引,以及一个以cust_name开头的索引。要使用它们来查找记录,SQL必须使用state索引中的state ='OH'构建所有行的列表;使用cust_name索引中cust_name ='John Smith'的所有行的列表,然后确定两个列中的哪些行。

在决定是否使用索引时,SQL Server会将统计信息视为其表。例如,如果它知道每个可能的state只识别少量行(具有高度基数),并且每个唯一的cust_name识别少量行,那么生成这两个列表可能是值得的,并且匹配他们了但是,如果表中有100,000行,而state只有两个不同的值,则更有可能根据cust_name找到可能的匹配项,然后检查它们是否恰好处于正确的状态; state ='OH'的行列表太长,值得一试。

在尝试查找记录时,可以以其他方式使用索引。在上面的查询中,如果customer表中有50个其他列,并且有一个索引将查询的任何部分中的所有列都作为索引列或包含列,那么查询所需的所有信息都存在于指数。它甚至可以在不查看表的情况下生成查询的结果集。这称为覆盖指数。

请注意,非等式搜索(在范围上或使用column LIKE 'S%')仍然可以使用索引,但仅限于应用范围的索引中的第一列。

也不是可以使用索引来搜索某些条件:column LIKE '%Smith',或者没有直接使用列的标准,例如CAST(datestr as datetime) < '2017-12-21 14:00'

索引的成本

我已经注意到上面聚集索引的成本。根据索引的列,每个插入表中的内容可能或多或少地要求引擎在数据页上分成两个,以便容纳新行。同样,如果可以更改索引列,则更新可能会导致行从表中的一个点移动到另一个点。这可能导致索引/表被分段;在页面上存储的信息少于页面可以容纳的信息,因此需要将更多页面读入内存以响应查询。

非聚集索引的成本可能更高。每个非聚集索引都可以作为基础表的部分副本。当添加或删除一行时,需要更改表中的所有索引;更新行时,可能需要更改每个索引。如果表上有15个非聚簇索引,则每次插入或删除基本上都会更新16个表,而不是一个。

此外,每个非聚集索引都必须存储在磁盘上。在表上有15个索引会增加磁盘空间消耗:可能增加15%,可能增加1000%(取决于索引)。

由于这些因素,抛出另一个索引并不总是符合您的最佳利益,因为查询很慢。在某些时候,太多的索引会使插入,更新和删除变慢,并且可能会占用太多的磁盘空间。

你的具体例子

如果您经常在idcompanyName上执行搜索,那么您可能希望在每列上都有索引。

如果经常搜索companyNamerecordType,那么这两列是列出的前两列的索引可能有助于提高性能。如果索引在companyNamerecordTypeemail上,那么在搜索所有这三个字段时它会有所帮助;对于companyNamerecordType,或者,仅适用于companyName。没有recordType或没有companyNameemailcompanyName,它将无助于搜索recordType

如果两个指数都具有较高的基数,那么两个指数,一个单独在companyName上(或者不立即跟随recordType),一个在recordType上(companyName相同的限制)可能会有所帮助。否则,数据库可能只使用应该减少总记录的数量。

© www.soinside.com 2019 - 2024. All rights reserved.