【问题标题】:Speed of MySQL SELECT (are indexes worth it on columns with little variation)?MySQL SELECT 的速度(在变化很小的列上是否值得索引)?
【发布时间】:2013-06-28 19:36:02
【问题描述】:

我已经对此主题进行了大量搜索/阅读,但我仍然找不到解决方案。

我有数万行或数十万行的表,总数据量在 300GB 左右。我需要选择的列包含很多 HTML,这可能是问题的一部分。我正在使用压缩。该查询包含两个 WHERE 子句:

 SELECT id, olr_id, COMPRESS(source_html) 
 FROM buildings 
 WHERE scrape_status=1 
 AND parse_status=0 LIMIT 1;

正如人们所预料的那样,id 是一个主键。此外,olr_id 是唯一索引。此查询需要 160-300 秒才能返回结果,这是完全不可行的。奇怪的是(至少对我而言),删除 parse_status 的 WHERE 子句会导致查询在 2-3 秒内完成。起初,我想这可能是因为它太具体了,所以我什至尝试删除 scrape_status 并只运行 parse_status WHERE 子句,但显然正是这个特定的子句导致了执行时间的疯狂增加。

问题是,我不知道索引这些列是否有任何好处(scrape_statusparse_status),因为它们的值范围是 0-2。我想这是我的主要问题——对值差异如此小的列进行索引是否有帮助?我曾经在某处读过,索引确实最适合变化很大的列,但正如我所说,我不知所措,对于数十万条记录来说,160-300 秒是完全不合理的。

任何输入将不胜感激。如果您需要其他输入来帮助我,我很乐意提供。请注意,以下是三个查询中每一个的 EXPLAIN 结果:

id: 1
select_type: SIMPLE
table: building
type: ALL
possible_keys: null
key: null
key_len: null
ref: null
rows: 58664
Extra: Using where

感谢您抽出宝贵时间阅读并提供任何可能的帮助。

【问题讨论】:

  • 只是一个想法,我什至不确定你会怎么做,但我认为你可以标记表的中点,然后同时运行两个查询(分叉或类似那)一个通过前半部分,一个通过你的表的后半部分。
  • 如果你在解析和抓取状态上添加了一个复合索引,查询会很快。你说的索引会很痛苦。为什么你会得到你想要的值,就像 DBMS 遇到的第一个值一样,(限制 1)?
  • @CppandQtBeginner 我会考虑将其作为可能的解决方案。
  • 如果您从中删除compress(source_html),查询需要多长时间?
  • @TonyHopkinson 也会研究复合索引。我得到这样的值是因为它处于“无限”循环中,获取第一个未解析的记录,然后解析它。您如何建议这样做?

标签: mysql select indexing where


【解决方案1】:

WHERE 子句中创建两列的复合索引:

CREATE INDEX ix_sp ON buildings (scrape_status, parse_status);

虽然它们每个都不会对表进行太多分区,但组合可能会。

也试试以下方法:

SELECT b1.id, olr_id, COMPRESS(source_html)
from buildings b1
JOIN (SELECT id
      FROM buildings 
      WHERE scrape_status=1 
      AND parse_status=0
      LIMIT 1) b2
USING (id)

您的原始查询可能正在压缩所有匹配的行,即使它们中的大部分都被LIMIT 子句抛出。此版本仅压缩一个选定的行。

【讨论】:

  • 不,MySQL 不会对行的选择列表中的表达式求值,直到确定它们与 WHERE 子句中的条件匹配。您可以对此进行测试:SELECT SLEEP(1) FROM SomeTable WHERE unindexed_column=123 并查看它休眠的秒数是否与表中的行数一样多,或者与值为 123 的行数一样多。
  • @BillKarwin 问题不是 WHERE 子句,而是 LIMIT 子句。
  • @Barmar 感谢您的出色回答。您还让我想到了第二个(也许更容易一点)的想法。我注意到如果我在不选择source_html 的情况下运行查询,它会快得多。我可以不选择,找到第一个匹配行的ID,然后选择该行的source_html。我将试一试,看看在处理所有索引更新之前会发生什么。
  • 这正是这个连接正在做的事情,但它在一个查询而不是 2 个查询中完成。
  • @Barmar 好的,我试一试。再次感谢您的帮助。
【解决方案2】:

即使索引列没有很多不同的值,索引在您搜索的值不常见的情况下也很有用。换句话说,当搜索更具选择性并匹配一小部分行时,索引会有所帮助。

因此,在这种情况下,是否创建索引取决于符合条件scrape_status=1parse_status=0 的行的百分比。例如,假设 scrape_status=1 匹配 2% 的行,parse_status=0 匹配 1% 的行,我想使用 parse_status 作为索引的前导列。

如果是这样,但几乎所有带有parse_status=0 的行也有scrape_status=1,那么制作复合索引可能没有什么额外的好处。而如果scrape_status=1 可以有效地进一步限制匹配的行,那么您肯定希望将它内置到索引中。

MySQL 还对 LIMIT 进行了优化。见http://dev.mysql.com/doc/refman/5.6/en/limit-optimization.html 一旦找到所需的匹配行数,它就会尝试退出查询。 MySQL 5.6 在这方面增加了一些新的优化。

MYSQL 不会为与 WHERE 子句中的条件不匹配的行计算选择列表表达式。

然而,MySQL 确实 必须从磁盘读取数据行以根据 WHERE 子句中的条件评估它们。这种 I/O 是大量性能成本的来源,这也是使用索引缩小搜索范围如此重要的原因。如果您的 source_html 列是包含长字符串的 TEXT 列,这可能会增加 I/O 的额外开销,因为 MySQL 将不得不从磁盘读取额外的数据页(有关更多详细信息,请参阅 Blob Storage in Innodb)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-02
    • 2011-10-08
    • 1970-01-01
    • 2013-02-20
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多