【问题标题】:SQL Improve Efficiency: LIMIT the amount of FILESORTSQL 提高效率:LIMIT the amount of FILESORT
【发布时间】:2017-02-10 19:10:18
【问题描述】:

我将通过这样的查询来解释自己:(post_id=PRIMARY, blog_id=index)

SELECT post_id FROM posts WHERE blog_id IN (2,3,...) ORDER BY post_id DESC LIMIT 10

更新:IN() 中的 id 可能很多。 如果数据库使用 blog_id 作为查询的键,它必须进行文件排序,因为索引看起来像这样:

(blog_id,post_id)-> (1,55) (1,59) (1,69) (2,57) (2,71) (2,72) (3,12)

如果您只搜索一个 id blog_id = 2 而不是 IN(),则不需要进行任何文件排序,因为所有匹配项都已按顺序排列。

我认为它正在发生的问题,不是 100% 肯定,而是通过查看查询执行时间,如果我添加一个 LIMIT 10 有效的方法是只捕获和文件排序每个 blog_id 的最后 10 个 id索引键匹配,也许它已经这样做了,但看起来对于 IN (2,3,4) ORDER BY post_id DESC LIMIT 10,它对数千个 id 进行排序而不是 30。

我希望我完全错了,因为如果我不是,那将是一个可怕的低效错误。 如果我是对的,我可以做任何引擎或改变吗?甚至更改数据库。目前我在 10.1.13-MariaDB 并且表是 InnoDB

【问题讨论】:

  • 你真的在做SELECT post_id,而不是SELECT *?这对 this 问题有重大影响。
  • 是的,因为它是一个更大的子查询,我在其中选择 * 并连接到其他表,并且在一年前的上一个问题中,我被告知这种形式更有效,而且确实如此。 stackoverflow.com/questions/30414641/…

标签: mysql sql mariadb


【解决方案1】:

不幸的是,MySQL 没有索引可以让您随心所欲。

但是,您可以重写您的查询并使用现有索引:

SELECT p.post_id
FROM ((SELECT post_id
       FROM posts
       WHERE blog_id = 2
       ORDER BY post_id DESC
       LIMIT 10
      ) UNION ALL
      (SELECT post_id
       FROM posts
       WHERE blog_id = 3
       ORDER BY post_id DESC
       LIMIT 10
      )
     ) p
ORDER BY post_id DESC
LIMIT 10;

每个子查询都会使用索引。对 20 个元素进行排序非常快。

【讨论】:

  • 嗯,IN() id 可以是数百个,它们是动态的,它们会发生变化,这就是一个例子。从我的角度来看,我认为我所说的很容易并且在技术上是可行的,所以我不明白为什么它没有完成,在我的脑海中没有意义。顺便说一句,我现在使用 MariaDB,它有新的表引擎。有没有可能?你说没有这样做的索引,也许我对索引顺序的理解是错误的?因为索引适合我,所以我在这里看到的问题是引擎搜索的方式。
  • PD:“并且它们是动态的,它们会改变”我的意思是数量的变化,可以整理出来,但正如我所说的,可能有很多 id。
  • UNION 方法适用于少量 blog_id;对于大数(N)不太好,UNION 的开销,加上 tmp 表将是 10*N 行。
  • @Vixxs 。 . .只能回答您提出的问题。您的问题有 2 或 3 个 ID。如果您有很多(比如数百或数千),那么IN 可能不是最好的方法。
  • 你是对的。在我的辩护中,我会说这两个例子表明 id 的数量是可变的,这暗示了数量可能更大,它们作为一个例子。但正如我所说,你是对的,我仍然可以选择这个答案或任何更正确地回答未更新答案的答案。
【解决方案2】:

看EXPLAIN SELECT ...;看看它是否说“文件排序”。

执行以下操作以获取详细信息,即使是小型数据集:

FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';

您确实需要INDEX(blog_id, post_id)。如果您使用的是 InnoDB 并且该表有

PRIMARY KEY(post_id),
INDEX(blog_id)

那么你确实有那个复合索引。这是因为每个二级索引都隐含包含 PK 的列。

由于您使用的是 MariaDB,请查看 LIMIT ROWS EXAMINED 是否会执行您询问的其他事情。

当优化器看到这个时:

WHERE blog_id IN (2,3)
ORDER BY post_id DESC LIMIT 10

它同时拥有INDEX(blog_id) 和INDEX(post_id),它会根据有限的统计数据做出决定:走哪条路:

计划 A:过滤 blog_id + 文件排序,或
方案 B:按 post_id 顺序扫描,希望尽快找到 10 行。

任何一个都有风险。计划 A,如果大多数或所有行都是 (2,3),将有一个大排序。方案 B,当匹配的行数少于 10 时,将扫描整个表(或索引)。

【讨论】:

  • 是的,我之前尝试过强制主服务器,并且根据测试,取决于帖子的数量或者它们是否过于深入索引,一种或另一种方式表现更好,我会有通过查看平均查询来做出选择。但我宁愿不做出那个选择,而是找到一种方法来做我的问题所暗示的,我想在几乎所有查询中都会胜过这两个选择。 LIMIT ROWS EXAMINED 看起来不是为此而设计的,如果数字太低,它会产生这个致命错误:#1028 - 排序中止:
  • 那么我认为您需要重新考虑“要求”。或者也许改变“用户期望”。你能牺牲那长长的名单吗?订购?还有什么? (并非所有性能问题都可以解决;您遇到了一个简单易解释但难以解决的问题。)
猜你喜欢
  • 2022-11-20
  • 2022-12-28
  • 1970-01-01
  • 1970-01-01
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
相关资源
最近更新 更多