私の会社がリリースした Facebook レーシング ゲームのグローバル リーダーボードを実装する方法に取り組んでいます。私がやりたいことは、プレイヤーのユーザー ID とレースの時間を保存できるようにすることです。以下のような表があります。
+--------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+-------+
| userID | mediumint(8) unsigned | NO | PRI | 0 | |
| time | time | YES | MUL | NULL | |
+--------+-----------------------+------+-----+---------+-------+
そして、次のようなデータのサンプルセット:
+--------+----------+
| userID | time |
+--------+----------+
| 505610 | 10:10:10 |
| 544222 | 10:10:10 |
| 547278 | 10:10:10 |
| 659241 | 10:10:10 |
| 681087 | 10:10:10 |
+--------+----------+
私のクエリは PHP から来ます。無制限のリソースがあると仮定すると、次のことができます。
$q1 = "Set @rank := 0";
$q2 = "select @rank:=@rank+1 as rank,userID,time from highscore order by time asc where userID=$someUserID";
$q3 = "Set @rank := 0";
$q4 = "select @rank:=@rank+1 as rank,userID,time from highscore order by time asc where rank > $rankFromSecondQuery - 10 and rank < $rankFromSecondQuery + 10";
しかし、無限のリソースはありません。Facebook のソーシャル ゲームに参加するため、これを拡張して何百万人ものプレイヤーをサポートできるようにする必要があります。そのため、Google 中をクロールするのに数日を費やした後、クエリを次のようにまとめることができました。
$q5 = "select rank,userID,time from (select @rank:=0) r, (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where userID=$someUserID"
$q6 = "select rank,userID,time from (select @rank:=0) r, (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where rank > $rankFromFirstQuery - 10 and rank < $rankFromSecondQuery + 10";
これは機能しますが、クエリあたりの平均実行時間は約 2.3 秒で、あまりきれいではありません。
編集: $q5 と $q6 を実行すると、次のようになります。
mysql> select rank,userID,time from (select @rank:=0) r, (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where userID=11345;
+--------+--------+----------+
| rank | userID | time |
+--------+--------+----------+
| 423105 | 11345 | 12:47:23 |
+--------+--------+----------+
1 row in set (2.42 sec)
mysql> select rank,userID,time from (select @rank:=0) r, (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where rank>423100 and rank<423110;
+--------+---------+----------+
| rank | userID | time |
+--------+---------+----------+
| 423101 | 2416665 | 12:47:22 |
| 423102 | 2419720 | 12:47:22 |
| 423103 | 2426606 | 12:47:22 |
| 423104 | 2488517 | 12:47:22 |
| 423105 | 11345 | 12:47:23 |
| 423106 | 92350 | 12:47:23 |
| 423107 | 94277 | 12:47:23 |
| 423108 | 114685 | 12:47:23 |
| 423109 | 135434 | 12:47:23 |
+--------+---------+----------+
9 rows in set (2.58 sec)
以下は、Explain 拡張ブロック $q5 で、$q6 のブロックはほぼ同じに見えます。
mysql> explain select rank,userID,time from (select @rank:=0) r, (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where userID=11345;
+----+-------------+------------+--------+---------------+----------+---------+------+---------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+----------+---------+------+---------+----------------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 1 | PRIMARY | <derived3> | ALL | NULL | NULL | NULL | NULL | 2500000 | Using where |
| 3 | DERIVED | highscore | index | NULL | idx_time | 4 | NULL | 2500842 | Using index |
| 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+---------------+----------+---------+------+---------+----------------+
したがって、最終的に私が本当にやりたいことは、これを 1 つのクエリにまとめて、CPU の高いサーバーを 1 つまたは 2 つ使用して実行時間を短縮できるようにすることです。それか、テーブル内のすべての行にヒットしている Explain ブロックの serve3 行に関連付けられているクエリの一部のインデックスにヒットする方法を見つけたいと思います。
これまでに成功せずに試したいくつかのクエリを次に示します。
select rank,userID,time from (select @rank:=0) r, (select @playerRank := rank from (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where userID=11345) as myFoo where @playerRank>423100 and @playerRank<423110;
select rank,userID,time from (select @playerRank := rank from (select @rank := 0) r, (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where userID=11345) as myFoo where @playerRank>423100 and @playerRank<423110;
select * from (select @rank:=0) r, (select @playerRank := userID from (select @rank:=@rank+1 as rank,userID,time from highscore order by time asc) as myMine where userID=11345) as myFoo where @playerRank>423100 and @playerRank<423110;
最初の 2 つのゲームでは、「ERROR 1054 (42S22): Unknown colum 'rank' in 'field list'」エラーが発生し、3 番目のゲームでは、探していたデータではなく空のセットが返されました。
上記の 2 つのクエリをインデックスにヒットさせて実行時間を短縮する方法、または 2 つのクエリを 1 つに結合する方法のいずれかを知っている人はいますか? また、MySQL 構成設定の微調整や Percona などの使用経験があり、その経験を共有したい場合は、Percona のようなものを使用するなどのチューニング/最適化にもオープンです。