2009-02-04 6 views
1

PHPWiki a une requête lente de 5 secondes chaque fois que vous enregistrez une modification de page. La requête pris souvent dans le « mysql-slow.log » est:Requête MySQL lente. Que devrais-je indexer?

INSERT INTO wikiscore 
SELECT w1.topage, COUNT(*) 
FROM wikilinks AS w1, wikilinks AS w2 
WHERE w2.topage=w1.frompage 
GROUP BY w1.topage; 

Les indices actuels sont les suivants:

table "wikilinks" has a primary index on "frompage" and "topage" 
table "wikiscore" has a primary index on "pagename" and "score" 

Comment pourrais-je reformuler la requête SELECT pour revenir plus rapidement les mêmes résultats? Comment pourrais-je changer les index afin que cette requête soit plus rapide? Ma pensée est qu'elle pourrait être sur-indexée?

J'ai chronométré le résultat de la partie SELECT de la requête seulement et cela prend 1-2 secondes seul. L'INSERT doit prendre le reste de ce temps.

Il y a un décalage lors de l'enregistrement des pages que je voudrais éliminer. Je n'ai pas la possibilité de passer à un autre moteur wiki (ou une version de PHPwiki) en raison de la quantité de modifications qui ont été faites.

Des idées?

modifier ---

Les résultats de « Explain » sur la partie SELECT de la requête sont les suivants:

SIMPLE 
w2 
index 
PRIMARY 
204 
31871 
Using index; Using temporary; Using filesort 

SIMPLE 
w1 
ref 
PRIMARY 
PRIMARY 
102 
phpwiki.w2.topage 
14 
Using index 
+0

Le journal des requêtes lentes dit maintenant: # Query_time: 4 Lock_time: 0 Rows_sent: 0 Rows_examined: 416659 avant changé l'indice (voir les commentaires ci-dessous) ce journal a déclaré: # Query_time: 5 Lock_time: 0 Rows_sent: 0 Rows_examined: 445641 – jjclarkson

Répondre

3

table "wikiliens" a un index primaire sur "FromPage" et "toPage"

WHERE w2.topage=w1.frompage 

Cette condition ne peut être recherchée sur l'indice composite décrit ci-dessus. Changez l'ordre (créez un index sur topage, frompage) ou créez un index supplémentaire sur topage. Le problème à la racine de ces problèmes est que les rangs de chaque page du système sont mis à jour à chaque modification.

Ce système de classement me semble un peu étrange: il compte le lien vers les liens, pas les liens eux-mêmes.

Si 1000 pages contiennent un lien vers Moscou et seulement Moscou liens vers Beket étang, puis l'étang obtenir 1000 points et Moscou ne reçoit pas de points du tout, bien que tout le monde connaît Moscou et aucun des l'étang. Je pense que ce n'est pas ce que vous vouliez dire. Très probablement, il devrait ressembler à ça:

INSERT INTO 
     wikiscore 
SELECT 
     linked.topage, COUNT(*) AS cnt 
FROM wikilinks current, wikilinks linked 
WHERE [email protected]_page 
     AND linked.topage = current.topage 
GROUP BY 
     linked.topage 
ON DUPLICATE KEY UPDATE 
     score = cnt; 

Cela somme tous les liens vers toutes les pages référencées de la page en cours, qui semble être ce que vous voulez.Dans ce cas, vous devrez vous débarrasser de score dans PRIMARY KEY sur wikiscore, mais je ne vois pas l'intérêt de le mettre de toute façon là.

Si vous voulez accélérer le classement des requêtes, vous créez des indices comme ça:

ALTER TABLE wikilinks ADD CONSTRAINT pk_wikilinkes_fromto PRIMARY KEY (frompage, topage); 

CREATE INDEX ix_wikilinks_topage ON wikilinks (topage); 

ALTER TABLE wikiscore ADD CONSTRAINT pk_wikiscore_pagename PRIMARY KEY (pagename); 

CREATE INDEX ix_wikiscore_score ON wikiscore (score); 
+0

Vous avez besoin d'un index avec topage comme colonne principale. Il peut permettre des doublons et ne doit pas inclure le fromage ou toute autre colonne. –

+0

Son EXPLAIN indique qu'un index est utilisé.Il me semble que la requête pourrait partir de "w2" et utiliser l'index pour rechercher des lignes dans "w1" par frompage. –

+0

J'ai changé l'index sur wikilinks pour "topage" et "frompage" et vu un temps de requête plus rapide sur la partie SELECT. Cependant l'EXPLAIN sur celui-ci semblerait indiquer que plus de rangées sont regardées. Je ne suis pas sûr de ce que cela signifie – jjclarkson

2

Il devrait être utile d'utiliser la déclaration EXPLAIN pour savoir quelle partie de votre requête prend le plus de temps. Ensuite, vous pouvez décider quelles mesures doivent être prises pour optimiser votre requête.

1

Je vais avoir un peu de mal à comprendre ce que la requête ne. Je comprends qu'il trouve des liens d'une page à l'autre. Donc w1.topage est les liens vers cette page, et w1.frompage est des liens de cette page vers d'autres pages. Et donc l'insertion ajoute la page et le nombre de liens vers cette page.

Suis-je sur la bonne voie?

Votre principal problème est cette ligne:

FROM wikilinks AS w1, wikilinks AS w2 

Si vous pensez que la table a 1000 entrées, le moteur de recherche doit correspondre 1000 entrées à l'autre entrée, il est donc saisir 1000 × 1000 lignes (sans compte tenu de la clause WHERE ou GROUP). Comme vous obtenez de plus en plus d'entrées, le temps de requête augmente de façon exponentielle. (kaboom)

En outre, vous ne modifiez qu'une seule page, vous devriez donc pouvoir raisonnablement supposer que les liens vers cette page particulière ne changeront pas, mais que des liens pourraient en découler. Ainsi, au lieu d'écrire la table wikilinks à chaque mise à jour, supprimez les liens de cette page particulière, puis réinsérez tous les liens de cette page vers d'autres.

+0

À l'heure actuelle wikilinks a 31871 lignes. Mais cette requête crée un score de combien de liens sont à chaque page de toutes les pages. J'ai l'intention de voir si je peux juste interroger les liens pour la page enregistrée seulement et mettre à jour une rangée dans la table de wikiscore. – jjclarkson

1

La réponse de Quassnoi vous donnera une certaine vitesse sur le SELECT. Si INSERT prend encore quatre secondes, l'ajout d'index ne va rien aider. Vous pourriez peut-être couper beaucoup de données du processus en ajoutant AND COUNT (*)> 0 à votre SELECT, s'il est souhaitable d'omettre des pages avec zéro compte de liens entrants.

Vous pouvez obtenir au moins une amélioration en supprimant les index de wikiscore. Votre clé primaire sur le nom de page, le score n'a pas vraiment de sens (vous pouvez stocker plusieurs scores de la même page, mais pas s'ils sont score?), Et devrait probablement être une clé primaire sur pagename. S'il existe d'autres index, vous pourrez peut-être vous en débarrasser.

Si le wikiscore n'est pas créé récemment lorsque cela se produit, vous pourriez avoir un avantage à lancer une OPTIMIZE TABLE.

Ce qui serait vraiment génial, cependant, est de savoir si vous avez changé toute la théorie derrière cette requête afin que, au lieu de reconstruire l'ensemble de la table de wikiscore chaque fois qu'une page est enregistrée, vous mettez à jour que le score de la page enregistrée et les pages vers lesquelles il est lié.

+0

Oui, cela prend tout son sens. Je vais creuser dans le code et voir combien il faudrait pour mettre à jour le score de la page enregistrée seulement. – jjclarkson

+0

Aucun surdébit n'a été signalé dans phpmyadmin pour l'une des deux tables. – jjclarkson

0

Voilà comment j'ai modifié le code PHP en source

// update pagescore 
//old way... 
/*  
mysql_query("DELETE FROM $WikiScoreStore", $dbi["dbc"]); 
mysql_query("INSERT INTO $WikiScoreStore" 
       ." SELECT w1.topage, COUNT(*) FROM $WikiLinksStore AS w1, $WikiLinksStore AS w2" 
       ." WHERE w2.topage=w1.frompage GROUP BY w1.topage", $dbi["dbc"]); 

*/ 

//delete this pagescore    
mysql_query("DELETE FROM $WikiScoreStore WHERE pagename='$frompage'", $dbi["dbc"]); 
//insert just this pagescore 
mysql_query("INSERT INTO $WikiScoreStore" 
        ." SELECT w1.topage, COUNT(*) FROM $WikiLinksStore AS w1, $WikiLinksStore AS w2" 
       ." WHERE w2.topage=w1.frompage AND w1.topage='$frompage' GROUP BY w1.topage", $dbi["dbc"]); 

de PHPWiki Depuis ce changement de code et les réglages d'index, je n'ai pas de requêtes lentes. Merci.!

+0

Peut-être que vous devriez diriger cette solution vers les mainteneurs de PHPWiki, afin qu'ils puissent l'appliquer dans le projet. – Tiago