MySql索引不适用于GROUP BY

时间:2017-05-20 09:52:26

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

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

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万条记录,在结果表中,我有大约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;

2 个答案:

答案 0 :(得分:0)

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

  

使用GROUP BY索引的最重要的前提条件是   所有GROUP BY列都引用同一索引的属性

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

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

答案 1 :(得分:0)

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通过

            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)

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

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