1

投票システム (好き/嫌い) を備えた Web サイトを持っています。

アプリケーションは別の開発者によって開発されており、現在、Web サイトはますます大きくなっており、パフォーマンスが真剣に考慮されています。

私は次の表を持っています:

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;

トークン列は、各ユーザー/投票/日付を識別するために使用されます。これは、ユーザーの指紋の一部である一意のトークンであり、一度投票して投票タイプを変更できるようにします。

最も遅いクエリの 1 つは次のとおりです。

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 

3 番目のクエリが最も速い場合もありますが、最悪の場合もあります。

各クエリが20回実行される場所で10回実行しました

このベンチマークを INDEXES なしで実行しました (上の 1 つを除く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 句で使用される大きなテーブルの列には、インデックスを追加する必要があります。例のクエリでは、トークン列に 1 つ必要です。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 に答える