Проблема Эффективности MySQL - Как найти правильный баланс нормализации …?

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

Я сталкиваюсь с проектным решением, и я не уверен, как продолжить. Вот очень по упрощенной версии того, что я создаю: Люди могут оценить фотографии 1-5, и я должен отобразить средние голоса по изображению при отслеживании отдельные голоса. Например, 12 человек голосовали 1, 7 человек голосовали 2 и т.д. и т.д.

Пятно нормализации меня первоначально разработало структуру таблицы как это:

Table pictures
id* | picture | userID | 

Table ratings
id* | pictureID | userID | rating

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

Для нахождения средней оценки изображения я только что выполнил что-то вроде этого:

SELECT AVG(rating) FROM ratings WHERE pictureID = '5' GROUP by pictureID 

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

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

Используя ненормализованную версию, казалось бы, был бы более эффективным. например:

Table picture
id | picture | userID | ratingOne | ratingTwo | ratingThree | ratingFour | ratingFive

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

Кто-то может указать на меня в правильном направлении того, что сделать? Мое начальное исследование показывает, что я должен "найти правильный баланс", но как я иду о нахождении того баланса? Любые статьи или дополнительная информация о чтении ценились бы также.

Спасибо.

6
задан Foo 21 March 2010 в 08:28
поделиться

5 ответов

Ваш нормализованный подход имеет большой смысл, а денормализованный - нет.


По моему опыту (управление производительностью телекоммуникационной компании, сотни тысяч точек данных за 1/4 часа), мы бы сделали следующее:

Table: pictures
id* | picture | userID | avg_rating | rating_count

Table: ratings
id* | pictureID | userID | rating

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


В телефонной компании мы также сохраняем дату оценки в вашей таблице «изображений» и отметку времени 1/4 часа в таблице оценок, но я не думаю, что вам нужен такой уровень детализации.


«Денормализация» заключается в перемещении вычисляемого факта (count (рейтинг) и avg (рейтинг)) в таблицу изображений. Это экономит циклы ЦП, но требует больше памяти.

4
ответ дан 10 December 2019 в 02:45
поделиться

вот как я бы подошел к проблеме http://pastie.org/879604

drop table if exists picture;
create table picture
( 
 picture_id int unsigned not null auto_increment primary key,
 user_id int unsigned not null, -- owner of the picture, the user who uploaded it
 tot_votes int unsigned not null default 0, -- total number of votes 
 tot_rating int unsigned not null default 0, -- accumulative ratings 
 avg_rating decimal(5,2) not null default 0, -- tot_rating / tot_votes
 key picture_user_idx(user_id)
)engine=innodb;

insert into picture (user_id) values 
 (1),(2),(3),(4),(5),(6),(7),(1),(1),(2),(3),(6),(7),(7),(5);


drop table if exists picture_vote;
create table picture_vote
( 
 picture_id int unsigned not null,
 user_id int unsigned not null,-- voter
 rating tinyint unsigned not null default 0, -- rating 0 to 5
 primary key (picture_id, user_id)
)engine=innodb;

delimiter #

create trigger picture_vote_before_ins_trig before insert on picture_vote
for each row
begin
 declare total_rating int unsigned default 0;
 declare total_votes int unsigned default 0;

 select tot_rating + new.rating, tot_votes + 1 into total_rating, total_votes 
   from picture where picture_id = new.picture_id;

 -- counts/stats
 update picture set
    tot_votes = total_votes, tot_rating = total_rating, 
    avg_rating = total_rating / total_votes
 where picture_id = new.picture_id;

 end#
 delimiter ;

надеюсь, что это поможет :)

2
ответ дан 10 December 2019 в 02:45
поделиться

Хороший способ насладиться обоими мирами - с помощью Mysql Trigger. http://dev.mysql.com/doc/refman/5.0/en/triggers.html

Теперь добавьте триггер, который когда-либо оценивает изображение, он будет обновите avg_rating в таблицах изображений. (используя тот же выбор, который вы указали)

Теперь, когда вы выбираете, вы можете выбирать только на одной таблице. И он всегда обновляется. А если вы хотите получить точную информацию о том, кто оценивает какую картинку, вы также можете выбрать ее из рейтинговой таблицы.

1
ответ дан 10 December 2019 в 02:45
поделиться

Что будут содержать эти поля ratingOne к ratingFive? Количество полученных голосов? Тогда вы не узнаете, кто голосовал. Если вам действительно нужно денормализовать, я бы просто добавил поле «средний рейтинг» в графическую таблицу и обновлял его при каждом голосовании (и оставил таблицу оценок как есть).

В общем, не попадитесь преждевременной оптимизации. Попробуйте написать тестовый сценарий, который создает 100 000 изображений и 1 миллион оценок (или любую другую цифру, которую вы хотите поддержать), и посмотрите, сколько времени займет ваш запрос AVG. Скорее всего, все еще будет достаточно быстро. Убедитесь, что ваша таблица "рейтингов" имеет индекс по идентификатору изображения, чтобы БД не нужно было просматривать миллион строк.

1
ответ дан 10 December 2019 в 02:45
поделиться

В мире РСУБД денормализация означает «Я хочу повысить эффективность запросов за счет увеличения объема обслуживания , сохраняя при этом правильность модели »

В вашем случае эффективность действительно немного повысится (поскольку все рейтинги всегда берутся с одной и той же страницы данных).

А как насчет правильности модели?

При таком дизайне вы, во-первых, не знаете, кто голосовал (эта информация больше не сохраняется), а, во-вторых, не можете оценить картинку более пяти раз.

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

1
ответ дан 10 December 2019 в 02:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: