我正在尝试应用一个索引来加速我的应用程序中最慢的查询之一:
SELECT * FROM orders WHERE product_id IN (1, 2, 3, 4) AND user_id = 5678;
我有product_id
,user_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生成的,所以我对可以重构查询本身的程度有点限制。谢谢!
答案 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字段开头的索引。