我的网站有投票系统(喜欢/不喜欢)。
该应用程序由另一位开发人员开发,现在该网站越来越大,性能也得到了认真考虑。
我有下表:
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)会加快查询速度。
答案 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;
希望有所帮助:)