【问题标题】:MySql Indexes are not applied in GROUP BYMySql 索引不适用于 GROUP BY
【发布时间】:2017-10-20 10:23:00
【问题描述】:

我有两张表来制作我的搜索引擎,一张包含所有关键字,另一张包含每个关键字的所有可能目标。

Table: keywords
id (int)
keyword (varchar)

Table: results
id (int)
keyword_id (int)
table_id (int)
target_id (int)

对于这两个表,我将 MyISAM 设置为存储引擎,因为 95% 的时间我只是在这些表上运行选择查询,而在 5% 的时间里,插入查询。当然,我已经比较了使用 InnoDB 的性能,考虑到我后来的查询,性能很差。

我还添加了以下索引

keywords.keyword (unique)
results.keyword_id (index)
results.table_id (index)
results.target_id (index)

keywords 表中,我有大约 120 万条记录,在 results 表中我有大约 980 万条记录。

现在问题是我运行以下查询,结果在 0.0014 秒内生成

SELECT rs.table_id, rs.target_id
FROM keywords ky INNER JOIN results rs ON ky.id=rs.keyword_id
WHERE ky.keyword LIKE "x%" OR ky.keyword LIKE "y%"

但是当我添加 GROUP BY 时,结果是在 0.2 秒内产生的

SELECT rs.table_id, rs.target_id
FROM keywords ky INNER JOIN results rs ON ky.id=rs.keyword_id
WHERE ky.keyword LIKE "x%" OR ky.keyword LIKE "y%"
GROUP BY rs.table_id, rs.target_id

我测试了复合索引、单列索引,甚至删除了 table_id 和 target_id 索引,但在所有情况下,性能都是一样的,似乎在 Group By 子句中,索引没有被应用。

解释计划表明:

id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
1 | SIMPLE | ky | range | PRIMARY,keyword | keyword | 767 | NULL | 3271 | Using index condition; Using where; Using temporary; Using filesort
1 | SIMPLE | rs | ref | keyword_id | keyword_id | 4 | ky.id | 3

我已经添加了以下复合键

ALTER TABLE results ADD INDEX `table_id` (`table_id`, `target_id`) USING BTREE;

【问题讨论】:

  • 当服务器配置正确时,InnoDB 比 MyISAM 更快读取percona.com/blog/2007/01/08/…
  • 我阅读了文档,我找不到任何关于服务器配置的信息,根据我的理解,考虑到我的应用程序,MyISAM 更快。
  • 在 MyISAM 中,keywords.keyword (unique) 是次优的;使用 InnoDB 就可以了。

标签: mysql search group-by myisam large-data


【解决方案1】:

Here's GROUP BY 优化的 MySQL 文档,它是这么说的:

为 GROUP BY 使用索引的最重要的先决条件是 所有 GROUP BY 列都引用同一索引中的属性

因此,如果您在这两列上有不同的索引,GROUP BY 将不会使用它们。您应该尝试在 table_idtarget_id 上创建复合索引。

此外,查询似乎使用LIKE 运算符。请注意,如果在LIKE 中比较的值中有前导通配符,那么 MySQL 无论如何都无法为该列使用任何索引。查看查询的explain plan 并查看使用了哪些索引。

【讨论】:

  • 我在 table_id 和 target_id 上尝试了复合键,但它不起作用。 EXPLAIN 计划也不包括复合索引。此外,考虑到记录数和查询时间,您可能会注意到关键字列上的索引工作正常。 EXPLAIN plan 还表示正在使用该索引。
  • 可能是因为LIKE操作符我解释的,看看查询的解释计划。
  • 这是我得到的关键字索引:SIMPLE ky range PRIMARY,keyword keyword 767 NULL 3271 Using index condition;使用哪里;使用临时的;使用文件排序
  • 它是否显示任何其他索引?你能把它添加到问题中吗?
  • @MohammadAli 从解释计划来看,似乎索引正在keyword_id 上使用。由于 MySQL 每个表只能使用一个索引,GROUP BY 索引不会出现在解释计划中。我会尝试删除现有索引并在(keyword_idtable_idtarget_id)上添加一个复合索引。
【解决方案2】:

JOIN + GROUP BY(或DISTINCT)就是我所说的“explode-implode”——首先JOIN 乘以要查看的“行”数,然后GROUP BY 缩小行数。

避免这种情况的一种解决方法是关注主表,然后在另一个表中检查EXISTS

SELECT  rs.table_id, rs.target_id
    FROM  keywords ky
    WHERE  EXISTS(
        SELECT  1
            FROM  results rs
            WHERE  ky.id = rs.keyword_id
              AND  ( ky.keyword LIKE "x%"
                 OR  ky.keyword LIKE "y%" )
                 );

rs 需要INDEX(keyword_id)

对此的改进可能是摆脱OR via

            WHERE  ky.id = rs.keyword_id
              AND  ky.keyword REGEXP "^[xy]"

但这不是很有帮助,因为它仍然需要完全检查keyword

另一个改进可能是将OR 变成UNION

(  SELECT  rs.table_id, rs.target_id
        FROM  keywords ky
        INNER JOIN  results rs  ON ky.id=rs.keyword_id
        WHERE ky.keyword LIKE "x%"
) UNION ALL
(  SELECT  rs.table_id, rs.target_id
        FROM  keywords ky
        INNER JOIN  results rs  ON ky.id=rs.keyword_id
        WHERE ky.keyword LIKE "y%"
)

ky: INDEX(keyword, id)
rs: INDEX(keyword_id)

这里的优点(除了避免inflate-deflate)是可以使用索引。

(请为两张表提供SHOW CREATE TABLE;可能还有其他提示。)

【讨论】:

    猜你喜欢
    • 2013-12-07
    • 2011-03-12
    • 1970-01-01
    • 1970-01-01
    • 2017-04-19
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多