【问题标题】:SQL -- How is DISTINCT so fast without an index?SQL——没有索引的 DISTINCT 怎么这么快?
【发布时间】:2023-04-08 02:54:01
【问题描述】:

我有一个数据库,其中有一个名为“links”的表,在 SQLite 中有 6 亿行。数据库中有 2 列 - “src”列和“dest”列。目前没有索引。

在 src 和 dest 之间有相当数量的公共值,但也有相当数量的重复行。

我要做的第一件事是删除所有重复的行,然后对结果进行一些额外的处理,但是我遇到了一些奇怪的问题。

首先,SELECT * FROM links WHERE src=434923 AND dest=5010182。现在这会很快返回一个结果,然后需要很长时间才能运行,因为我假设它正在对其余 600m 行执行表扫描。

但是,如果我执行SELECT DISTINCT * FROM links,那么它会立即开始快速返回行。问题是:这怎么可能?当然,对于每一行,该行都必须与表中的所有其他行进行比较,但这需要对表中剩余的行进行表扫描,这应该需要很长时间!

知道为什么SELECT DISTINCT 比标准的SELECT 快​​得多吗?

【问题讨论】:

  • 你的数据库中有主键吗?
  • 表的架构是什么? (请随意匿名。)
  • 如果您反复读取 SQLite 文件,它将最终进入操作系统页面缓存。因此,重复读取可能会影响 RAM 而不是磁盘。取决于您的 RAM 与 DB 的大小以及盒子上发生的其他情况。
  • @Spike 6 亿行适合页面缓存?即使这是可能的,这如何解释一次扫描比另一次扫描更快?他们都会从记忆中阅读
  • 想象一下两个查询同时实时运行。两者都在进行全表扫描。两者都读取第 1 行、第 2 行、第 3 行和第 4 行,确定它们是否与您的查询匹配并应发送给您。对于第一个查询,大多数行不匹配,因此您看不到任何输出并等待。对于第二个,大多数行都匹配,因此您会看到它们滚动。同样的速度,第二个感觉更快。

标签: sql database optimization sqlite


【解决方案1】:

考虑一下。在没有应用排序的情况下,它可以按扫描顺序返回结果。它只是保留到目前为止看到的值的列表(更有可能是一个有效的结构,如 b 树)。如果未找到给定值,则将其返回并添加到簿记结构中。完全不需要与所有其他行进行比较。

【讨论】:

  • 这是一个很好的答案,而且是有道理的(我很傻,因为之前没有意识到这一点)。然而:我们确定它是把它放在一个 b-tree 中,还是它只是创建一个临时的类似表的存储,它变得越来越大并且需要从头到尾扫描每一行?
  • B-tree 只是一个假设。但是,对于成员资格测试,b-tree 通常比全扫描(O(logN) vs O(N))快得多
  • 我意识到 b-tree 具有 O(logN) 的优势,这就是为什么我真的希望 DISTINCT 的 SQLite 实现使用 b-tree 进行临时存储——谁能证实这一点?
【解决方案2】:

更准确地说,一个查询并不比另一个快。更准确地说,查询完成所用的时间对于两个查询应该是相同的。不同之处在于,带有 DISTINCT 的查询只需返回更多行,因此它似乎响应更快,因为您正在快速接收行。然而,两者背后发生的是同一个表扫描。 distinct 查询有一个数据结构,用于存储返回的内容并过滤重复项。因此,实际上应该需要更长的时间才能完成查询,但是(返回的行数)/时间更大,因为匹配的行数更多。 (另请注意:一些查看器添加了查询结果限制,这可以使不同的查询看起来运行得更快(因为您达到了结果限制并停止)。

【讨论】:

  • +1。有点复杂,但我同意基本前提。从磁盘/内存中获取行不是瓶颈。数据传输是瓶颈,因此更少的行 = 更快。
猜你喜欢
  • 1970-01-01
  • 2020-12-19
  • 2011-06-02
  • 2012-03-18
  • 2014-05-09
  • 1970-01-01
  • 2016-03-19
  • 2012-09-19
  • 2010-09-13
相关资源
最近更新 更多