数据库索引如何工作?

鉴于索引随着数据集大小的增加而变得如此重要,有人能解释一下索引在数据库无关级别上是如何工作的吗?

有关索引字段的查询的信息,请查看如何索引数据库列

1071987 次浏览

为什么需要它?

当数据存储在基于磁盘的存储设备上时,它以数据块的形式存储。这些块被完整地访问,使它们成为原子磁盘访问操作。磁盘块的结构与链表非常相似;都包含一个数据部分,一个指向下一个节点(或块)位置的指针,并且两者都不需要连续存储。

由于许多记录只能在一个字段上排序,我们可以声明在未排序的字段上搜索需要线性搜索,需要(N+1)/2块访问(平均),其中N是表跨越的块数。如果该字段是非关键字段(即不包含唯一条目),则必须以N块访问搜索整个表空间。

而对于排序字段,可以使用具有log2 N块访问的二进制搜索。此外,由于数据是在给定非键字段的情况下排序的,因此一旦找到更高的值,就不需要搜索表的其余部分以查找重复值。因此性能提升是巨大的。

什么是索引?

索引是一种对多个字段上的多个记录进行排序的方法。在表中的字段上创建索引会创建另一个保存字段值的数据结构,以及指向与之相关的记录的指针。然后对该索引结构进行排序,允许对其执行二进制搜索。

索引的缺点是这些索引需要磁盘上的额外空间,因为索引使用MyISAM引擎一起存储在表中,如果索引同一表中的许多字段,则此文件可以快速达到底层文件系统的大小限制。

它是如何工作的?

首先,让我们概述一个示例数据库表模式;

Field name       Data type      Size on diskid (Primary key) Unsigned INT   4 bytesfirstName        Char(50)       50 byteslastName         Char(50)       50 bytesemailAddress     Char(100)      100 bytes

说明:使用char代替varchar以允许磁盘值的准确大小。此示例数据库包含500万行并且未索引。现在将分析几个查询的性能。这些查询使用id(排序键字段)和一个使用第一个名字(非键未排序字段)的查询。

例1-排序与未排序字段

给定我们的示例数据库,其中包含r = 5,000,000条固定大小的记录,记录长度为R = 204字节,并且它们使用MyISAM引擎存储在一个表中,该引擎使用默认块大小B = 1,024字节。该表的阻塞因子将是每个磁盘块bfr = (B/R) = 1024/204 = 5条记录。保存该表所需的总块数为N = (r/bfr) = 5000000/5 = 1,000,000个块。

在id字段上进行线性搜索需要平均N/2 = 500,000次块访问才能找到一个值,因为id字段是一个键字段。但是由于id字段也被排序,可以进行二分搜索,需要平均log2 1000000 = 19.93 = 20次块访问。立即我们可以看到这是一个巨大的改进。

现在第一个名字字段既不是排序的,也不是键字段,因此二分搜索是不可能的,值也不是唯一的,因此表将需要搜索到最后以获得确切的N = 1,000,000块访问。索引旨在纠正这种情况。

假设索引记录只包含索引字段和指向原始记录的指针,它自然会小于它所指向的多字段记录。因此索引本身需要的磁盘块比原始表少,因此需要更少的块访问来迭代。

Field name       Data type      Size on diskfirstName        Char(50)       50 bytes(record pointer) Special        4 bytes

说明:MySQL中的指针长度为2、3、4或5个字节,具体取决于表的大小。

例2-索引

给定我们的示例数据库r = 5,000,000条记录,索引记录长度为R = 54字节,并使用默认块大小B = 1,024字节。索引的阻塞因子为每个磁盘块bfr = (B/R) = 1024/54 = 18条记录。保存索引所需的块总数为N = (r/bfr) = 5000000/18 = 277,778个块。

现在,使用第一个名字字段的搜索可以利用索引来提高性能。这允许对索引进行二进制搜索,平均log2 277778 = 18.08 = 19块访问。要找到实际记录的地址,需要进一步的块访问才能读取,使总数达到19 + 1 = 20块访问,与在非索引表中找到第一个名字匹配项所需的1,000,000块访问相去甚远。

什么时候应该使用?

鉴于创建索引需要额外的磁盘空间(上面的示例增加了277,778个块,增加了约28%),并且过多的索引会导致文件系统大小限制引起的问题,因此必须仔细考虑选择要索引的正确字段。

由于索引仅用于加速在记录中搜索匹配字段,因此在执行插入或删除操作时,索引仅用于输出的字段只是浪费磁盘空间和流转时长,因此应该避免。此外,考虑到二分搜索的性质,数据的基数或唯一性很重要。对基数为2的字段进行索引会将数据分成两半,而基数为1,000将返回大约1,000条记录。使用如此低的基数,有效性降低为线性排序,如果基数小于记录数的30%,查询优化器将避免使用索引,从而有效地使索引浪费空间。

我第一次读到这篇文章对我很有帮助。谢谢。

从那时起,我对创建索引的缺点有了一些了解:如果你写入一个有一个索引的表(UPDATEINSERT),你实际上在文件系统中有两个写操作。一个用于表数据,另一个用于索引数据(以及它的使用(以及-如果聚集-表数据的使用))。如果表和索引位于同一个硬盘上,这会花费更多的时间。因此,一个没有索引(堆)的表将允许更快的写操作。(如果你有两个索引,你最终会有三个写操作,依此类推)

然而,在两个不同的硬盘上为索引数据和表数据定义两个不同的位置可以减少/消除时间成本增加的问题。这需要根据所需硬盘上的文件定义额外的文件组,并根据需要定义表/索引位置。

索引的另一个问题是随着时间的推移插入数据时它们的碎片。REORGANIZE有帮助,您必须编写例程才能完成。

在某些情况下,堆比带有索引的表更有帮助,

例如:-如果你有很多相媲美的写作,但只有一个晚上阅读下班时间以外的报告。

此外,区分聚集索引和非聚集索引也相当重要。

帮助我:聚集索引和非聚集索引实际上是什么意思?

索引只是一种数据结构,可以更快地搜索数据库中的特定列。这种结构通常是b树或哈希表,但它可以是任何其他逻辑结构。

简单描述!

索引只不过是表中存储特定列的值的数据结构。索引是在表的一列上创建的。

示例:我们有一个名为User的数据库表,它有三列-NameAgeAddress。假设User表有数千行。

现在,假设我们想运行一个查询来查找任何名为“John”的用户的所有详细信息。如果我们运行以下查询:

SELECT * FROM UserWHERE Name = 'John'

数据库软件实际上必须查看User表中的每一行,以查看该行的Name是否为“John”。这将需要很长时间。

这就是index帮助我们的地方:index用于通过从本质上减少表中需要检查的记录/行数来加速搜索查询

如何创建索引:

CREATE INDEX name_indexON User (Name)

index一个表中的列值(例如:John)组成,这些值存储在数据结构中。

所以现在数据库将使用索引来查找名为John的员工因为索引可能会按字母顺序排序用户名。而且,因为它是排序的,这意味着搜索一个名称因为所有以“J”开头的名字都是正确的在索引中彼此相邻!

现在,假设我们想运行一个查询来查找任何名为“Abc”的员工的所有详细信息?

SELECT * FROM EmployeeWHERE Employee_Name = 'Abc'

如果没有索引会发生什么?

数据库软件实际上必须查看员工表中的每一行,看看该行的Employee_Name是否为“Abc”。而且,因为我们希望每一行中的名称都包含“Abc”,所以一旦我们只找到一行名称为“Abc”,我们就不能停止查找,因为可能还有其他行名称为abc。因此,必须搜索直到最后一行的每一行-这意味着数据库必须检查该场景中的数千行才能找到名称为“Abc”的行。这就是所谓的全表扫描

数据库索引如何帮助提高性能

索引的全部意义在于通过减少表中需要检查的记录/行数来加速搜索查询。索引是一种数据结构(最常见的是B树),用于存储表中特定列的值。

B树索引如何工作?

B-树是索引最流行的数据结构的原因是它们具有时间效率-因为查找,删除和插入都可以在对数时间内完成。而且,B-树更常用的另一个主要原因是因为存储在B-树中的数据可以排序。RDBMS通常确定实际用于索引的数据结构。但是,在某些RDBMS的场景中,您实际上可以在创建索引本身时指定希望数据库使用的数据结构。

哈希表索引如何工作?

使用哈希索引的原因是因为哈希表在查找值时非常高效。因此,如果使用哈希索引,比较与字符串相等的查询可以非常快速地检索值。

例如,我们之前讨论的查询可以受益于在Employee_Name列上创建的哈希索引。哈希索引的工作方式是列值将是哈希表的键,映射到该键的实际值只是指向表中行数据的指针。由于哈希表基本上是一个关联数组,典型的条目看起来像“Abc=>0x28939”,其中0x28939是对Abc存储在内存中的表行的引用。在哈希表索引中查找像“Abc”这样的值并返回对内存中该行的引用显然比扫描表以在Employee_Name列中找到值为“Abc”的所有行要快得多。

哈希索引的缺点

哈希表不是排序的数据结构,有许多类型的查询,哈希索引甚至帮不了忙。例如,假设你想找出所有不到40岁的员工。你怎么能用哈希表索引做到这一点?嗯,这是不可能的,因为哈希表只适合查找键值对——这意味着检查相等性的查询

数据库索引中到底有什么?所以,现在你知道数据库索引是在表中的一列上创建的,并且索引将值存储在该特定列中。但是,重要的是要理解数据库索引不会将值存储在同一表的其他列中。例如,如果我们在Employee_Name列上创建索引,这意味着Employee_Age和Employee_Address列值不会也存储在索引中。如果我们只是将所有其他列存储在索引中,那么就像创建整个表的另一个副本一样-这会占用太多空间并且效率非常低。

数据库如何知道何时使用索引?当运行类似于“SELECT*OF雇员WHEREEmployee_Name='Abc'”的查询时,数据库将检查要查询的列上是否有索引。假设Employee_Name列上确实创建了索引,数据库将不得不决定使用索引来查找要搜索的值是否真的有意义-因为在某些情况下,使用数据库索引实际上效率较低,而仅仅扫描整个表更有效。

拥有数据库索引的成本是多少?

它占用空间-表越大,索引越大。索引对性能的另一个影响是,每当您添加、删除或更新相应表中的行时,都必须对索引执行相同的操作。请记住,索引需要包含与索引涵盖的表列中的数据相同的分钟数据。

作为一般规则,仅当索引列中的数据将被频繁查询时,才应在表上创建索引。

另见

  1. 哪些列通常是好的索引?
  2. 数据库索引如何工作

将数据库索引视为一本书的索引。

如果你有一本关于狗的书,你想找到关于德国牧羊犬的信息,你当然可以翻阅这本书的所有页面,找到你想要的东西——但这当然是耗时的,而且不是很快。

另一种选择是,您可以转到本书的索引部分,然后使用您正在寻找的实体的名称(在这种情况下,德国牧羊犬)找到您正在寻找的内容,并查看页码以快速找到您正在寻找的内容。

在数据库中,页码被称为一个指针,它将数据库定向到实体所在磁盘上的地址。使用相同的德国牧羊犬类比,我们可以有这样的东西(“德国牧羊犬”,0x77129),其中0x77129是存储德国牧羊犬行数据的磁盘上的地址。

简而言之,索引是一种数据结构,它存储表中特定列的值,以加快查询搜索。

经典示例“书籍中的索引”

考虑一本1000页的“书”,除以10章,每节100页。

很简单吧?

现在,假设您要查找包含单词“炼金术士”的特定章节。没有索引页,您别无选择,只能扫描整本书/章节。即:1000页。

这个类比在数据库世界中被称为“全表扫描”

在此处输入图片描述

但是有了索引页,你就知道该去哪里了!更重要的是,要查找任何重要的特定章节,你只需要一次又一次地查看索引页。找到匹配的索引后,你可以跳过其余的部分有效地跳转到该章。

但是,除了实际的1000页之外,您还需要大约10页来显示索引,因此总共有1010页。

因此,索引是一个单独的部分,存储索引的值列+按排序顺序指向索引行的指针,以提高效率查找。

学校里的事情很简单,不是吗?