为350,000多名用户计算“排名”的有效方法

时间:2012-10-20 21:18:37

标签: php mysql performance for-loop

  

可能重复:
  Mysql rank function

我正在建立一个排名网站,每个用户将根据级别拥有唯一的排名。 #1是最好的排名,因为有35万用户,最差排名将是#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名用户,这往往会非常缓慢。基本上,在数据库中, Rank (“ORDER BY`Cop` DESC”)是他们的级别,因此查询将对它们进行排序。不幸的是,其余的过程很慢。

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

3 个答案:

答案 0 :(得分:2)

您为新用户计算NewNum,然后在此

之后递增所有数字
 UPDATE Members SET Rank = Rank+1  WHERE Rank > NewNum

编辑将RankNum更改为Rank。我在编辑后审核了问题

答案 1 :(得分:1)

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

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

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

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

现在查询ScoreMap表:

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

这是您的新用户的RankNum。得分低于新用户的每个人的Rank都会增加1,您可以在登录时立即定期更新,将缓存放在前面等等。

PS:将你的Rank评分函数规范化为0到非常高的数字(例如万亿)之间的BIGINT。这将有助于您在插入新表时避免冲突。你想在这个范围内进行均匀分布拍摄,因为你基本上使用得分作为散列函数。

答案 2 :(得分:0)

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

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

同时更新和处理订单。