Эффективная структура таблицы MySQL для рейтинговой системы

Этот пост является продолжением ответа на этот вопрос: Лучший способ хранения списка идентификаторов пользователей.

Я взял клитус и Мехрдад Афшари в эпическом совете по использованию нормализованного подхода к базе данных. Правильно ли настроены следующие таблицы для правильной оптимизации? Я новичок в эффективности MySQL, поэтому я хочу убедиться, что это эффективно.

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

SELECT avg(vote) FROM votes WHERE uid = $uid AND gid = $gid;    
SELECT count(uid) FROM votes WHERE uid = $uid AND gid = $gid;

CREATE TABLE IF NOT EXISTS `games` (
  `id` int(8) NOT NULL auto_increment,
  `title` varchar(50) NOT NULL,
  PRIMARY KEY  (`id`)
) AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(8) NOT NULL auto_increment,
  `username` varchar(20) NOT NULL,
  PRIMARY KEY  (`id`)
) AUTO_INCREMENT=1 ;


CREATE TABLE IF NOT EXISTS `votes` (
  `uid` int(8) NOT NULL,
  `gid` int(8) NOT NULL,
  `vote` int(1) NOT NULL,
  KEY `uid` (`uid`,`gid`)
) ;

Ответы

Ответ 1

средний голос (ов) за игру: SELECT avg(vote) FROM votes WHERE gid = $gid;

количество голосов для игры: SELECT count(uid) FROM votes WHERE gid = $gid;

Поскольку у вас не будет меньших идентификаторов пользователей или игр, чем 0, вы можете сделать их целыми без знака (int(8) unsigned NOT NULL).

Если вы хотите обеспечить, чтобы пользователь мог сделать только один голос за игру, создайте первичный ключ в uid и gid в таблице votes вместо обычного индекса.

CREATE TABLE IF NOT EXISTS `votes` (
  `uid` int(8) unsigned NOT NULL,
  `gid` int(8) unsigned NOT NULL,
  `vote` int(1) NOT NULL,
  PRIMARY KEY (`gid`, `uid`)
) ;

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

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

Ответ 2

Выглядит хорошо.

Я бы использовал users_id и games_id вместо gid и uid, который звучит как глобальный id и уникальный id

Ответ 3

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

Напишите script, который генерирует 100 000 игр, 50 000 пользователей и миллион голосов. Может быть немного чрезмерным, но если ваши запросы не занимают часы с таким количеством элементов, это никогда не будет проблемой.

Ответ 4

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

Кроме того, в отношении выбора механизма хранения я еще не вижу причины (в достаточно сложном/размерном приложении) не использовать innodb, а не только из-за транзакционной семантики.

Ответ 5

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