MySQL简单查询优化

时间:2012-08-27 14:59:55

标签: mysql optimization

我有以下查询:

SELECT
  b.item_name,
  COUNT(distinct c.user_id) AS total_count,
  AVG(c.item_rating) AS avg_rating
FROM       item_ratings as c
INNER JOIN items AS b ON b.item_id = c.item_id
INNER JOIN users AS u ON u.user_id = c.user_id
WHERE item_active = 1 AND u.user_valid = 1
GROUP BY c.item_id

此查询在高度优化的数据库上运行500秒 - 不确定发生了什么。

指数法

item_ratings - item_user_id, (item_id, user_id), item_rating, item_id
users - user_id, user_valid
items - item_id (primary), item_search (item_id, item_name), item_r (parent_id, item_id, item_active) 

表格大小

item_ratings表接近500万条记录,而items表大约为200k,用户大约为250k。

解释

解释查询似乎对项目进行了表格排序(返回所有200k行),即使item_active上有索引也是如此。其他表(item_ratings和user)都使用正确的索引。

更新

完整说明

id  select_type     table   type    possible_keys   key     key_len     ref       rows  Extra
1   SIMPLE  b   ALL     PRIMARY,item_id, item_search, item_r    NULL    NULL    NULL    218419  Using where; Using temporary; Using filesort
1   SIMPLE  c   ref     item_user_id ,user_id, item_id  4   myDB.b.item_id  29  Using where
1   SIMPLE  u   eq_ref  PRIMARY,user_valid,user_id  PRIMARY     4   myDB.c.user_id  1   Using where

硬件 这是运行Ubuntu 10.10的专用MySQL服务器盒,内存16GB的RAM。这些表正在运行MyISAM。

有什么建议吗?

2 个答案:

答案 0 :(得分:2)

你是对的。此查询不应该花费八分钟。一种可能性是索引实际上使查询更糟,因为查询想要进行全表扫描。在解决这些问题之前,我建议如下:

据推测,users和items表具有不同的id。此外,大概用户对给定项目只有一个评级。如果是这样,您可以删除计数distinct并将其替换为count:

SELECT b.item_name, COUNT(c.user_id) AS total_count, AVG(c.item_rating) AS avg_rating
FROM item_ratings as c INNER JOIN
     items AS b
     ON b.item_id = c.item_id INNER JOIN
     users AS u
     ON u.user_id = c.user_id
WHERE item_active = 1 AND u.user_valid = 1
GROUP BY c.item_id 

其次,“is_active”没有索引。索引已打开(parent_id,item_id,item_active)。您的查询未使用parent_id,因此不会使用此索引。

第三,由于聚合,它似乎是通过项目索引。由于您似乎想要item_name而不是item_id,我建议您将组更改为:

group by c.item_name

这可以让它生成更好的查询计划。

答案 1 :(得分:0)

即使使用item_active字段上的索引,查询仍然非常慢。由于此查询每天只运行一次,因此我找到了另一种解决方案,希望可以用于其他用户。

我基本上只是使用此查询来提取活跃啤酒列表:

SELECT b.beer_name
FROM items as b
WHERE b.item_active = 1

然后在每一行中,我循环并得到每个活跃项目的评级计数和平均评级,如下所示:

SELECT COUNT(DISTINCT c.user_id) AS total_count, AVG(c.item_rating) AS avg_rating
FROM item_ratings as c 
INNER JOIN users AS u ON u.user_id = c.user_id
WHERE item_active = 1 AND u.user_valid = 1 and b.item_id = @item_id

@item_id是我编写的PHP循环中的item_id。在此之后,我将结果并将其放入查询表中。这个解决方案对我很有用,因为这个小查询运行时间不到一秒,并且可以在非高峰时间运行批处理样式格式,而不会导致任何其他表被锁定。

感谢大家的建议和帮助!