J'ai dû concevoir quelques stratégies de pagination en utilisant PHP et MySQL pour un site qui génère plus d'un million de pages vues par jour. J'ai suivi la stratégie en plusieurs étapes :
Indexation multi-colonne. J'aurais dû faire cela en premier lieu avant d'essayer une vue matérialisée.
Génération d'une vue matérialisée. J'ai créé une tâche cron qui faisait une dénormalisation commune des tables de documents que j'utilisais. Je ferais un SELECT ... INTO OUTFILE ...
puis créer la nouvelle table et la remplacer :
SELECT ... INTO OUTFILE '/tmp/ondeck.txt' FROM mytable ...;
CREATE TABLE ondeck_mytable LIKE mytable;
LOAD DATA INFILE '/tmp/ondeck.txt' INTO TABLE ondeck_mytable...;
DROP TABLE IF EXISTS dugout_mytable;
RENAME TABLE atbat_mytable TO dugout_mytable, ondeck_mytable TO atbat_mytable;
Cela a permis de réduire le temps de verrouillage sur l'écriture de mytable
au minimum et les requêtes de pagination pouvaient être effectuées sur la vue matérialisée atbat
. J'ai simplifié ce qui précède en omettant la manipulation réelle, qui est sans importance.
Memcache. J'ai ensuite créé un wrapper autour de ma connexion à la base de données pour mettre en cache ces résultats paginés dans memcache. Cela a grandement amélioré les performances. Cependant, ce n'était toujours pas suffisant.
Génération par lots. J'ai écrit un démon PHP et extrait la logique de pagination. Il détectait les changements dans mytable
et régénérait périodiquement depuis le premier enregistrement modifié jusqu'au plus récent tous les pages dans le système de fichiers du serveur web. Avec un peu de mod_rewrite
, je pouvais vérifier si la page existait sur le disque et la servir. Cela m'a également permis de tirer pleinement parti du reverse proxying en laissant Apache détecter les en-têtes If-Modified-Since
et répondre avec des codes de réponse 304
. (Évidemment, j'ai supprimé toute option permettant aux utilisateurs de sélectionner le nombre de résultats par page, une fonctionnalité sans importance.)
Mise à jour : *RE `count():** Lors de l'utilisation de tables MyISAM,
COUNT` n'a pas posé de problème lorsque j'ai pu réduire la contention lecture-écriture sur la table. Si j'utilisais InnoDB, je créerais un déclencheur qui mettrait à jour une table adjacente avec le nombre de lignes. Ce déclencheur ajouterait simplement +1 ou -1 en fonction des instructions INSERT ou DELETE.
RE sélecteurs de page (molettes). Lorsque je suis passé à un cache de requêtes agressif, les requêtes de molettes étaient également mises en cache, et lors de la génération par lots des pages, j'utilisais des tables temporaires - donc calculer la molette était sans problème. Beaucoup de calculs de molettes ont été simplifiés car il s'agissait d'un schéma de fichiers prévisible qui ne nécessitait en réalité que le numéro de page le plus élevé. Le numéro de page le plus bas était toujours 1.
Molette en fenêtre. L'exemple que vous donnez ci-dessus pour une molette en fenêtre (<< 4 [5] 6 >>) devrait être assez facile à réaliser sans aucune requête tant que vous connaissez votre nombre maximal de pages.