通俗地说就是数据库索引

use*_*264 3 sql sql-server indexing

多年来,我构建了许多简单的数据库,大多数时候每个表中的记录数只有几百条。小型数据库。

最近,我有一个包含大约 20 列的表。但其记录已增长至 500k。

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

如果您有一个像我这样的表,有数十万行,并且它包含的只是 ID 列上的索引,那么可以肯定地说,当您需要提高简单查询的速度时,您只需在该列上创建一个索引即可是否经常用于识别记录?

ID 公司名称 电子邮件 名字 姓氏 电话号码 记录类型

如果我们经常按 ID 查询记录,那么如果我们对公司列做同样的事情,我们就会在 ID 列上有一个索引。

如果我们执行通过公司名称和记录类型标识记录的查询,我们将为这两列创建一个特定索引作为聚集索引?或者专门针对这两列的索引。

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

RDF*_*ozz 5

既然您将问题标记为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'
;
Run Code Online (Sandbox Code Playgroud)

我们正在尝试查找与特定的state和匹配的行cust_name

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

  • 索引中的前两列是cust_nameand state(或stateand cust_name);
  • 索引中的第一列是state;或者
  • 索引中的第一列是cust_name

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

为什么索引中列的顺序很重要?因为这就是索引中数据的存储方式。state如果和上有索引cust_name,那么我们找到第一行 where state= 'OH'; 然后,在“OH”行中,我们找到第一行,其中cust_name=“John Smith”。我们知道从那里开始直到statecust_name更改的所有行都可供考虑。

如果索引在statecity、 和 上cust_name,那么我们可以找到第一行 where state= 'OH'; 但是,从那里找到第一行 where cust_name= 'John Smith' 只会找到当前城市的第一个 'John Smith'(例如,'Akron')。在“辛辛那提”、“克利夫兰”、“哥伦布”、“代顿”等地可能会有更多“约翰·史密斯”);我们必须检查整个城市列表才能找到所有城市。

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

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

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

请注意,非等式搜索(在范围内或使用column LIKE 'S%')仍然可以使用索引,但最多只能到应用范围的索引中的第一列。

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

索引的成本

我已经在上面指出了聚集索引的成本。根据索引的列,每次插入表中都可能或多或少需要引擎将数据页分成两部分,以便容纳新行。同样,如果可以更改索引列,则更新可能会导致行从表中的一个位置移动到另一个位置。这可能会导致索引/表碎片化;在每个页面上存储的信息少于该页面可以容纳的信息,因此需要将更多页面读入内存以响应查询。

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

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

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

你的具体例子

如果您经常对id或执行搜索companyName,您可能需要在每一列上都有一个索引。

如果您经常搜索companyNamerecordType,那么这两列是列出的前两列的索引可能会提高性能。如果索引位于companyNamerecordType和上email,那么在搜索所有这三个字段时可能会有所帮助;对于companyNamerecordType,或者,仅对于companyNamerecordType如果没有 andcompanyNameemail同时没有companyNameand ,它将无助于搜索recordType

如果两个索引都具有高基数,则两个索引,一个单独 on companyName(或不立即跟随recordType),一个 on recordType(与 rt 相同的限制companyName可能会有所帮助。否则,数据库可能只使用应拉回较少总记录的任一记录。