Mysql 如何提高我的投票系统的性能?

Mysql 如何提高我的投票系统的性能?,mysql,sql,performance,voting-system,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, P

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

这个应用程序是由另一个开发者开发的,现在这个网站越来越大,性能是一个重要的考虑因素

我有下表:

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秒才能返回

我不能在这里使用缓存,因为我需要实时检查是否允许投票并增加计数

我不能改变太多的应用程序逻辑,因为它依赖于应用程序中到处使用的太多依赖性(它设计得很糟糕)

因此,我正在寻找改进性能的选项,甚至是一些

编辑:我在标记列上有一个索引

大约有2000000行,所有令牌几乎都是唯一的


编辑

我用你所有的建议做了一个基准测试:

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上的索引)


这很奇怪,我认为计数(id)会使查询速度加快一点。

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

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

一般来说,您应该为经常运行的查询的where子句中使用的大型表中的列添加索引。在您的示例查询中,您需要在token列上设置一个。看起来您正在使用MySQL数据库,下面是该数据库的create table语句的重要部分:

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

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

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

听起来您应该创建一个存储汇总数据的表。这样,查询不需要每次都进行完整计数,而只需要上次求和时的计数。(取决于您的整个系统,如果从未删除行,您可能会有一个与下面非常类似的表)

然后,当您在投票中插入一行时,您也可以调用

UPDATE voteCounts
set `count` = `count` +1
WHERE
token = '012345' ;

听起来您应该创建一个存储汇总数据的表。这样,查询不需要每次都进行完整计数,而只需要上次求和时的计数。(取决于您的整个系统,如果从未删除行,您可能会有一个与下面非常类似的表)

然后,当您在投票中插入一行时,您也可以调用

UPDATE voteCounts
set `count` = `count` +1
WHERE
token = '012345' ;

我对您当前的实现并没有太多关注,但我对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;
完整脚本:

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;

希望能有所帮助:)

我对您当前的实现没有太多的关注,但我对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;
完整脚本:

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;


希望有帮助:)

你的数据库有索引吗?尼克·克莱格,是你吗?@E先生,不是我不是尼克·克莱格,他是谁?:)有几排?解释计划显示了什么?您是否满足于使用触发器?这些数据是永久保存的还是可以存档的?@JohnT我们刚刚在英国举行了一次关于选举改革的会议(被拒绝),由自由民主党(联合政府的一部分)及其领导人、副总理尼克·克莱格提出了改革计划。你的数据库有任何索引吗?尼克·克莱格,是你吗?@E先生,不是我不是尼克·克莱格,他是谁?:)有几排?解释计划显示了什么?您是否满足于使用触发器?数据是永久保存还是存档?@JohnT我们刚刚在英国举行了一次关于选举改革的会议(被拒绝),自由民主党(联合政府的一部分)及其领导人、副首相尼克·克莱格提出了改革计划。@Johan你是如何修改格式的。查看您的修订版,我看不出与我的修订版有什么不同。我将
主键
添加到字段
标记
。因为否则JohnT(又名Nick Clegg)的查询速度仍然很慢。@Johan您是如何修改格式的。查看您的修订版,我看不出与我的修订版有什么不同。我将
主键
添加到字段
标记
。因为否则JohnT(又名Nick Clegg)仍然会有一个缓慢的查询。表中有多少行,以及
标记的选择性如何?@JohnT,尝试不在标记中添加索引,而是在标记和id中添加索引。@JohnT,我还认为您应该将查询更改为类似
选择count(id)AS nb FROM vote WHERE(token='001234567890123456788901234567890');
@JohnT,或to
从投票中选择计数(id)作为nb,其中(token='001234567890123456788901234567890')按令牌分组;
@JohnT,不要忘记重新启动MySQL服务器或运行一些flush index命令。表中有多少行,以及
token
的选择性如何?@JohnT,尝试不在token中添加索引,而是在token和id中添加索引。@JohnT,我还认为您应该将查询更改为类似
选择计数(id)的内容作为来自投票地点的nb(令牌='001234567890123456788901234567890');
@JohnT,或
选择计数(id)作为来自投票地点的nb(令牌='0012345678901234568901234567890')按令牌分组;
@JohnT,并且不要忘记重新启动MySQL服务器或运行一些刷新索引命令。