MySql 索引未应用于 GROUP BY

MySql Indexes are not applied in GROUP BY

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

Table: keywords
id (int)
keyword (varchar)

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

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

我还添加了以下索引

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

关键字 table 中,我有大约 120 万条记录,在 结果 table 中,我有大约 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;

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

The most important preconditions for using indexes for GROUP BY are that all GROUP BY columns reference attributes from the same index

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

上创建复合索引

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

JOIN + GROUP BY(或DISTINCT)就是我所说的"explode-implode"——首先JOIN乘以[=43=的数量] 查看,然后 GROUP BY 缩小行数。

避免这种情况的一种解决方法是关注主要 table,然后检查另一个 table:

中的 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
            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)

这里的优点(除了避免膨胀-收缩)是可以使用索引。

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