Cette question concerne les meilleures pratiques en architecture.
Notre architecture actuelle
J'ai une classe PHP qui accède à MySQL pour les informations utilisateur. Appelons ça User
. User
est accessible plusieurs fois, nous avons donc implémenté des couches de mise en cache pour réduire la charge.
La première couche est ce que nous appelons le cache "par demande". Une fois les données récupérées de MySQL, nous stockons les données dans une propriété privée de User
. Toute demande ultérieure de données renvoie la propriété au lieu de demander à nouveau les données à MySQL.
Étant donné que la demande Web vit et meurt sur une base par demande, ce cache empêche uniquement l'application d'accéder à MySQL plus d'une fois dans une seule demande.
Notre deuxième couche est Memcached. Lorsque la propriété privée est vide, nous vérifions d'abord Memcached pour les données. Si Memcached est vide, nous interrogeons MySQL pour les données, mettons à jour Memcached et mettons à jour la propriété privée de User
.
La question
Notre application est un jeu, et il est parfois impératif que certaines données soient aussi à jour que possible. En l'espace d'environ cinq minutes, une demande de lecture des données utilisateur peut se produire 10 ou 11 fois; alors une mise à jour peut se produire. Les demandes de lecture suivantes doivent être à jour ou les mécanismes de jeu échouent.
Donc, ce que nous avons fait, c'est implémenter un morceau de code qui est exécuté lorsqu'une mise à jour de la base de données se produit. Ce code définit la clé dans Memcached avec les données mises à jour, de sorte que toutes les demandes ultérieures à Memcached sont à jour.
Est-ce optimal? Y a-t-il des problèmes de performances ou d'autres "accrochages" dont nous devrions être conscients lorsque nous essayons de maintenir une sorte de "cache vivant" comme celui-ci?
la source
Réponses:
Ma recommandation est d'examiner votre profil d'utilisation et vos exigences pour le cache.
Je ne vois aucune raison pour laquelle vous laisseriez des données périmées dans memcached. Je pense que vous avez choisi la bonne approche, c'est-à-dire: mettre à jour la base de données.
Dans tous les cas, vous allez avoir besoin d'un wrapper sur votre mise à jour DB (ce que vous avez fait). Votre code pour mettre à jour l'utilisateur dans la base de données et dans la RAM devrait également faire un push vers memcached, ou une expiration dans memcached.
Par exemple - si vos utilisateurs effectuent normalement une mise à jour une fois par session dans le cadre de la déconnexion, il n'y a pas grand-chose à mettre à jour les données dans le cache (par exemple un score élevé) - vous devez l'expirer immédiatement.
SI toutefois, ils vont mettre à jour les données (par exemple, l'état actuel du jeu), puis 0,2 seconde plus tard, vous obtiendrez immédiatement une page PHP qui demandera les données, vous voudriez qu'elles soient fraîches dans le cache.
la source
Je ne m'y attarderais pas comme vous l'avez dit. Ce que vous devez faire est de décider si vous avez réellement besoin de données complètement à jour. Ensuite, si vous en avez besoin, décidez quelles parties des données doivent être à jour à tout moment et séparez-les des éléments qui peuvent être mis en cache dans votre architecture.
Par exemple, vous souhaitez probablement mettre à jour l'adresse e-mail de votre utilisateur dès qu'il la modifie, de sorte que vous n'envoyez pas de courrier à la mauvaise adresse, mais il est peu probable que la date de naissance ou le nom de famille de l'utilisateur doive être complet. à jour pour offrir une expérience utilisateur décente. (NB je n'utilise pas d'exemple d'architecture de jeu car je ne sais pas quel type de jeu viser, et je pense que celui-ci est assez facile à comprendre).
De cette façon, vous disposez de deux ensembles de données clairs: les données en cache à court et à long terme. Vous pouvez probablement vous en tirer avec une durée de cache d'une minute environ sur les données à court terme, juste pour soulager la charge sur la base de données, mais les données à long terme peuvent être laissées dans le cache sur une durée glissante aussi longtemps que c'est utilisé.
Ensuite, vous devez gérer les mises à jour. J'examinerais d'abord l'utilisation d'un déclencheur de base de données pour supprimer simplement les éléments du cache une fois qu'ils sont obsolètes. Cela forcera votre couche métier à déclencher une actualisation du cache la prochaine fois qu'elle demandera les données, libérant ainsi de l'espace dans le cache si les données ne sont pas utilisées (par exemple si un utilisateur modifie son adresse e-mail puis se déconnecte immédiatement) . Si cela va causer des problèmes de performances dans l'interface utilisateur (c'est-à-dire introduire trop de retard en attendant les actualisations du cache), vous pouvez simplement déclencher l'appel du cache une fois que l'élément est supprimé du cache. Je chercherais également à optimiser les temps de lecture de la base de données pour ce petit ensemble de données, afin de garantir que tout retard induit lors de l'actualisation du cache est minime (cela devrait être plus facile car il vous suffit de charger les données dont vous avez vraiment besoin).
Ce que je ne ferais pas, en aucun cas, est d'ajouter une méthode supplémentaire de remplissage du cache, car vous devrez alors maintenir l'appel (et les crochets API, etc.) à deux endroits.
En ce qui concerne les accrochages, la principale chose à laquelle vous devez faire attention si vous écrivez directement dans le cache est la synchronisation. Si de nombreux threads tentent de lire pendant que vous effectuez votre mise à jour silencieuse, vous risquez d'avoir de graves problèmes de données non valides, ce qui ira à l'encontre du fait d'essayer de garder les données à jour en premier lieu.
la source