Solutions retenues pour l'optimisation de WikiNi
Optimisation des requêtes utilisées à l'installation (
StephaneAulery?, 07/12/2006)
Ajout d'une méthode
QuerOnly? (
StephaneAulery?, 07/12/2006)
Cette méthode est destinée à executer les requêtes de type INSERT, UPDATE, ... qui n'ont pas besoin de renvoyer des données. Ainsi il est possible d'utiliser la fonction mysql_unbuffered_query qui évite à
MySQL d'engorger sa mémoire.
Ajout de la méthode
PageExist? (
StephaneAulery?, 04/12/2006)
Le principe que j'ai retenu est le suivant : plutôt que d'aller vérifier l'existence de chaque
MotWiki parmi la liste des pages créées, avec, à chaque fois une requête différente,
PageExist? fait une seule requête pour aller chercher la liste de l'ensemble des pages du wiki, garde cette liste en mémoire, et s'y référer pour chaque lien.
Cette méthode corrige une carence de la méthode Link !
Résultat : Gain d'approximativement 10 % sur le temps consommé par
MySQL. Reste à évaluer le gain sur le temps total.
Appel aléatoire et fréquence de la purge (
StephaneAulery?, 01/12/2006)
J'ai simplifié après maintes discussion avec
DidierLoiseau l'appel aléatoire de la purge en choisissant une fonction très rapide de génération de nombre aléatoire.
Optimisation de la fonction
LoadSingle? (
StephaneAulery?, 29/11/2006)
Cette méthode a été optimisée en ne faisant plus appel à la fonction
LoadAll? et en libérant la ressource
MySQL après traitement.
Systéme de Purge
Voir
ProtectionContreLeVandalisme pour plus d'informations
Voici ma version en deux requêtes (nb.: ça doit être faisable en une seule avec
MySQL4??) :
<?php
/**
* Make the purge of page versions that are older than the last version older than 3 "pages_purge_time"
* This method permits to allways keep a version that is older than that period.
*/
function PurgePages()
{
if ($days = $this->GetConfigValue("pages_purge_time")) { // is purge active ?
// let's search which pages versions we have to remove
// this is necessary beacause even MySQL does not handel multi-tables deletes before version 4.0
$sql = 'SELECT DISTINCT a.id FROM '.$this->GetConfigValue('table_prefix').'pages AS a, '
.$this->GetConfigValue('table_prefix').'pages AS b '
.'WHERE a.latest = \'N\' '
.'AND a.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY) '
.'AND a.tag = b.tag AND a.time < b.time '
.'AND b.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY)';
$ids = $this->LoadAll($sql);
if (count($ids)) { // there are some versions to remove from DB
// let's build one big request, that's better...
$sql = 'DELETE FROM '.$this->GetConfigValue('table_prefix').'pages '
.'WHERE id IN (';
foreach($ids AS $key => $line){
$sql .= ($key ? ', ':'') . $line['id']; // NB.: id is an int, no need of quotes
}
$sql .= ')';
// ... and send it !
$this->Query($sql);
}
}
}
?>
Explications :
On fait une sélection en travaillant deux fois sur la table wikini_pages simultanément. Pour ce faire, on est obligé de leur donner des alias (FROM ' . $wnPages . ' a,' . $wnPages . ' b). Je vais d'ailleurs parler de "table a" et "table b" dans la suite, bien qu'il s'agisse en fait de la même...
- MySQL prend dans la table a les entrées qui ne sont pas la dernière version (WHERE a.latest = \'N\') mais antérieures à la date de purge (AND a.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY) - il s'agit d'une optimisation)
- et cherche dans la table b une entrée ayant le même tag (AND a.tag = b.tag), mais une date de modification ultérieure à celle de l'entrée de la table a (AND a.time < b.time) et également antérieure à la date de purge (AND b.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY)).
- Du coup la version la plus récente ayant dépassé la période de purge ne sera pas supprimmée (il n'y aura pas de version dont la date est comprise entre sa date et la date correspondant à la période de purge), mais toutes les versions antérieures à celles-ci le seront.
- Normalement une requête de ce type pourrait être très longue vu le nombre de combinaisons possibles, mais grâce au DISTINCT et au fait qu'on ne sélectionne que le champ id de la table a, MySQL arrêtera de chercher des correspondances dans la table b dès qu'elle en aura trouvé une, et passera à l'entrée suivante de la table a. (sans le DISTINCT une même version pourraît se retrouver plusieurs fois dans le résultat s'il y a plusieurs versions entre sa date de modification et la date de purge) -- DidierLoiseau
- Sinon je pense que la requête de suppression devrait être très rapide (plus rapide même que celle de sélection) car MySQL n'a qu'à parcourir les éléments un à un et vérifier s'ils sont dans la liste fournie ou non. Le problème de lenteur actuel est dû au fait que la suppression se fait à l'intérieur d'une boucle (qui contient d'ailleurs deux requêtes) -- DidierLoiseau