【问题标题】:Index versus Sequential search performance?索引与顺序搜索性能?
【发布时间】:2020-01-25 06:10:50
【问题描述】:

假设我有一个数据库,其中包含有关书籍及其出版日期的信息。 (两个属性,bookName 和 publicationDate)。

假设publicationDate这个属性有一个哈希索引。

如果我想显示 2010 年出版的每一本书,我会输入以下查询:从 Books where publicationDate=2010 中选择 bookName。

在我的讲座中解释说,如果数据量很大,并且出版日期非常多样化,更优化的方法是使用哈希索引,以便只保留2010年出版的书籍。

但是,如果数据库中的绝大多数书籍都是 2010 年出版的,那么就性能而言,最好按顺序搜索数据库。

我真的不明白为什么?哪些情况下使用索引更优化,为什么?

【问题讨论】:

  • 查询越接近需要访问数据库的所有记录,对索引的需求就越少。如果要访问所有记录,则索引的开销是不必要的

标签: sql optimization indexing hash


【解决方案1】:

令人惊讶的是,您在不了解这个概念的情况下学习哈希索引。哈希索引是一个相当先进的数据库概念;大多数数据库甚至都不支持它们。

虽然这个例子很有误导性。 2010 不是日期;这是一年。这很重要,因为哈希索引仅适用于相等比较。所以从日期中获取一年数据的自然方法:

where publicationDate >= date '2010-01-01' and
      publicationDate < date '2011-01-01'

无法使用哈希索引,因为比较不是相等比较。

索引可用于多种用途:

  • 快速确定哪些行符合过滤条件,从而减少需要读取的数据页。
  • 识别具有聚合常用键值的行。
  • 匹配表之间的行以进行连接。
  • 支持唯一约束(通过唯一索引)。
  • 对于 b-tree 索引,支持order by

这是第一个目的,就是减少被读取的数据页的数量。读取数据页并非易事,因为它需要从磁盘中获取。顺序扫描会读取所有数据页,无论它们是否需要。

如果只有一行符合索引条件,则只需要读取一页。这是性能上的一大胜利。但是,如果每个页面都有符合条件的行,那么您正在阅读所有页面无论如何。该索引似乎不太有用。

而且使用索引不是免费的。索引本身需要加载到内存中。在查找操作期间需要对键进行散列和处理。如果您只扫描页面,那么所有这些开销都是不必要的(尽管用于过滤的关键比较还有其他开销)。

【讨论】:

  • 哈希索引是一个相当高级的数据库概念;”我对此很好奇。哈希表是最基本的数据结构之一,它们是在计算机科学的第一年或第二年教授的,也是最简单的索引之一。 MySQL 和 Postgres 都支持它们。
  • 对不起,我的意思是出版年份!这是一个糟糕的翻译,英语不是我的第一语言。
  • @Schwern 。 . . “数据库”“计算机科学”。当您进入哈希冲突、数据倾斜、数据不适合内存或分布式数据库的世界时,它们不是最简单的索引之一。在任何情况下,SQL 数据库中的“默认”索引都是 B 树,基于它们在所有支持索引的数据库中的普遍性。它们更通用,因为它们支持更多的 SQL 操作。
【解决方案2】:

使用索引有性能成本。如果匹配的百分比是整个表的一小部分,则不必扫描整个表就可以弥补这一成本。但是如果匹配的比例很大,那么简单地读取表格会更快。

读取索引是有成本的。一个小的、经常使用的索引可能在内存中,但一个大的或不经常使用的索引可能在磁盘上。这意味着搜索索引和获取匹配行号的磁盘访问速度较慢。如果查询与少量行匹配,则此开销胜过搜索整个表。如果查询匹配大量的行,这个开销就是浪费;无论如何,您将不得不阅读整个表格。


然后是 IO 成本。 使用磁盘顺序读写比随机读写要快得多。我们说话的速度快了 10 到 100 倍。

旋转磁盘有一个物理部分,即磁头,它必须四处移动以读取磁盘的不同部分。移动所需的时间称为“寻道时间”。当您在表中的行之间跳来跳去时,可能是乱序的,这是random access 并会导致查找时间。相比之下,读整张表很可能是一长串连续读;头部不必跳来跳去,没有寻道时间。

SSD 的速度要快得多,无需移动任何物理部件,但它们仍然是 much faster for sequential access than random

另外,随机访问在操作系统和磁盘之间有更多的开销;它需要更多说明。

因此,如果数据库决定一个查询将匹配表中的大多数行,它可以决定按顺序读取它们并清除不匹配的行比通过索引查找行并使用较慢的随机访问。


考虑一组邮政信箱,每个信箱都在一个大网格中编号。按数字查找每个盒子非常快,但从一个盒子开始并按顺序打开它们要快得多。我们有谁拥有哪个盒子以及他们住在哪里的索引。

您需要获取南北港的邮件。您在索引中查找属于 South Northport 的某个人的邮箱,发现其中只有几个,然后单独获取邮件。这是一个索引查询和随机访问。速度很快,因为只需检查几个邮箱。

现在我请您为所有人接收邮件,但 South Northport。您可以反向使用索引:获取 South Northport 的箱子列表,从每个箱子的列表中减去这些箱子,然后单独获取每个箱子的邮件。但这将是缓慢的随机访问。相反,由于无论如何您都将不得不打开几乎每个箱子,因此最好按顺序检查每个箱子,看看它是否是寄往 South Northport 的邮件。


更正式地说,索引与表扫描的性能是这样的。

# Indexed query
C[index] + (C[random] * M)

# Full table scan
(C[sequential] + C[match]) * N

其中C 是各种常量成本(或足够接近的常量),M 是匹配行数,N 是表中的行数。

我们知道 C[sequential]C[random] 快 10 到 100 倍。因为磁盘访问比 CPU 或内存操作慢得多,C[match](检查行是否匹配的成本)与C[sequential] 相比会相对较小。更正式的...

C[random] >> C[sequential] >> C[match]

使用它我们可以假设C[sequential] + C[match]C[sequential]

# Indexed query
C[index] + (C[random] * M)

# Full table scan
C[sequential] * N

M &lt;&lt; N 索引查询获胜。当 M 接近 N 时,全表扫描获胜。

请注意,使用索引的成本并不是固定不变的。 C[index] 是诸如加载索引、查找键和读取行 ID 之类的事情。这取决于索引的大小、索引的类型以及它是在磁盘上(冷)还是在内存中(热),这可能是非常可变的。这就是为什么当您第一次启动数据库服务器时,前几个查询通常相当慢的原因。


在现实世界中,它比这更复杂。实际上,行被分解为data pages,并且数据库有许多技巧来优化查询和磁盘访问。但是,通常情况下,如果您匹配大部分行,则全表扫描将优于索引查找。

如今,哈希索引的用途有限。它是一个简单的键/值对,只能用于相等性检查。大多数数据库使用B-Tree 作为标准索引。它们的成本稍高一些,但可以处理更广泛的操作,包括相等、范围、比较和前缀搜索,例如 like 'foo%'

Postgres Index Types documentation 很好地概括了索引类型的各种优缺点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    • 2011-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多