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_id
和 target_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
;可能还有其他提示。)
我有两个 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_id
和 target_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
;可能还有其他提示。)