Mise en mémoire cache des résultats paginés, purge à la mise à jour - comment résoudre le problème?

StackOverflow https://stackoverflow.com/questions/109480

Question

J'ai créé un forum et nous implémentons une solution de mise en cache apc et memcache pour enregistrer certains travaux.

J'ai commencé à implémenter la couche de cache avec des clés du type "Catégories :: getAll", et si j'avais des données propres à l'utilisateur, j'ajouterais les clés avec des éléments tels que l'ID utilisateur, afin d'obtenir . "User :: getFavoriteThreads | 1471" . Lorsqu'un utilisateur ajoutait un nouveau fil de discussion favori, je supprimais la clé de cache et recréait l'entrée.

Cependant, voici le problème:

Je voulais mettre les discussions en cache dans un forum. Assez simple, "Forum :: getThreads | $ iForumId". Mais ... avec la pagination, je devrais scinder cela en plusieurs entrées de cache, par exemple

"Forum::getThreads|$iForumId|$iLimit|$iOffset".

Ce qui est correct, jusqu'à ce que quelqu'un poste un nouveau sujet dans le forum. Il me faut maintenant supprimer toutes les clés sous " Forum :: getThreads | $ iForumId " , quelles que soient la limite et l'offset.

Quel serait un bon moyen de résoudre ce problème? Je préfère vraiment ne pas parcourir toutes les limites possibles et compenser jusqu'à ce que je trouve quelque chose qui ne correspond plus.

Merci.

Était-ce utile?

La solution

Vous pouvez également consulter le coût de stockage des données du cache, en termes d'effort et de coût de la CPU, par rapport à ce que le cache vous achètera.

Si vous constatez que 80% des vues de votre forum consultent la première page de discussions, vous pouvez alors décider de la mettre en cache uniquement. Cela signifierait que les écritures en lecture et en cache sont beaucoup plus simples à implémenter.

De même avec la liste des discussions préférées d’un utilisateur. S'il s'agit de quelque chose que chaque personne visite rarement, le cache n'améliorera peut-être pas trop les performances.

Autres conseils

Juste une mise à jour: J'ai décidé que le point de Josh sur l'utilisation des données était très bon. Il est peu probable que les utilisateurs continuent à consulter la page 50 d’un forum.

Sur la base de ce modèle, j'ai décidé de mettre en cache les 90 derniers threads de chaque forum. Dans la fonction d'extraction, je vérifie la limite et le décalage pour voir si la tranche de thread spécifiée est dans le cache ou non. S'il se trouve dans la limite du cache, j'utilise array_slice () pour récupérer la partie droite et la renvoyer.

De cette façon, je peux utiliser une seule clé de cache par forum et il faut très peu d'effort pour vider / mettre à jour le cache: -)

Je voudrais également souligner que dans d'autres requêtes plus lourdes en ressources, je suis allé avec le modèle de flungabunga, enregistrant les relations entre les clés. Malheureusement, Stack Overflow ne me laisse pas accepter deux réponses.

Merci!

J'ai réussi à résoudre ce problème en étendant la classe memcache avec une classe personnalisée (par exemple, ExtendedMemcache) qui possède une propriété protégée qui contiendra une table de hachage de groupe vers des valeurs de clé.

La méthode ExtendedMemcache- > set accepte 3 arguments ( $ strGroup , $ strKey , $ strValue ) Lorsque vous appelez set, il stockera la relation entre $ strGroup et $ strKey , dans la propriété protected, puis enregistrera la $ strKey à $ strValue dans memcache .

Vous pouvez ensuite ajouter une nouvelle méthode à la classe ExtendedMemcache appelée "deleteGroup", qui, une fois transmise, trouvera les clés associées à ce groupe et purge chaque clé à son tour.

Ce serait quelque chose comme ça: http://pastebin.com/f566e913b J'espère que tout ce qui a du sens et fonctionne pour vous.

PS. Je suppose que si vous vouliez utiliser des appels statiques, la propriété protected pourrait être enregistrée dans memcache lui-même sous sa propre clé. Juste une pensée.

Vous essayez essentiellement de mettre en cache une vue, qui va toujours devenir compliquée. Vous devriez plutôt essayer de ne mettre en cache que des données, car les données changent rarement. Ne cachez pas un forum, cachez les lignes de threads. Ensuite, votre appel à la base de données devrait simplement renvoyer une liste d'identifiants que vous avez déjà dans votre cache. L’appel de base de données va s’éclipser rapidement sur toutes les tables MyISAM et vous n’aurez alors pas à faire une jointure importante, qui consomme de la mémoire de base de données.

Une solution possible consiste à ne pas paginer le cache des threads dans un forum, mais à placer les informations de thread dans Forum :: getThreads | $ iForumId . Ensuite, dans votre code PHP, n'extrayez que ceux de votre choix pour cette page, par exemple

.
$page = 2;
$threads_per_page = 25;
$start_thread = $page * $threads_per_page;

// Pull threads from cache (assuming $cache class for memcache interface..)
$threads = $cache->get("Forum::getThreads|$iForumId");

// Only take the ones we need
for($i=$start_thread; $i<=$start_thread+$threads_per_page; $i++)
{
    // Thread display logic here...
    showThread($threads[$i]);
}

Cela signifie que vous avez encore un peu de travail à faire pour les extraire sur chaque page, mais que vous n’avez maintenant plus qu’à vous soucier d’invalider le cache à un endroit lors de la mise à jour / l’ajout d’un nouveau thread.

flungabunga: Votre solution est très proche de ce que je recherche. La seule chose qui m'empêche de faire cela est de devoir stocker les relations dans Memcache après chaque demande et de les charger à nouveau.

Je ne suis pas sûr de la performance que cela aurait comme conséquence, mais cela semble un peu inefficace. Je vais faire des tests et voir comment ça marche. Merci pour une suggestion structurée (et un code à afficher, merci!).

Soyez très prudent lorsque vous effectuez ce type d’optimisation sans avoir des faits concrets à évaluer.

La plupart des bases de données ont plusieurs niveaux de caches. Si ceux-ci sont réglés correctement, la base de données fera probablement beaucoup mieux le travail de mise en cache que vous ne le pouvez vous-même.

En réponse à flungabunga:

Une autre façon de mettre en œuvre un groupement consiste à insérer le nom du groupe plus un numéro de séquence dans les clés elles-mêmes et à incrémenter le numéro de séquence de manière à "effacer". le groupe. Vous stockez le numéro de séquence valide actuel de chaque groupe dans sa propre clé.

par exemple

get seqno_mygroup
23

get mygroup23_mykey
<mykeydata...>
get mygroup23_mykey2
<mykey2data...>

Ensuite, pour "supprimer" le groupe simplement:

incr seqno_mygroup

voila:

get seqno_mygroup
24

get mygroup24_mykey
...empty

etc ..

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