2011-09-14 47 views
3

我正在python和MySQL中構建一個視頻推薦網站(請考慮音樂視頻的pandora)。我有三個表在我的分貝:我該如何加快(或分手)這個MySQL查詢?

視頻 - 一個視頻表。數據不會改變。欄目有:

CREATE TABLE `video` (
    id int(11) NOT NULL AUTO_INCREMENT, 
    website_id smallint(3) unsigned DEFAULT '0', 
    rating_global varchar(128) DEFAULT '0', 
    title varchar(256) DEFAULT NULL, 
    thumb_url text, 
PRIMARY KEY (`id`), 
KEY `websites` (`website_id`), 
KEY `id` (`id`) USING BTREE 
) ENGINE=InnoDB AUTO_INCREMENT=49362 DEFAULT CHARSET=utf8 

video_tag - 標籤與每個視頻相關的(屬性)的表。不會改變。

CREATE TABLE `video_tag` (
    id int(7) NOT NULL AUTO_INCREMENT, 
    video_id mediumint(7) unsigned DEFAULT '0', 
    tag_id mediumint(7) unsigned DEFAULT '0', 
PRIMARY KEY (`id`), 
KEY `video_id` (`video_id`), 
KEY `tag_id` (`tag_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=562456 DEFAULT CHARSET=utf8 

user_rating - 用戶給每個標籤的好壞評級表。數據總是在變化。

CREATE TABLE `user_rating` (
    id int(11) NOT NULL AUTO_INCREMENT, 
    user_id smallint(3) unsigned DEFAULT '0', 
    tag_id int(5) unsigned DEFAULT '0', 
    tag_rating float(10,5) DEFAULT '0', 
PRIMARY KEY (`id`), 
KEY `video` (`tag_id`), 
KEY `user_id` (`user_id`) USING BTREE 
) ENGINE=InnoDB AUTO_INCREMENT=447 DEFAULT CHARSET=utf8 

根據用戶的喜好,我想進球每個不留神視頻,並嘗試預測他們會喜歡最擅長的。這導致了以下大量的查詢,這需要大約2秒完成了50,000部影片:

SELECT video_tag.video_id, 
     (sum(user_rating.tag_rating) * video.rating_global) as score 

FROM video_tag 
JOIN user_rating ON user_rating.tag_id = video_tag.tag_id 
JOIN video ON video.id = video_tag.video_id 

WHERE user_rating.user_id = 1 AND video.website_id = 2 
AND rating_global > 0 AND video_id NOT IN (1,2,3) GROUP BY video_id 
ORDER BY score DESC LIMIT 20 

我迫切需要,使這個更有效,所以我只是在尋找意見,以什麼是最好的方向是。有些想法我已經考慮:

一)返工我的數據庫表結構(不知道)如何)

b將更多的分組和聚集成Python的(還沒有想出一個辦法加入三個表實際上更快)

C)保存在存儲器中不改變表嘗試和速度計算時間(早修修補補還沒有產生任何收益尚未..)

你會如何建議使這一更有效率?

謝謝你!

-

每評價請求,EXPLAIN SELECT ..所示:

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE user_rating ref  video,user_id user_id 3 const 88 Using where; Using temporary; Using filesort 
1 SIMPLE video_tag ref  video_id,tag_id tag_id 4 db.user_rating.tag_id 92 Using where 
1 SIMPLE video  eq_ref PRIMARY,websites,id PRIMARY 4 db.video_tag.video_id 1 Using where 
+0

你甚至沒有包含你的表結構,你會如何期待社區中的某些東西? – ajreal

+0

感謝您的建議。我不想淹沒信息,但基於您的反饋,我添加了表格結構。 – thegreatt

+1

這也沒有太大的幫助,你應該包括適當的模式,因爲模式將包括數據類型+索引類型/列 – ajreal

回答

1
  • 更改字段類型的* rating_global *爲數值型的(漂浮或整數),不需要它是varchar。就個人而言,我會將所有評分字段更改爲整數,我發現他們不需要浮動。

  • 將KEY放在id上,PRIMARY KEY已經編入索引。 video.id,rating_global,website_id

  • 觀察您的參考(例如video_id - > video.id)的整數長度,您可能會用盡數字。這些尺寸應該是一樣的。

我建議以下2步解決方案來替換查詢:

CREATE TEMPORARY TABLE rating_stats ENGINE=MEMORY 
SELECT video_id, SUM(tag_rating) AS tag_rating_sum 
FROM user_rating ur JOIN video_tag vt ON vt.id = ur.tag_id AND ur.user_id=1 
GROUP BY video_id ORDER BY NULL 

SELECT v.id, tag_rating_sum*rating_global AS score FROM video v 
JOIN rating_stats rs ON rs.video_id = v.id 
WHERE v.website_id=2 AND v.rating_global > 0 AND v.id NOT IN (1,2,3) 
ORDER BY score DESC LIMIT 20 

對於後者查詢來執行非常快,你可以結合你的PRIMARY KEY在視頻表字段website_id和rating_global(也許只有website_id已經足夠了)。

您還可以使用其他表格,並根據用戶登錄/操作頻率動態預先計算這些統計信息。我猜你可以顯示緩存的數據而不是顯示實時結果,應該沒有太大的區別。

+0

謝謝 - 您的查詢實現了約30%的速度增益!我意識到,我可能需要進一步降低它,然後才能生產。我注意到大約85%的查詢時間花在第一個查詢(CREATE TEMPORARY TABLE ..)中,所以如果你有任何額外的建議,我將不勝感激。 – thegreatt

+0

此外,我試圖避免緩存,以便用戶的最新投票可以納入建議。 – thegreatt

+0

不錯!你可以做另一個改變來加速事情。刪除* user_rating.id *列並將* user_id *和* tag_id *列轉換爲該表的PRIMARY KEY,因爲這兩個字段是表中唯一的組合。 ALTER TABLE'user_rating'添加主鍵('tag_id','user_id')。 – wisefish