Question

J'ai un site Web qui a le classement de l'utilisateur comme une partie centrale, mais le nombre d'utilisateurs a augmenté à plus de 50 000 et il met une pression sur le serveur en boucle à travers toutes les mettre à jour le rang toutes les 5 minutes. Y at-il une meilleure méthode qui peut être utilisé pour mettre à jour facilement les rangs au moins toutes les 5 minutes? Il ne doit pas être avec php, il pourrait être quelque chose qui fonctionne comme un script Perl ou quelque chose si quelque chose comme ça serait en mesure de faire le travail mieux (même si je ne sais pas pourquoi ce serait, tout simplement quitter mon options ouvertes ici).

Voici ce que je fais actuellement à mettre à jour les rangs:

$get_users = mysql_query("SELECT id FROM users WHERE status = '1' ORDER BY month_score DESC");
$i=0;
while ($a = mysql_fetch_array($get_users)) {
    $i++;
    mysql_query("UPDATE users SET month_rank = '$i' WHERE id = '$a[id]'");
}

UPDATE (solution):

Voici le code de la solution, qui prend moins de 1/2 d'une seconde pour exécuter et mettre à jour toutes les 50.000 lignes (faire rang la clé primaire comme suggéré par Tom Haigh).

mysql_query("TRUNCATE TABLE userRanks");
mysql_query("INSERT INTO userRanks (userid) SELECT id FROM users WHERE status = '1' ORDER BY month_score DESC");
mysql_query("UPDATE users, userRanks SET users.month_rank = userRanks.rank WHERE users.id = userRanks.id");
Était-ce utile?

La solution

Faire userRanks.rank une clé primaire auto-incrémentation. Si vous insérez ensuite userids dans userRanks dans l'ordre décroissant de rang, il incrémente la colonne rank sur chaque ligne. Cela devrait être extrêmement rapide.

TRUNCATE TABLE userRanks;
INSERT INTO userRanks (userid) SELECT id FROM users WHERE status = '1' ORDER BY month_score DESC;
UPDATE users, userRanks SET users.month_rank = userRanks.rank WHERE users.id = userRanks.id;

Autres conseils

Ma première question serait: pourquoi vous faites cette opération de type de vote tous les cinq minutes

?

Certes, classer les changements seront en réponse à un événement et vous pouvez localiser les changements à quelques lignes dans la base de données au moment où cet événement se produit . Je suis certain que l'ensemble de la base d'utilisateurs de 50 000 ne modifie pas le classement toutes les cinq minutes.

Je suppose que la "status = '1'" indique qu'un rang de l'utilisateur a changé si, au lieu de définir quand l'utilisateur déclenche un changement de rang, pourquoi ne calcule pas le rang à ce moment-là?

Cela semble être une meilleure solution que le coût de reclassement serait amorti sur toutes les opérations.

Maintenant, je peux avoir mal compris ce que vous vouliez dire par le classement dans ce cas, ne hésitez pas à me détromper.

Une alternative simple pour la mise à jour en vrac pourrait être quelque chose comme:

set @rnk = 0;
update users 
set month_rank = (@rnk := @rnk + 1)
order by month_score DESC

Ce code utilise une variable locale (@rnk) qui est incrémenté à chaque mise à jour. Étant donné que la mise à jour se fait sur la liste ordonnée de rangées, la colonne de month_rank sera réglé sur la valeur incrémentée pour chaque ligne.

Mise à jour de la table des utilisateurs ligne par ligne sera une tâche fastidieuse. Il serait mieux si vous pouvez réorganiser votre requête afin que les mises à jour de ligne par ligne ne sont pas nécessaires.

Je ne suis pas sûr à 100% de la syntaxe (comme je l'ai jamais utilisé MySQL avant) mais voici un échantillon de la syntaxe utilisée dans MS SQL Server 2000

DECLARE @tmp TABLE
(
    [MonthRank] [INT] NOT NULL,
    [UserId] [INT] NOT NULL,
)

INSERT INTO @tmp ([UserId])
SELECT [id] 
FROM [users] 
WHERE [status] = '1' 
ORDER BY [month_score] DESC

UPDATE users 
SET month_rank = [tmp].[MonthRank]
FROM @tmp AS [tmp], [users]
WHERE [users].[Id] = [tmp].[UserId]

Dans MS SQL Server 2005/2008, vous utiliserez probablement un CTE.

Chaque fois que vous avez une boucle de taille importante qui exécute des requêtes à l'intérieur, vous avez une très probable antimodèle. Nous pourrions examiner le schéma et l'exigence de traitement avec plus d'informations, et voir si nous pouvons faire tout le travail sans une boucle.

Combien de temps faut-il passer le calcul des scores, par rapport à l'attribution du classement?

Votre problème peut être traité de plusieurs façons. Honnêtement plus de détails de votre serveur peut vous orienter dans une direction tout à fait différente. Mais le faire de cette façon vous causez 50.000 petits cadenas sur une table de lecture fortement. Vous pouvez obtenir de meilleures performances avec une table de mise en scène, puis une sorte de transition. Insère dans une table n'est en train de lire vont probablement être mieux.

Considérons

mysql_query("delete from month_rank_staging;");
while(bla){
  mysql_query("insert into month_rank_staging values ('$id', '$i');");
}
mysql_query("update month_rank_staging src, users set users.month_rank=src.month_rank where src.id=users.id;");

Ça va causer un verrou (plus) sur la table, mais pourrait améliorer votre situation. Mais encore une fois, cela peut être ainsi hors de la base en fonction de la véritable source de votre problème de performance. Vous devriez probablement plus profond à vos journaux, configuration de MySQL, les connexions de base de données, etc.

Peut-être que vous pouvez utiliser tessons par le temps ou autre Catégorie. Mais lisez cette attentivement avant .. .

Vous pouvez diviser le traitement de rang et l'exécution de la mise à jour. Donc, courir à travers toutes les données et traiter la requête. Ajouter chaque instruction de mise à jour à un cache. Lorsque le traitement est terminé, exécutez les mises à jour. Vous devriez avoir la partie WHERE de mise à jour de référence une clé primaire définie sur auto_increment, comme mentionné dans d'autres postes. Cela permettra d'éviter les mises à jour d'interférer avec la performance du traitement. Il permettra également d'empêcher les utilisateurs plus tard dans la file d'attente de traitement de prendre injustement l'avantage des valeurs des utilisateurs qui ont été traités avant de les (si le rang d'un utilisateur affecte celui d'un autre). Elle empêche également la base de données de compensation de ses caches de table des CHOISIT votre code de traitement fait.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top