0

可能重复:
Mysql rank 函数

我正在制作一个排名网站,每个用户都会有一个基于级别的唯一排名。#1 是最好的排名,因为有 350,000 名用户,所以最差的排名将是 #350000。

任何用户都不能拥有与其他任何人相同的排名。现在,当添加新用户时,会计算他们的级别。计算后,脚本将“重建”排名,逐个遍历每个用户,并计算他们的新排名。

以下是对查询的解释:

  • “id”是成员在数据库中的ID
  • “RankNum”是他们的等级。它需要重命名。
  • “排名”是他们的唯一排名,#1 到 #350000。

这是我当前的脚本:

function RebuildRanks() {
    $qD = mysql_query("SELECT `Id`,`RankNum` FROM `Members` ORDER BY `Rank` DESC, `Id` ASC");
    $rowsD = mysql_num_rows($qD);

    $curRank = 0;

    for($x = 1; $x <= $rowsD; $x++) {
        $rowD = mysql_fetch_array($qD);

        $curRank++;
        if($rowD['RankNum'] != $curRank) {
            if($curRank != 0) {
                mysql_query("UPDATE `Members` SET `RankNum`='$curRank' WHERE `Id`='".$rowD['Id']."'");
            }
        }
    }

    return true;
}

对于 350,000 名用户,这往往会运行得很慢。本质上,在数据库中排名(“ORDER BY `Rank` DESC”)是他们的级别,因此查询将对他们进行排序。不幸的是,该过程的其余部分很慢。

以这种方式处理所有 350,000 个用户大约需要 97 秒。是否有任何可能的解决方案可以更有效、更快速地运行它?

4

3 回答 3

2

您为新用户计算 NewNum,然后在此之后递增所有数字

 UPDATE Members SET Rank = Rank+1  WHERE Rank > NewNum

编辑将 RankNum 更改为 Rank。我在您编辑后查看了问题

于 2012-10-20T21:32:40.660 回答
1

随着用户群的增长,您的 RebuildRanks() 函数将继续成为您的性能瓶颈;你最好的办法是设计一个系统,避免每次新用户加入时重新排名。这是一个可能有帮助的示例解决方案:

添加一个新表 (ScoreMap),它是您的用户按分数排序的哈希(使用此术语而不是“排名”来消除歧义)。

CREATE TABLE ScoreMap (
  Score BIGINT NOT NULL, 
  Id BIGINT NOT NULL, 
  UNIQUE INDEX (Score), 
  UNIQUE INDEX (Id)
);

当新用户加入时,计算他们的分数并插入到这个新表中。如果在 Score 上发生冲突,您可以解决这个问题(如果没有两个用户可以拥有相同的 RankNum,那么您必须为 Score 提供某种打破平局的功能;根据需要在此表中移动用户)。

现在查询 ScoreMap 表:

SELECT COUNT(*) FROM ScoreMap WHERE Score >= [the score you just calculated]

那是您的新用户的RankNum。分数比新用户差的每个人的排名都会增加 1,您可以在他们登录时立即更新,定期更新,在前面放置缓存等。

PS:将您的排名评分函数标准化为介于 0 和非常高的数字(例如一万亿)之间的 BIGINT。这将帮助您在插入新表时避免冲突。您希望在此范围内实现均匀分布,因为您实际上是在使用分数作为散列函数。

于 2012-10-20T21:58:44.480 回答
0

一种方法当然是使用如下查询语句(未经测试):

UPDATE `Members`
SET `RankNum` = (@newRank := @newRank + 1)
ORDER BY `Rank` DESC, `Id` ASC, @newRank := 0

同时更新和处理订单。

于 2012-10-20T22:22:46.857 回答