在这篇文章中,我们将探讨有效的数据库索引策略。数据库性能对任何大规模数据驱动应用都至关重要。设计不佳的索引和缺乏索引是数据库应用瓶颈的主要来源。设计高效的索引对实现良好的数据库和应用性能至关重要。随着数据库规模的增长,找到有效的方法来检索和操作数据变得越来越重要。设计良好的索引策略是实现这种效率的关键。本文深入探讨索引架构,并讨论最佳实践,以帮助我们设计有效的索引来满足应用需求。

让我们从数据库索引的基础开始。索引就像书末的索引一样,是一种加速数据检索操作的数据结构。正如书索引列出关键词和页码以帮助快速定位信息一样,数据库索引也起到类似的作用,加速数据检索而无需扫描数据库表中的每一行。

数据库索引类比

数据库索引的结构包括一个有序的值列表,每个值连接到指向这些数据页所在位置的指针。索引页保存这种组织结构,提供了一种更高效的方式来定位特定信息。

索引通常存储在磁盘上。它们与表关联以加速数据检索。由表中一列或多列组成的键构成索引,对于大多数关系数据库,这些索引存储在 B+ 树结构中。这种结构允许数据库有效地定位关联的行。

为数据库找到合适的索引是在快速查询响应和更新成本之间的平衡。窄索引(列数较少的索引)节省磁盘空间和维护成本,而宽索引适用于更广泛的查询。通常,需要多次迭代设计才能找到最高效的索引。

最简单的形式,索引是一个排序表,允许使用二分查找在排序数据结构上以 O(Log N) 时间复杂度进行搜索。

各种数据结构(如 B 树、位图或哈希表)可用于实现索引。虽然所有这些结构都提供高效的数据访问,但它们的实现细节有所不同。

对于关系数据库,索引通常使用 B+ 树实现,这是 B 树的一种变体。

B+ 树是一种特定类型的树数据结构,理解它需要一些关于其前身 B 树的背景知识。

B 树(平衡树)是一种自平衡树数据结构,它维护排序数据并允许高效的插入、删除和搜索操作。所有这些操作都可以在 O(Log N) 时间内执行。

以下是区分 B 树结构的特点:

  • 所有叶子在同一层级——这就是树”平衡”的原因
  • 所有内部节点(除根节点外)的子节点数量范围从 d(树的最小度)到 2d。然而,根节点至少有两个子节点
  • 有 k 个子节点的非叶子节点包含 k-1 个键。这意味着如果一个节点有三个子节点(k=3),它将保存两个键(k-1),将数据分成与每个子节点对应的三部分

B 树是一种优秀的数据结构,用于存储不适合主内存的数据,因为它的设计最小化了磁盘访问次数。而且因为树是平衡的,所有叶子节点在同一深度,查找时间保持一致且可预测。

B 树结构

B+ 树是 B 树的一种变体,广泛用于基于磁盘的存储系统,特别是数据库索引。B+ 树具有某些独特的特性,改进了 B 树。

  • 在 B+ 树中,数据指针(指向实际记录的指针)这意味着许多键可以存储在内部节点中,减少了树的整体高度。这减少了执行许多操作所需的磁盘访问次数
  • 所有叶子节点链接在一起形成链表。这使得范围查询变得高效。我们可以访问范围的第一个节点,然后简单地跟随链表检索其余部分
  • 在 B+ 树中,每个键出现两次,一次在内部节点,一次在叶子节点。内部节点中的键充当决定所需值可能在哪个子树的分割点

B+ 树结构

这些特性使 B+ 树特别适合具有大量不适合主内存的数据的系统。由于数据只能从叶子节点访问,每次查找都需要从根到叶子的路径遍历。所有数据访问操作花费一致的时间。这种可预测性使 B+ 树成为数据库索引的有吸引力的选择。

B 树 vs B+ 树对比

现在我们了解了 B+ 树如何用于索引,让我们看看典型数据库引擎如何使用它来维护索引。

聚簇索引(Clustered Index)重新排序表中记录的物理存储方式。它不会随机存储行,甚至不会按插入顺序存储。相反,它组织它们以与索引顺序对齐,因此称为”聚簇”。用于排列此顺序的特定列称为聚簇键。

这种排列决定了磁盘上数据的物理顺序。把它想象成按姓氏然后名字排序的电话簿。数据(电话号码和地址)与排序索引一起存储。

然而,因为物理数据行只能按一种顺序排序,一个表只能有一个聚簇索引。添加或修改聚簇索引可能很耗时,因为它需要物理重新排序数据行。

选择聚簇键也很重要。通常,选择唯一的顺序键是有益的,以避免重复条目并最小化插入新数据时的页分裂。这就是为什么在许多数据库中,主键约束会自动在该列上创建聚簇索引(如果没有明确定义其他聚簇索引)。

聚簇索引

然而,这个一般指导有一个例外:PostgreSQL。在 PostgreSQL 中,数据按插入顺序存储,而不是基于聚簇索引或任何其他索引。然而,PostgreSQL 提供 CLUSTER 命令,可用于重新排序表中的物理数据以匹配特定索引。值得注意的是,当数据插入或更新时,这种物理顺序不会自动维护——要维护顺序,需要重新运行 CLUSTER 命令。

非聚簇索引(Non-clustered Index)有点像书末的索引。它们维护一个独立的键值列表,每个键都有一个指针指示包含该值的行的位置。指针将索引条目绑定回数据页。

由于非聚簇索引与数据行分开存储,数据的物理顺序与索引建立的逻辑顺序不同。这种分离意味着使用非聚簇索引访问数据涉及至少两次磁盘读取——一次访问索引,另一次访问数据。这与聚簇索引形成对比,在聚簇索引中索引和数据是一体的。

非聚簇索引的一个主要优势是我们可以在一个表上有多个非聚簇索引,每个索引对不同类型的查询都很有用。它们对涉及不包含在聚簇索引中的列的查询特别有益。它们增强了不涉及聚簇键或不需要扫描数据范围的查询的性能。

考虑权衡很重要。虽然非聚簇索引可以加速读取操作,但它们可能会减慢写入操作,因为每当表中的数据被修改时,每个索引都必须更新。在决定给定表的非聚簇索引的数量和类型时,取得平衡至关重要。

非聚簇索引

索引通过提供更高效的数据访问路径来加速数据检索,而无需扫描每一行。有不同类型的索引。我们来看看常见的类型。

主索引(Primary Index)通常是访问数据的主要手段。创建表时,主键通常兼作聚簇索引,这意味着表中的数据在磁盘上根据此键物理排序。这确保按主键搜索时快速检索数据。

这种设置的效率很大程度上取决于主键的性质。如果键是顺序的,写入表通常是高效的。但如果键不是顺序的,可能需要重新洗牌数据以维护顺序。这会使写入过程效率降低。

注意,虽然主键通常用作聚簇索引,但这不是一成不变的规则。聚簇索引可以基于任何列或列集,不一定是主键。

主索引

本文为学习目的的个人翻译,译文仅供参考。

原文链接:Database Indexing Strategies

版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。