1

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

该应用程序已由另一个开发人员开发,现在网站越来越大,性能正在受到重视。

我有下表:

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 次

我在没有任何索引的情况下运行了这个基准测试(除了一个 on id

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

4

4 回答 4

6

如果还没有索引,您应该查看索引标记列。

于 2011-05-12T20:59:56.913 回答
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' ;
于 2011-05-12T21:04:13.233 回答
1

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

CREATE TABLE `vote` (
..
  token varchar(64) collate utf8_unicode_ci NOT NULL,
  index token_ind (token),
..
) ENGINE=InnoDB;
于 2011-05-12T21:03:30.710 回答
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;

希望能帮助到你 :)

于 2011-05-13T17:43:11.480 回答