索引查询“WHERE IN(1,2,3)AND b = 4”

时间:2012-03-21 22:39:59

标签: mysql sql ruby-on-rails indexing

我正在尝试应用一个索引来加速我的应用程序中最慢的查询之一:

SELECT * FROM orders WHERE product_id IN (1, 2, 3, 4) AND user_id = 5678;

我有product_iduser_id(product_id, user_id)对的索引。但是,服务器不使用任何这些索引:

+----+-------------+------- +------+-------------------------------------------------------------------------------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys                                                                             | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-------------------------------------------------------------------------------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | orders | ALL  | index_orders_on_product_id,index_orders_on_user_id,index_orders_on_product_id_and_user_id | NULL | NULL    | NULL |    6 | Using where |
+----+-------------+--------+------+-------------------------------------------------------------------------------------------+------+---------+------+------+-------------+

(开发中只有6行,所以无论如何,但是在生产中大约有400k行,因此执行需要大约0.25秒,并且这个查询经常被解雇。)

如何在这里避免简单的WHERE?我想我可以为每个product_id发送查询,这可能比这个版本更快,但产品的数量可能非常高,所以如果它在一个查询中可行,那将是非常可取的。这个查询是由Rails生成的,所以我对可以重构查询本身的程度有点限制。谢谢!

2 个答案:

答案 0 :(得分:5)

要在您的生产表(400k行)上获得此特定查询的最佳效果,您需要{user_id, product_id}上的复合索引,该顺序

理想情况下,这将是 only 索引,您将使用InnoDB,因此表格为clustered。在修改数据时,每个附加索引都会受到惩罚,并且在secondary indexes in clustered tables are even more expensive之上会比基于堆的表中的二级索引受到惩罚。

要了解为什么user_id(而不是product_id)应该位于索引的前沿,请查看Anatomy of an Index。基本上,由于WHERE只搜索一个user_id,因此首先将其聚集在索引附近的相关product_id值。

{product_id, user_id}也会起作用,但会不太好地“分散”“目标”索引节点。)

答案 1 :(得分:4)

当数据库中的行数太少时,它不使用索引,因为执行完整扫描会更便宜。尝试检查prod环境中的数据,看看它是否使用了一个索引。

另请注意,您可以删除其中一个索引index_by_product_id,因为您已经拥有另一个以product_id字段开头的索引。