我正在研究如何为我公司发布的 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)
这是解释扩展块 $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 |
+----+-------------+------------+--------+---------------+----------+---------+------+---------+----------------+
所以最终,我真正想做的就是把它简化为一个查询,这样我就可以使用一两个高 CPU 服务器来缓和执行时间。要么,要么我想找出一种方法,只在查询部分中命中与表中所有行的解释块中的派生3 行相关联的索引。
到目前为止,这是我尝试过的几个查询但没有成功:
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;
前两个游戏给我一个“错误 1054 (42S22): Unknown column 'rank' in 'field list' 错误,第三个只是返回一个空集而不是我正在寻找的数据。
任何人都知道如何让我上面列出的两个查询命中索引以减少执行时间,或者如何将两个查询合并为一个,这样我只需要忍受一次痛苦的执行时间?如果有人有使用类似东西的经验并想分享他们的经验,我也愿意接受调整/优化,例如调整 MySQL 配置设置和/或使用 Percona 之类的东西。