Dans les applications Web modernes, l'architecture distribuée est devenue le moyen grand public d'améliorer la disponibilité et l'évolutivité du système. Cependant, l'un des défis posés par les environnements distribués est. Si l'état de cache entre les nœuds distribués ne peut pas être correctement géré, les données incohérentes peuvent entraîner des erreurs, une expérience utilisateur dégradée et même des risques de sécurité.
Cet article présentera comment utiliser la fonction APCU_ENTRY de PHP pour implémenter un mécanisme de mise en cache efficace et cohérent dans un système distribué.
APCU_ENTRY est une fonction pratique fournie par l'extension PHP APCU pour obtenir des valeurs en cache atomiquement, ou générer et stocker le cache s'il n'existe pas. La signature de la fonction est la suivante:
mixed apcu_entry(string $key, callable $generator, int $ttl = 0)
$ key : Cache Key Name.
$ générateur : une fonction appelée lorsque l'élément de cache n'existe pas, utilisé pour générer des valeurs mises en cache.
$ ttl : temps de cache, en secondes. 0 signifie expiré.
Cette fonction a des caractéristiques sécurisées et évite l'effet "choquant" dans un environnement multithread (ou simultané), garantissant que la valeur du cache ne sera générée qu'une seule fois.
Par défaut, APCU est basé sur la mémoire locale et ne peut pas être partagé entre les serveurs. Par conséquent, parmi plusieurs nœuds PHP-FPM, l'APCU de chaque nœud est isolé.
Par exemple:
Serveur A demande la clé de cache User_123_Profile et exécutera la logique de génération.
Le serveur B demande à nouveau cette clé et APCU ne frappe pas le cache car il n'existe que dans la mémoire locale du serveur A.
Dans ce cas, si plusieurs nœuds essaient de retirer les données en même temps et d'écrire au cache APCU local, cela entraînera une consommation de ressources redondante et d'éventuelles incohérences de données.
Nous pouvons utiliser APCU et Redis pour utiliser APCU comme cache de premier niveau pour les nœuds (cache l1) et redis comme couche de cache partagée (cache L2) .
Chaque nœud hiérarchise la tentative de récupération des données de l'APCU local.
Si APCU ne frappe pas, utilisez APCU_ENTRY pour vous assurer que la logique de génération de données n'est appelée qu'une seule fois.
Dans la logique de génération d' APCU_ENTRY :
Vérifiez d'abord si Redis a déjà les données;
Si redis frappe, chargez-vous de redis et enregistrez sur APCU;
Si Redis manque également, obtenez-le de la base de données ou d'une autre source et enregistrez-le sur Redis et APCU.
function getUserProfile($userId) {
$cacheKey = "user_profile_$userId";
return apcu_entry($cacheKey, function() use ($cacheKey, $userId) {
// Redis Comme un cache distribué
$redis = new Redis();
$redis->connect('gitbox.net', 6379);
$redisKey = "global_cache:$cacheKey";
$cached = $redis->get($redisKey);
if ($cached !== false) {
return json_decode($cached, true);
}
// Simuler les données de chargement d'une base de données
$profileData = loadUserProfileFromDB($userId);
// Sauvegarder Redis(Définir le temps d'expiration,Par exemple10minute)
$redis->setex($redisKey, 600, json_encode($profileData));
return $profileData;
}, 60); // APCu cache60Deuxième
}
Grâce à la méthode ci-dessus, APCU_ENTRY garantit que chaque nœud entrera la logique de chargement des données qu'une seule fois lorsque la première demande sera effectuée et évite la requête répétée de la base de données. L'existence de Redis assure la cohérence du cache à travers les nœuds.
Pour améliorer la cohérence des données, les stratégies suivantes peuvent être adoptées:
Temps d'expiration unifié : assurez-vous que le TTL de Redis et APCU est relativement cohérent et évite la "lecture sale".
Utilisez le numéro de version pour contrôler les données : ajoutez un numéro de version ou un horodatage pour mettre en cache les données pour vérifier qu'elle est récente lors de la lecture.
Mécanisme de défaillance active : par exemple, une fois les données de l'utilisateur à jour, tous les nœuds sont informés pour effacer le cache APCU correspondant (peuvent être implémentés par redis publish / abonnement).
Bien que l'APCU ne puisse pas partager directement des données dans un environnement distribué, via le mécanisme de sécurité des threads d' APCU_ENTRY , nous pouvons créer un système de cache efficace qui fonctionne en collaboration avec le cache local + global. Combiné avec des outils de mise en cache partagés tels que Redis, il peut réduire efficacement la charge de la base de données, améliorer les performances du système et maintenir la cohérence des données autant que possible.
Ce modèle convient particulièrement aux scénarios d'entreprise tels que les informations utilisateur, les données de configuration, les détails du produit, etc., où la fréquence de lecture est élevée et la fréquence de mise à jour est faible. C'est l'un des moyens importants de construire un système distribué PHP haute performance.