mysql - 从巨大的无序分数MySQL表中获取排名

标签 mysql

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

为了简单起见,我们假设结构为highscore (id, userId, itemId Score)。 userId 和 Score 应该很直观,但我需要 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

最佳答案

我的猜测是研究EXPLAIN,你可以找到问题所在,但想象一下,你可以按照一些技巧解决它

在我的游戏体验中,有些排名没有实时更新,因为这很耗时。因此,您可以更新时间窗口中的排名。

所以如果你知道排名过程需要 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;

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

关于mysql - 从巨大的无序分数MySQL表中获取排名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40353180/

相关文章:

php - 'mysqldump' 未被识别为内部或外部命令

mysql - 使用左连接的 SQL 查询中的 LIMIT

php - MySQL - 根据用户输入动态显示月份

php - 我怎样才能得到按字母顺序分页的mysql结果

php - mysql 更新查询将多个条目插入单列时出错

MySQL Replication——查询是否被 master 修改过?

mysql - MySQL 中使用连接的慢查询

mysql排序顺序操作

mysql - Sails 和 MySQL 正在创建重复的行

MySQL - 使用共享 key 对来自三个表的结果进行分组