我有以下查询:
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。
有什么建议吗?
答案 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。在此之后,我将结果并将其放入查询表中。这个解决方案对我很有用,因为这个小查询运行时间不到一秒,并且可以在非高峰时间运行批处理样式格式,而不会导致任何其他表被锁定。
感谢大家的建议和帮助!