从巨大的无序分数MySQL表中获得排名

时间:2016-11-01 02:06:18

标签: mysql

我有一个用于高分的MySQL表。该表包含150,000,000多个条目。

为简单起见,我们假设结构highscore (id, userId, itemId score)。 userId和得分应该是直观的,我需要itemId,因为游戏可以用100个不同的项目完成,我想找出使用​​某个项目的某个用户的阶梯排名。 (例如:使用项目67的用户123456得分为1337,因此该项目的排名为#987654)

例如,我有一个userId 12345,想看看他如何在全局梯形图列表中使用itemId 67进行排名。

在其他stackoverflow线程上,我找到了这样的解决方案:

SELECT id, userId, itemId, score, rank
  FROM
(
  SELECT id, userId, itemId, score, @n := IF(@g = score, @n, @n + 1) rank, @g := score
    FROM highscore(SELECT @n := 0) i
   ORDER BY score DESC
) q

where userId = 12345 and itemId = 67

但是这个查询需要314秒才能运行(我有idscoreuserId, itemIditemId的mysql索引。我需要一个解决方案,允许人们在该项目的运行中查找他们的全局排名。

有没有机会在合理的时间内找到能够让我在这里获得排名的查询? (< 0.1秒)如果需要,我还会对结构和指数的变化保持开放态度。

如果不可能及时得到这个,还有其他方法吗?使用cronjob每24小时克隆一次表,添加行rankByItem并为每行计算它?听起来对我来说更加不必要的工作。

我希望有人有个主意。

针对上述查询展开扩展(抱歉,我不知道如何在这里制作表格。我试图让它可读):

+------------------------------------------------------------------------------+
| id  select_type table type possible_keys key key_len ref rows filtered Extra |
+------------------------------------------------------------------------------+
| 1  PRIMARY <derived2> ALL NULL NULL NULL NULL 215011943 100.00 Using where   |
| 2  DERIVED <derived3> system NULL NULL NULL NULL 1 100.00 Using filesort     |
| 2  DERIVED highscore ALL NULL NULL NULL NULL 215033733 100.00                |
| 3  DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used            |
+------------------------------------------------------------------------------+

创建表:

CREATE TABLE `highscore` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `userId` int(10) unsigned NOT NULL,
 `itemId` smallint(3) unsigned NOT NULL,
 `date` date NOT NULL,
 `score` smallint(5) unsigned NOT NULL,
 # ...
 PRIMARY KEY (`id`),
 UNIQUE KEY `user_item` (`userId`,`itemId`),
 KEY `item` (`itemId`),
 KEY `score` (`score`)
) ENGINE=InnoDB AUTO_INCREMENT=215042396 DEFAULT CHARSET=utf8

1 个答案:

答案 0 :(得分:0)

我的猜测是研究你可以找到问题所在的EXPLAIN,但是想象一下,你可以按照tricks

来解决问题。

在我的游戏体验中,一些排名并未实时更新,因为这会耗费时间。所以你在时间窗口更新排名。

所以如果你知道排名过程需要5分钟。您每5分钟创建一个时态表

CREATE TABLE temp_rank as 
    SELECT id, 
           userId, 
           itemId, 
           score, 
           @n := IF(@g = score, @n, @n + 1) rank, 
           @g := score
    FROM highscore, (SELECT @n := 0, @g := '') i
    ORDER BY score DESC;

然后在userId中为itemIdtemp_rank创建一个索引,以便您的所有SELECT都应该立即完成。