Question

Nous essayons de mettre à jour les objets memcached lorsque nous écrivons dans la base de données pour éviter d'avoir à les lire à partir de la base de données après insertion / mise à jour.

Pour notre objet post de forum, nous avons un champ ViewCount contenant le nombre de fois qu'un post est vu.

Nous craignons d'introduire une condition de concurrence critique en mettant à jour l'objet memcached, car le même message pourrait être affiché en même temps sur un autre serveur de la batterie.

Vous avez une idée de la façon de traiter ce type de problèmes - il semblerait qu’une sorte de verrouillage soit nécessaire, mais comment le faire de manière fiable sur tous les serveurs d’une batterie?

Était-ce utile?

La solution

Si vous traitez avec des données qui ne nécessitent pas nécessairement d'être mises à jour en temps réel, et pour moi le nombre de vues est l'un d'entre elles, vous pouvez ajouter un champ d'expiration aux objets qui sont stockés dans memcache.

Une fois que l'expiration est arrivée, la base de données sera lue et la nouvelle valeur sera lue, mais jusque-là, elle restera vide.

Bien sûr, pour les nouveaux articles, vous voudrez peut-être que ce soit mis à jour plus souvent, mais vous pouvez coder pour cela.

Memcache ne stocke qu’une copie de votre objet dans l’une de ses instances, mais pas dans la plupart d’entre elles. Je ne me soucierais donc pas du verrouillage d’objets ni de quoi que ce soit. C’est à la base de données à gérer, pas à votre cache.

Modifier:

Memcache ne garantit pas que vos données ne seront pas écrasées lorsque vous configurez des serveurs variés.

Dans les documents memcache:

  • Une série de commandes n'est pas atomique. Si vous émettez un "get" contre un élément, utilisez les données, puis souhaitez le "replacer" dans memcached, vous n'êtes pas assuré d'être le seul processus travaillant sur cette valeur. En parallèle, vous pourriez éventuellement écraser une valeur définie par quelque chose d'autre.

Conditions de course et données obsolètes

Lors de la conception de votre application pour la mise en cache des données, gardez à l’esprit que vous devez gérer les conditions de concurrence et les données obsolètes occasionnelles.

Supposons que vous mettiez en cache les cinq derniers commentaires pour les afficher dans une barre latérale de votre application. Vous décidez que les données ne doivent être actualisées qu'une fois par minute. Cependant, vous avez oublié de vous rappeler que cet affichage dans la barre latérale est rendu 50 fois par seconde! Ainsi, une fois que 60 secondes se sont écoulées et que le cache a expiré, plus de 10 processus exécutent la même requête SQL pour remplir à nouveau ce cache. Chaque fois que le cache expire, il en résulte une rafale soudaine de trafic SQL.

Pire encore, vous avez plusieurs processus mettant à jour les mêmes données, et le mauvais finit par dater le cache. Ensuite, vous avez des données obsolètes et obsolètes qui flottent.

N'oubliez pas les éventuels problèmes de remplissage ou de repeuplement de notre cache. N'oubliez pas que le processus de vérification de memcached, d'extraction de code SQL et de stockage dans memcached n'est pas du tout atomique!

Autres conseils

Je pense qu’une solution pourrait consister à stocker viewcount séparément de l’objet Post, puis à faire un INCR dessus. Bien sûr, cela nécessiterait la lecture de 2 valeurs distinctes de memcached lors de l’affichage des informations.

Nous avons rencontré cela dans notre système. Nous avons modifié get ainsi

  • Si la valeur est non définie, elle est définie avec un indicateur ('g') et [8] seconde de durée de vie, et renvoie false pour que la fonction appelante le génère.
  • Si la valeur n'est pas marquée (! == 'g'), désérialisez-la et renvoyez-la.
  • Si la valeur est marquée (=== 'g'), attendez 1 seconde et réessayez jusqu'à ce qu'elle ne soit pas marquée. Il sera éventuellement défini par l'autre processus ou expiré par la durée de vie.

La charge de notre base de données a été réduite d'un facteur 100 lorsque nous l'avons implémentée.

function get($key) {
  $value=$m->get($key);
  if ($value===false) $m->set($key, 'g', $ttl=8);
  else while ($value==='g') {
    sleep(1);
    $value=$m->get($key);
  }
  return $value;
}
Les opérations

memcached sont atomiques. le processus du serveur mettra les demandes en file d'attente et traitera chacune d'elles complètement avant de passer à la suivante. Il n'est donc pas nécessaire de verrouiller.

modifier: memcached dispose d'une commande d'incrémentation, laquelle est atomique. Il vous suffit de stocker le compteur en tant que valeur distincte dans le cache.

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