我有两个表来制作我的搜索引擎,一个包含所有关键字,另一个包含每个关键字的所有可能目标。
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;
答案 0 :(得分:0)
Here's用于GROUP BY优化的MySQL文档,这就是它所说的:
使用GROUP BY索引的最重要的前提条件是 所有GROUP BY列都引用同一索引的属性
因此,如果这两列的索引不同,GROUP BY
将不会使用它们。您应该尝试在table_id
和target_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
;可能还有其他提示。)