我有一个问题:
SELECT items.*
FROM items
INNER JOIN other on other.some_key = items.some_key
WHERE
items.partition_id = 7 AND
items.created_date > '2016-01-01 00:00:00' AND
(
other.name like '%user query%' OR
match (fulltext_candidate) against ('+user* +query*' IN BOOLEAN MODE) OR
items.some_varchar like '%user query%'
)
ORDER BY items.created_date
LIMIT 20 OFFSET 0;
我添加了多个索引
ALTER TABLE `items` ADD INDEX `partition_id` (`partition_id`)
ALTER TABLE `items` ADD FULLTEXT `fti` (`fulltext_candidate`)
ALTER TABLE `items` ADD INDEX `created_date` (`created_date`)
ALTER TABLE `items` ADD INDEX `some_varchar` (`some_varchar`)
ALTER TABLE `other` ADD INDEX `name` (`name`)
查询很慢。慢查询日志显示查询扫描items表的所有行。我相信MySQL只在查询中使用一个索引。如果我执行简化查询,我在WHERE子句中只包含一列,则查询速度非常快。
如何使用全文和普通索引构建一个优化此查询的索引?
答案 0 :(得分:0)
这对评论来说太大了。
这看起来很奇怪。
inner join other on other.some_key = items.some_key
我假设some_key已编入索引,但有一个非常奇怪的名字?如果是,并且它没有按照指定命中查询的索引,那么我认为它可能正在进行全表扫描,因为这是获取数据的最快方法。我需要了解更多有关您的数据等方面的帮助。举个例子。在小表中,数据库可能会全表扫描它们,因为它是几千字节的数据,您可以通过多种方式在查询中访问它。优化器查看索引并调用它可以过滤结果集的数量,如果此数字很低则不会使用它。
答案 1 :(得分:0)
我假设您已从查询中删除了一些细节 - " other.some_key"和" items.somekey" (你的连接列)肯定应该编入索引。
5.0之前的MySQL版本每个查询只能使用一个索引。 explain将告诉您在这种情况下发生了什么。
无论您做什么,您的查询中都有一些标准不会使用索引:
other.name like '%user query%'
items.some_varchar like '%user query%'
有一个领先的通配符,索引对你没有好处(你可以转换为自由文本搜索吗?)。目前还不清楚这导致了多少性能影响 - 如果您的查询的其余部分将匹配记录的数量减少到数百或数千,那么它可能没有什么区别。再次,EXPLAIN将帮助您。
如果你可以删除领先的外卡,并且可以添加" some_varchar"对于复合指数,它应该有所帮助。
对于您的查询,我创建了一个复合索引;这样,优化器就不会猜到你了。
ALTER TABLE `items` ADD INDEX `compound` (some_key, partition_id, created_date)
答案 2 :(得分:0)
<强>答案强>
有这些;没有其他人对您的查询(或任何讨论的变体)有用:
items: INDEX(partition_id, created_date)
other: INDEX(some_key)
关于所有其他聊天的评论:
ORs
阻止使用其中提到的列的索引(例如FULLTEXT
)。也就是说,删除前导%
仍然无效 - 因为OR
。
(some_key, partition_id, created_date)
使其无法用于WHERE
和ORDER BY
。
即使您可以使用“索引合并”,也很少被调用,因此不值得考虑。 (而且它只是一个粗糙的绷带。)
如果您有MATCH ...
可以使用FULLTEXT
索引,则只会使用FULLTEXT
索引。也就是说, if 可以使用它。同样,OR
会阻止您的情况。当然,“复合”索引非常有用;但优化器总是假设FULLTEXT
更有用。
由于无法使用FULLTEXT
,优化程序应继续INDEX(partition_id, created_date)
并发现它很有用。