鉴于索引在数据集大小增加时非常重要,有人可以解释索引在数据库无关的级别上的工作原理吗?
有关索引字段的查询的信息,请查看如何索引数据库列 。
为什么需要?
当数据存储在基于磁盘的存储设备上时,它将存储为数据块。这些块完全被访问,使它们成为原子磁盘访问操作。磁盘块的结构与链表的结构大致相同; 两者都包含数据部分,指向下一个节点(或块)位置的指针,并且两者都不需要连续存储。
由于许多记录只能在一个字段上排序,我们可以说在没有排序的字段上搜索需要线性搜索,这需要N/2
块访问(平均),其中N
是表格跨越的块数。如果该字段是非关键字段(即不包含唯一条目),则必须在N
块访问时搜索整个表空间。
而对于排序字段,可以使用二进制搜索,其具有log2 N
块访问。此外,由于在给定非关键字段的情况下对数据进行排序,因此一旦找到更高的值,则不需要搜索表的其余部分以寻找重复值。因此,性能提升很大。
什么是索引?
索引是一种在多个字段上对多个记录进行排序的方法。在表中的字段上创建索引会创建另一个包含字段值的数据结构,以及指向与其相关的记录的指针。然后对该索引结构进行排序,允许对其执行二进制搜索。
索引的缺点是这些索引需要磁盘上的额外空间,因为索引使用 MyISAM 引擎一起存储在表中,如果同一个表中的许多字段被索引,则此文件可以快速达到底层文件系统的大小限制。
它是如何工作的?
首先,让我们概述一个示例数据库表模式;
Field name Data type Size on disk id (Primary key) Unsigned INT 4 bytes firstName Char(50) 50 bytes lastName Char(50) 50 bytes emailAddress Char(100) 100 bytes
注意 :使用 char 代替 varchar 以允许磁盘值的准确大小。此示例数据库包含五百万行并且未编入索引。现在将分析几个查询的性能。这些是使用id (已排序的键字段)的查询和使用firstName (非键未排序字段)的查询。
示例 1 - 排序与未排序的字段
给定我们的r = 5,000,000
个固定大小的记录的样本数据库,给出R = 204
字节的记录长度,并且使用使用默认块大小B = 1,024
字节的 MyISAM 引擎将它们存储在表中。表的阻塞因子是bfr = (B/R) = 1024/204 = 5
每个磁盘块bfr = (B/R) = 1024/204 = 5
记录。保持表所需的块总数是N = (r/bfr) = 5000000/5 = 1,000,000
个块。
假定 id 字段是关键字段,对 id 字段的线性搜索将需要平均N/2 = 500,000
块访问才能找到值。但由于 id 字段也被排序,因此可以进行二进制搜索,需要平均log2 1000000 = 19.93 = 20
块访问。我们可以立即看到这是一个巨大的进步。
现在firstName字段既没有排序也没有键字段,所以二进制搜索是不可能的,值也不是唯一的,因此表格需要搜索到最后的N = 1,000,000
块访问。正是这种情况,索引旨在纠正。
鉴于索引记录仅包含索引字段和指向原始记录的指针,因此它将比它指向的多字段记录小。因此索引本身比原始表需要更少的磁盘块,因此需要更少的块访问来迭代。 firstName字段的索引架构概述如下;
Field name Data type Size on disk firstName 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
每个磁盘块bfr = (B/R) = 1024/54 = 18
记录。保持索引所需的块总数是N = (r/bfr) = 5000000/18 = 277,778
个块。
现在,使用firstName字段进行搜索可以利用索引来提高性能。这允许索引的二进制搜索具有平均log2 277778 = 18.08 = 19
块访问。要查找实际记录的地址,这需要进一步访问块来读取,使总数达到19 + 1 = 20
块访问,与在非索引表中查找firstName匹配所需的 1,000,000 次块访问相差甚远。
什么时候应该使用?
鉴于创建索引需要额外的磁盘空间(上例中额外增加 277,778 个块,增加约 28%),并且太多索引可能导致文件系统大小限制引起的问题,必须仔细考虑选择正确的要索引的字段。
由于索引仅用于加速在记录中搜索匹配字段,因此,仅用于输出的索引字段仅仅是在执行插入或删除操作时浪费磁盘空间和处理时间,因此应该避免。同样考虑到二进制搜索的性质,数据的基数或唯一性很重要。对基数为 2 的字段进行索引会将数据分成两半,而基数为 1,000 则会返回大约 1,000 条记录。如此低的基数,有效性会降低到线性排序,如果基数小于记录数的 30%,查询优化器将避免使用索引,从而有效地使索引浪费空间。
我第一次看到它对我很有帮助。谢谢。
从那以后,我对创建索引的缺点有了一些了解:如果用一个索引写入表( UPDATE
或INSERT
),实际上在文件系统中有两个写操作。一个用于表数据,另一个用于索引数据(以及索引数据(和 - 如果是群集的 - 求助表数据))。如果表和索引位于同一硬盘上,则会花费更多时间。因此,没有索引(堆)的表将允许更快的写操作。 (如果你有两个索引,最终会有三个写操作,依此类推)
但是,在两个不同的硬盘上为索引数据和表数据定义两个不同的位置可以减少 / 消除增加时间成本的问题。这需要使用所需硬盘上的相应文件定义附加文件组,并根据需要定义表 / 索引位置。
索引的另一个问题是随着数据的插入,它们随着时间的推移而碎片化REORGANIZE
帮助,您必须编写例程才能完成它。
在某些情况下,堆比具有索引的表更有用,
例如: - 如果您有很多可以与之相对应的写入,但只能在工作时间之外每晚阅读一次以进行报告。
此外,聚簇索引和非聚簇索引之间的区别是非常重要的。
帮助我: - 群集和非群集索引实际上意味着什么?
索引只是一种数据结构,可以更快地搜索数据库中的特定列。此结构通常是 b 树或哈希表,但它可以是任何其他逻辑结构。
有关更多信息,我建议: 数据库索引如何工作?而且,索引如何帮助?