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服务器或运行一些刷新索引命令。