多年来,我已经构建了许多简单的数据库,并且大多数时候每个表中的记录数量都是几百个。微小的数据库。
最近,我有一张包含约20列的表格。但它的记录增长到了500k。
我注意到查询时间非常慢。 20秒左右可以获取100条记录。所以我决定研究索引,我试图用最简单的术语来理解。
如果你有一个类似我的表,有数十万行,它包含的所有行都是ID列的索引,可以肯定地说,当你需要提高简单查询的速度时,你只需创建一个索引经常用于识别记录的列?
ID 公司名 电子邮件 名字 姓 电话号码 RECORDTYPE
如果我们经常按ID查询记录,我们会在ID列上有一个索引,如果我们对公司列做了同样的事情。
如果我们通过companyName&amp ;;执行查询来识别记录recordtype,我们会为这两列创建一个特定的索引作为聚簇索引吗?或者专门针对这两列的索引。
我试图在这里使用熟悉的概念为自己非常基础,因为我一直在努力理解我在网上阅读的很多文章,因为我的查询似乎永远需要这是一种提高查询速度的常用方法简单的表结构?
答案 0 :(得分:1)
Layman的条款:将索引与街道目录背面的索引相同。有两种方法可以在街道目录中查找地址。第一种方法是从网格A1中的地图1开始,并在地图上搜索每个网格,直到最后一段时间后,您将在某个具有地址的页面上遇到网格(假设您很勤奋。)。或者,您可以按目的地按街道名称按字母顺序查找目录后面的地址参考,并引用页面和网格参考编号。第一种方法将平均进行n / 2次搜索以找到位置(其中n是街道参考的数量 - 认为行)。使用索引将使二进制拆分或其他技术能够非常快速地在记录中进行归零。
与索引进行权衡。它们存储了额外的空间,并且在保存记录时会有开销。所以,如果你有很多写作,他们可以减慢你的速度。聚簇索引避免了额外的空间和额外的查找步骤,因为实际数据是按索引定义的顺序存储的。
答案 1 :(得分: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'
;
我们正在尝试查找与特定state
和cust_name
匹配的行。
数据库引擎可以使用索引:
cust_name
和state
(或state
和cust_name
); state
;或cust_name
。如果有一个索引,其中我们要查找的所有列都是索引列(没有列我们不寻找列在最后一列之前我们正在寻找),然后SQL Server可能会使用该索引来查找有问题的记录。
为什么索引中列的顺序很重要?因为那是如何存储索引中的数据的。如果state
和cust_name
上有索引,那么我们会找到第state
=' OH&#39 ;;然后,在' OH'行,我们找到cust_name
=' John Smith'的第一行。我们知道从state
或cust_name
更改后的所有行都有效。
如果索引在state
,city
和cust_name
上,那么我们可以找到state
=' OH&#39 ;;然而,找到cust_name
=' John Smith'我会找到第一个约翰史密斯'对于当前的城市(比如,'阿克伦')。在辛辛那提'克利夫兰'哥伦布'代顿'可能会有更多的约翰史密斯在'等;);我们必须查看整个城市列表才能找到所有城市。
SQL Server可以在搜索中使用两个单独的索引;但是,它必须单独使用它们。我们假设我们有一个以state
开头的索引,以及一个以cust_name
开头的索引。要使用它们来查找记录,SQL必须使用state
=' OH'来构建所有行的列表。来自state
索引; cust_name
=' John Smith'所有行的列表从cust_name
索引,然后确定两个列表中的哪些行。
在决定是否使用索引时,SQL Server会将统计信息视为其表。例如,如果它知道每个可能state
只标识少量行(具有高度基数),并且每个唯一cust_name
标识一个小数字state
对于行,可能值得生成两个列表,并将它们匹配起来。但是,如果表格中有100,000行,cust_name
只有两个不同的值,则更有可能根据state
找到可能的匹配项,然后检查它们以查看如果他们碰巧处于正确的状态; customer
=' OH'行的列表太长了,值得一试。
在尝试查找记录时,可以以其他方式使用索引。在上面的查询中,如果column LIKE 'S%'
表中有50个其他列,并且有一个索引将查询的任何部分中的所有列都作为索引列或包含列,那么所有查询所需的信息存在于该索引中的 。它甚至可以生成查询的结果集,甚至无需查看表。这称为覆盖索引。
请注意,不等式搜索(在范围上或使用column LIKE '%Smith'
)仍然可以使用索引,但只能使用索引中应用范围的第一列。
也不是可以使用索引来搜索某些条件:CAST(datestr as datetime) < '2017-12-21 14:00'
,或者不直接使用该列的条件,例如id
。
我已经注意到上面聚集索引的成本。根据索引的列,每个插入表中的内容可能或多或少地要求引擎在数据页上分成两个,以便容纳新行。同样,如果可以更改索引列,则更新可能会导致行从表中的一个点移动到另一个点。这可能导致索引/表被分段;在页面上存储的信息少于页面可以容纳的信息,因此需要将更多页面读入内存以响应查询。
非聚集索引的成本可能更高。每个非聚集索引都可以作为基础表的部分副本。当添加或删除一行时,需要更改表中的所有索引;更新行时,需要更改每个索引 。如果表上有15个非聚集索引,则每个插入或删除基本上都是更新16个表,而不是一个。
此外,每个非聚集索引都必须存储在磁盘上。在表上有15个索引会增加磁盘空间消耗:可能是15%,可能是1000%(取决于索引)。
由于这些因素,因为查询速度慢而抛出另一个索引并不总是符合您的最佳利益。在某些时候,太多的索引会使插入,更新和删除变慢,并且可能会占用太多的磁盘空间。
如果您经常在companyName
或companyName
上执行搜索,则可能需要在每列上添加索引。
如果经常搜索recordType
和companyName
,那么这两列是列出的前两列的索引可能会有助于提高性能。如果索引位于recordType
,email
和companyName
上,则在搜索所有这三个字段时可能会有所帮助;适用于recordType
和companyName
,或仅适用于recordType
。在没有companyName
的情况下搜索email
或在没有companyName
和recordType
的情况下搜索companyName
无效。
两个索引,一个在recordType
上(或者不在recordType
后面),一个在companyName
上(相同的限制为{{1}})可以< / em> help,如果两个索引都具有高基数。否则,数据库可能只会使用任何一个应该减少总记录的数据。