1

我有以下查询:

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,users 约为 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。

有什么建议么?

4

2 回答 2

2

你是对的。此查询不应花费八分钟。一种可能性是索引实际上使查询变得更糟,因为查询想要进行全表扫描。在解决它们之前,我建议以下几点:

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

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

这可能允许它生成更好的查询计划。

于 2012-08-27T18:27:41.777 回答
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。在此之后,我将其结果放入表中进行查询。这个解决方案对我很有用,因为这个小查询只需不到一秒的时间就可以运行,并且可以在非高峰时段以批处理方式运行,而不会导致任何其他表被锁定。

感谢大家的建议和帮助!

于 2012-08-28T13:36:46.373 回答