如何提高我的投票系统的表现?

时间:2011-05-12 20:55:31

标签: mysql sql performance voting-system

我的网站有投票系统(喜欢/不喜欢)。

该应用程序由另一位开发人员开发,现在该网站越来越大,性能也得到了认真考虑。

我有下表:

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行,所有令牌几乎是唯一的


修改

我根据您的所有建议运行了基准测试:

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次

我运行此基准没有任何INDEXES(id上的一个除外)

这很奇怪,我虽然COUNT(id)会加快查询速度。

4 个答案:

答案 0 :(得分:6)

如果令牌列尚未编入索引,您应该查看索引列的索引。

答案 1 :(得分:3)

听起来你应该创建一个存储求和数据的表。这样,查询不需要每次都进行完整计数,而只需要从上次求和的计数开始计数。 (根据您的完整系统,如果永远不会删除行,您可能会有一个非常类似于以下的表)

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' ;

答案 2 :(得分:1)

通常,您应该为大型表中的列添加索引,这些列用于经常运行的查询的where子句。在示例查询中,您需要在令牌列上使用一个。看起来您正在使用MySQL数据库,因此这是该数据库的create table语句的重要部分:

CREATE TABLE `vote` (
..
  token varchar(64) collate utf8_unicode_ci NOT NULL,
  index token_ind (token),
..
) ENGINE=InnoDB;

答案 3 :(得分:0)

我并没有真正关注你当前的实现,但我用于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;

希望有所帮助:)