2011-09-14 2 views
3

Я создаю сайт рекомендаций для видео (думаю, пандора для музыкальных видео) в python и MySQL. У меня три таблицы в моем бб:Как ускорить (или разбить) этот запрос MySQL?

video - таблица с видео. Данные не меняются. Колонки:

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 секунд, чтобы завершить за 50000 видео:

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 

Я отчаянно нужно, чтобы сделать это более эффективным, так что я просто нужен совет относительно того, что наилучшим направлением является. Некоторые идеи я рассмотрел:

а) Ремонтное моя дб структура таблицы (не знаю, как)

б) Разгрузка более группирования и агрегирования в Python (не придумал, как присоединиться три таблицы что на самом деле быстрее)

с) Хранить не меняющиеся таблицы в памяти, чтобы попытаться и время скорости вычислений (ранее мастерить не принесли никаких успехов еще ..)

Как бы вы рекомендовали сделать это более эффективное ?

Спасибо!

-

По желанию в комментариях, 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. Лично я бы изменил все поля рейтинга на целое, я не считаю, что им нужно плавать.

  • Отбросьте КЛЮЧ на id, ПЕРВИЧНЫЙ КЛЮЧ уже проиндексирован. 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 

Для последнего запроса выполнить очень быстро, вы можете включить в ваш первичный ключ в полях таблицы видео website_id и rating_global (возможно, достаточно только site_id).

Вы также можете использовать другую таблицу с этими статистическими данными и предсказывать динамически исходя из частоты входа пользователя/действия. Я предполагаю, что вы можете показать кэшированные данные, а не показывать результаты в реальном времени, не должно быть большой разницы.

+0

Спасибо - ваш запрос достиг скорости увеличения около 30%! Я понимаю, что мне, вероятно, нужно это сделать дальше, прежде чем он сможет жить в производстве. Я заметил, что около 85% времени запроса затрачено на первый запрос (CREATE TEMPORARY TABLE ..), поэтому, если у вас есть дополнительные рекомендации, я был бы признателен. – thegreatt

+0

Кроме того, я стараюсь избегать кэширования, так что последнее голосование пользователя может быть включено в рекомендацию. – thegreatt

+0

Ницца! Вы можете сделать еще одно изменение, чтобы ускорить работу. Переместите столбец * user_rating.id * и преобразуйте столбцы * user_id * и * tag_id * в PRIMARY KEY для этой таблицы, потому что эти 2 поля являются уникальной комбинацией в таблице. ALTER TABLE 'user_rating' ADD PRIMARY KEY (' tag_id', 'user_id'). – wisefish

Смежные вопросы