2012-08-27 127 views
1

我有以下查詢:簡單的MySQL查詢優化

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) 

表大小

接近500萬個記錄的item_ratings表,而項目表是20萬左右和用戶是25萬左右。

解釋

的解釋查詢似乎做的項目(返回所有200K行)的表進行排序,即使是在一個item_active指數。其他表(item_ratings和user)都使用正確的索引。

最新通報

FULL EXPLAIN

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絲毫。這些表正在運行MyISAM。

有什麼建議嗎?

+5

發佈實際的'EXPLAIN'輸出... – Wrikken

+1

解釋輸出丟失,顯示缺少創建表輸出,缺少存儲引擎信息,缺少存儲引擎配置變量,用於運行此操作的硬件 - 也缺少信息。不知道任何人可以給出這麼多信息不存在的答案。 –

+1

我現在已經這樣做了,對於這個問題感到抱歉。 – gregavola

回答

2

你是對的。這個查詢不應該花八分鐘。一種可能性是索引實際上使查詢變得更糟,因爲查詢想要成爲全表掃描。在解決它們之前,我建議如下:

推測用戶和項目表具有不同的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 

這可能使其產生更好的查詢計劃。

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。在此之後,我將這個結果拿到桌子上查詢。這個解決方案對我來說很好,因爲這些小的查詢只需不到一秒的時間就可以運行,並且可以在高峯時段運行批處理樣式格式,而不會導致任何其他表被鎖定。

感謝大家對他們的建議和幫助!