简体   繁体   English

使用多个索引优化MySQL查询

[英]Optimize MySQL Query with multiple indexes

I have a query: 我有一个查询:

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;

I added multiple indexes 我添加了多个索引

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`)

The query is slow. 查询速度很慢。 The slow query log shows the query scanning all rows of the items table. 慢查询日志显示查询正在扫描项目表的所有行。 I believe MySQL is only using one index on the query. 我相信MySQL在查询中只使用一个索引。 If I do a reduced query, where I only include one column in the WHERE clause, the query is very fast. 如果我做一个简化的查询,在WHERE子句中只包括一列,那么查询将非常快。

How can I build a single index that will optimize this query, using both fulltext and normal indexes? 如何使用全文本索引和普通索引构建一个可以优化此查询的索引?

This is too big for a comment. 这太大了,无法发表评论。

This looks odd. 这看起来很奇怪。

inner join other on other.some_key = items.some_key

I'm assuming some_key is indexed but has a very weird name? 我假设some_key已编制索引,但名称很奇怪? If it is and it's not hitting an index on the query as specified then I reckon it might be doing a full table scan because that's the fastest way to get the data. 如果是这样,并且没有按指定查询命中索引,那么我认为它可能正在执行全表扫描,因为这是获取数据的最快方法。 I'd need to know more about your data etc to help. 我需要进一步了解您的数据等以提供帮助。 As an example. 举个例子。 In small tables the DB might full table scan them in because it's a few kilobytes of data and you may access it in your query in multiple ways. 在小型表中,DB可能会对整个表进行扫描,因为它只有几千字节的数据,您可以通过多种方式在查询中访问它。 The optimizer looks at an index and makes a call on how much it can filter the result set by, if this number is low it won't use it. 优化器查看索引并调用它可以过滤结果集的数量,如果该数字较低,则它将不使用它。

I'm assuming you've removed some detail from the query - "other.some_key" and "items.somekey" (your join columns) should definitely be indexed. 我假设您已经从查询中删除了一些细节-“ other.some_key”和“ items.somekey”(您的联接列)绝对应该建立索引。

Versions of MySQL before 5.0 can only use a single index per query. 5.0之前的MySQL版本每个查询只能使用一个索引。 An explain will tell you what's going on in this case. 一个解释将告诉您在这种情况下的情况。

There are a couple of criteria in your query that will not use an index, no matter what you do: 无论您做什么,查询中都有几个不会使用索引的条件:

other.name like '%user query%' 
items.some_varchar like '%user query%'

have a leading wildcard, and the index will do you no good (can you convert to free text search instead?). 有一个前导通配符,索引将对您不利(可以转换为自由文本搜索吗?)。 It's not clear how much of a performance hit this is causing - if the rest of your query filters down the number of matching records to hundreds or thousands, it's probably not making a difference. 目前尚不清楚这将对性能造成多大的影响-如果您的其余查询将匹配记录的数量过滤到数百或数千,则可能没有任何效果。 Again, EXPLAIN will help out. 同样,EXPLAIN将提供帮助。

If you can remove the leading wild card, and can add "some_varchar" to the compound index, it should help. 如果您可以删除前导通配符,并且可以将“ some_varchar”添加到复合索引,则应该会有所帮助。

For your query, I'd create a single, compound index; 对于您的查询,我将创建一个单一的复合索引。 that way, the optimizer doesn't second guess you. 这样,优化程序就不会再猜测您了。

ALTER TABLE `items` ADD INDEX `compound` (some_key, partition_id, created_date)

Answer 回答

Have these; 有这些; no others will be useful for your query (or any discussed variant): 没有其他内容对您的查询(或任何讨论的变体)有用:

items: INDEX(partition_id, created_date)
other: INDEX(some_key)

Comments on all the other chatter: 对所有其他聊天记录的评论

The ORs prevent any use of indexes (such as your FULLTEXT ) of columns mentioned in them. ORs阻止对它们中提到的列的索引(例如FULLTEXT )进行任何使用。 That is, removing the leading % still won't do any good - because of the OR . 也就是说,由于OR ,删除前导%仍然没有任何好处。

(some_key, partition_id, created_date) makes it unusable for the WHERE and ORDER BY . (some_key, partition_id, created_date)使它无法用于WHEREORDER BY

Even if you could use the "index merge", it is sooooo rarely invoked that it is not worth considering. 即使可以使用“索引合并”,也很少调用它,因此不值得考虑。 (And it is only a crude bandaid.) (这只是一个简单的创可贴。)

When you have a MATCH ... that could use a FULLTEXT index, only the FULLTEXT index will be used. 当您具有可以使用FULLTEXT索引的MATCH ... ,将仅使用FULLTEXT索引。 That is, if it can be used. 也就是说, 如果可以使用。 Again, the OR prevents it for your case. 同样, OR会针对您的情况进行阻止。 Sure, "compound" indexes are very useful; 当然,“复合”索引非常有用。 but the optimizer always assumes that FULLTEXT is more useful. 但是优化器始终认为FULLTEXT更有用。

Since FULLTEXT cannot be used, the optimizer should move on to INDEX(partition_id, created_date) and find it useful. 由于无法使用FULLTEXT ,因此优化器应继续使用INDEX(partition_id, created_date)并发现它很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM