Как улучшить производительность моей системы голосования? - PullRequest
1 голос
/ 13 мая 2011

У меня есть веб-сайт с системой голосования (нравится / не нравится).

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

У меня есть следующая таблица:

CREATE TABLE `vote` (
  `id` int(11) NOT NULL auto_increment,
  `article_id` int(11) NOT NULL,
  `token` varchar(64) collate utf8_unicode_ci NOT NULL,
  `type` int(1) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `article_id` (`article_id`)
) ENGINE=InnoDB;

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

Один из самых медленных запросов:

SELECT count(*) AS `nb` FROM `vote` WHERE (token = '00123456789012345678901234567890');

Иногда, когда сервер не выключается, возвращается почти 10 секунд.

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

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

Так что я ищу варианты улучшения, даже нескольких, производительности.

Редактировать : у меня есть индекс для столбца токена

есть ~ 2 000 000 строк и все токены почти уникальны


EDIT

Я провел тест со всеми вашими советами:

Top average queries
1. SELECT COUNT(*) AS nb FROM `vote` WHERE (`token` = '%s') completed in 2.19790604115 sec
2. SELECT COUNT(`id`) AS nb FROM `vote` WHERE (`token` = '%s') completed in 2.28792096376 sec 
3. SELECT COUNT(`id`) AS nb FROM `vote` WHERE (`token` = '%s') GROUP BY `token` completed in 2.3732401371 sec
4. SELECT COUNT(*) AS nb FROM `vote` WHERE (`token` = '%s') GROUP BY `token` completed in 2.57634830475 sec 

Иногда третий запрос самый быстрый, а иногда и худший.

Я запускал его 10 раз, когда каждый запрос выполнялся 20 раз

Я провел этот тест БЕЗ любых индексов (кроме одного на id)

Это странно, хотя COUNT (id) мог бы немного ускорить запрос.

Ответы [ 4 ]

6 голосов
/ 13 мая 2011

Вы должны посмотреть на индексирование столбца токена, если он еще не проиндексирован.

3 голосов
/ 13 мая 2011

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

CREATE TABLE `voteCounts` (
  `token` varchar(64) collate utf8_unicode_ci NOT NULL PRIMARY KEY,
  `count` int
) ENGINE=InnoDB;

Затем, когда вы вставляете строку в голосование, вы также можете вызвать

UPDATE voteCounts
set `count` = `count` +1
WHERE
token = '012345' ;
1 голос
/ 13 мая 2011

В общем случае следует добавлять индексы для столбцов в больших таблицах, которые используются в предложениях часто выполняемых запросов. В вашем примере запроса вам понадобится один в столбце токена. Похоже, вы используете базу данных MySQL, поэтому вот важная часть инструкции create table для этой базы данных:

CREATE TABLE `vote` (
..
  token varchar(64) collate utf8_unicode_ci NOT NULL,
  index token_ind (token),
..
) ENGINE=InnoDB;
0 голосов
/ 13 мая 2011

Я действительно не уделял слишком много внимания вашей текущей реализации, но следующий метод, который я использую для 99,99% систем голосования, чрезвычайно эффективен:

Результаты:

mysql> select * from article;
+------------+-----------+-----------+-------------+--------+
| article_id | title     | num_votes | total_score | rating |
+------------+-----------+-----------+-------------+--------+
|          1 | article 1 |         5 |          15 |   3.00 |
|          2 | article 2 |         3 |           7 |   2.33 |
|          3 | article 3 |         2 |           6 |   3.00 |
+------------+-----------+-----------+-------------+--------+
3 rows in set (0.00 sec)

mysql> select * from article_vote;
+------------+---------+-------+
| article_id | user_id | score |
+------------+---------+-------+
|          1 |       1 |     5 |
|          1 |       2 |     4 |
|          1 |       3 |     3 |
|          1 |       4 |     2 |
|          1 |       5 |     1 |
|          2 |       1 |     2 |
|          2 |       2 |     1 |
|          2 |       3 |     4 |
|          3 |       1 |     4 |
|          3 |       5 |     2 |
+------------+---------+-------+
10 rows in set (0.00 sec)

Полный скрипт:

drop table if exists article;
create table article
(
article_id int unsigned not null auto_increment primary key,
title varchar(255) not null,
num_votes int unsigned not null default 0,
total_score int unsigned not null default 0,
rating decimal(8,2) not null default 0
)
engine = innodb;

drop table if exists article_vote;
create table article_vote
(
article_id int unsigned not null,
user_id int unsigned not null,
score tinyint unsigned not null default 0,
primary key (article_id, user_id)
)
engine=innodb;

delimiter #

create trigger article_vote_after_ins_trig after insert on article_vote
for each row
begin
 update article set 
    num_votes = num_votes + 1,
    total_score = total_score + new.score,
    rating = total_score / num_votes  
 where 
    article_id = new.article_id;
end#

delimiter ;

insert into article (title) values ('article 1'),('article 2'), ('article 3');

insert into article_vote (article_id, user_id, score) values
(1,1,5),(1,2,4),(1,3,3),(1,4,2),(1,5,1),
(2,1,2),(2,2,1),(2,3,4),
(3,1,4),(3,5,2);

select * from article;
select * from article_vote;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...