diff --git a/reference/yac/book.xml b/reference/yac/book.xml index ff75283d17..6fb5aff320 100644 --- a/reference/yac/book.xml +++ b/reference/yac/book.xml @@ -1,5 +1,5 @@ - + @@ -27,7 +27,8 @@ une lecture est essentiellement une recherche dans une table de hachage en mémoire partagée. Yac est ainsi extrêmement rapide, avec une latence de lecture à la microseconde, et son débit peut s'adapter au nombre de - workers tant que les écritures sont réparties sur plusieurs clés. + workers tant que les écritures sont réparties sur plusieurs clés ; voir les + tests de performance. Parce que Yac échange des garanties de cohérence contre vitesse et @@ -38,7 +39,8 @@ Depuis yac 2.4.0, les petites valeurs scalaires — - NULL, booléens, entiers, courtes chaînes jusqu'à + NULL, booléens, la plupart des entiers (ceux qui + tiennent sur 60 bits signés dans une compilation 64 bits), chaînes jusqu'à 7 octets et tableaux vides — sont stockées directement dans le slot de la table de hachage plutôt que dans un bloc de valeur séparé (« valeurs intégrées »), ce qui supprime l'allocation et la copie de @@ -57,6 +59,7 @@ &reference.yac.setup; + &reference.yac.memory; &reference.yac.constants; &reference.yac.yac; diff --git a/reference/yac/configure.xml b/reference/yac/configure.xml deleted file mode 100644 index fa6f78f1ce..0000000000 --- a/reference/yac/configure.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - -
- &reftitle.install; - - - &pecl.info; - &url.pecl.package;yac - - - -
- - - diff --git a/reference/yac/constants.xml b/reference/yac/constants.xml index 9d1a9d1bd4..be89ed13c6 100644 --- a/reference/yac/constants.xml +++ b/reference/yac/constants.xml @@ -1,5 +1,5 @@ - + &reftitle.constants; @@ -13,6 +13,7 @@ + La version de l'extension, sous forme de chaîne. @@ -23,7 +24,8 @@ - Longueur maximale que la clé peut faire ; 48 octets. + Longueur maximale d'une clé, en octets : 48. Un préfixe d'instance est + décompté de cette limite. @@ -34,6 +36,9 @@ + Longueur maximale d'une valeur avant sérialisation, en octets : + 67 108 863 ((1 << 26) - 1). Les valeurs plus + grandes sont rejetées. @@ -44,6 +49,8 @@ + Taille maximale d'une entrée stockée, en octets : 1 048 576 (1M). Les + valeurs qui n'y tiennent pas, même compressées, sont rejetées. @@ -54,7 +61,7 @@ - Utilise le sérialisateur de PHP comme sérialisateur + Utilise le format serialize de PHP comme sérialisateur (par défaut). @@ -65,7 +72,8 @@ - Utilise Json comme sérialisateur (nécessite l'option --enable-json) + Utilise JSON comme sérialisateur. Nécessite que l'extension soit + compilée avec . @@ -76,7 +84,8 @@ - Utilise igbinary comme sérialisateur (nécessite l'option --enable-igbinary) + Utilise igbinary comme sérialisateur. Nécessite que l'extension soit + compilée avec . @@ -87,7 +96,8 @@ - Utilise msgpack comme sérialisateur (nécessite l'option --enable-msgpack) + Utilise msgpack comme sérialisateur. Nécessite que l'extension soit + compilée avec . @@ -98,7 +108,7 @@ - Le sérialisateur que YAC doit utiliser + Le sérialisateur actuellement utilisé par l'extension. diff --git a/reference/yac/ini.xml b/reference/yac/ini.xml index 88fe1265bb..0623bc1f7b 100644 --- a/reference/yac/ini.xml +++ b/reference/yac/ini.xml @@ -1,5 +1,5 @@ - +
&reftitle.runtime; @@ -19,7 +19,7 @@ yac.compress_threshold - -1 + 4K INI_SYSTEM @@ -43,7 +43,7 @@ yac.keys_memory_size - 4M + 8M INI_SYSTEM @@ -76,11 +76,17 @@ Les valeurs sérialisées plus grandes que ce nombre d'octets sont - compressées avant d'être stockées (actuellement avec LZ4). Mettre à - -1 (la valeur par défaut) pour désactiver - entièrement la compression. Compresser les grandes valeurs économise - de la mémoire partagée au coût de quelques cycles CPU lors du - stockage et de la récupération. + compressées (avec LZ4, depuis Yac 2.4.0) avant d'être stockées. + Les valeurs au delà de la limite de 1M par entrée + stockée (YAC_MAX_RAW_COMPRESSED_LEN) sont + toujours compressées, quel que soit ce réglage, car elles ne peuvent + pas être stockées non compressées. La valeur par défaut de + 4K active la compression ; -1 + la désactive pour les valeurs en deçà de la limite par entrée + stockée, et les autres valeurs positives sont ramenées dans + l'intervalle 1024..1M. + Compresser les grandes valeurs économise de la mémoire partagée au + coût de quelques cycles CPU lors du stockage et de la récupération. @@ -129,13 +135,34 @@ - Quantité de mémoire partagée utilisée pour la table de hachage qui - contient les clés et les informations de gestion. Chaque slot est une - structure de taille fixe, donc cette valeur détermine combien - d'éléments peuvent être suivis simultanément. La valeur par défaut - est 4M. Yac divise cette zone en segments ; la - taille de segment est de 4M, donc cette valeur doit être un multiple - de 4M. + Quantité de mémoire partagée pour la table des clés. Elle plafonne le + nombre d'entrées pouvant exister simultanément : la valeur par défaut + de 8M autorise environ 65 536 entrées. L'augmenter + lorsque le taux de succès baisse et que kicks + grimpe ; voir pour le + comportement d'une table pleine et les cas où cela importe vraiment. + + + + + + yac.values_memory_size + string + + + + Quantité de mémoire partagée pour le stockage des valeurs. Les valeurs + sont allouées par un allocateur linéaire sur des segments de 4M + chacun ; lorsqu'aucun segment n'a de place pour une nouvelle valeur, + le curseur d'allocation repart au début et les valeurs les plus + anciennes sont silencieusement écrasées (« recyclées »). Leurs + lectures se dégradent alors en défauts de cache, détectés par un + contrôle d'intégrité. Augmenter cette valeur si + recycles grimpe alors que les slots ont encore de + la place, ou activer + yac.compress_threshold + pour réduire les grandes charges utiles. Voir + . @@ -158,22 +185,6 @@ - - - yac.values_memory_size - string - - - - Quantité de mémoire partagée utilisée pour stocker les valeurs - réelles. La valeur par défaut est 64M. Yac alloue - cette zone en segments de 4M chacun, donc cette valeur doit être un - multiple de 4M. Lorsque la zone est pleine, les - entrées les moins récemment utilisées sont expulsées pour faire de la - place aux nouvelles. - - - diff --git a/reference/yac/memory.xml b/reference/yac/memory.xml new file mode 100644 index 0000000000..96e4d4a516 --- /dev/null +++ b/reference/yac/memory.xml @@ -0,0 +1,258 @@ + + + + + Gestion de la mémoire + + + Yac conserve ses données dans deux réservoirs de mémoire partagée + indépendants, configurés par + yac.keys_memory_size + et + yac.values_memory_size. + Ils se remplissent et se libèrent différemment, il est donc utile de savoir + à quel réservoir se rattache un symptôme avant de toucher à l'un ou l'autre + réglage. + + +
+ Ce que contient chaque réservoir + + Les deux réservoirs stockent chacun une moitié d'une entrée. Le réservoir + de clés contient les clés : un slot par clé mise en cache, portant la clé + elle-même (jusqu'à 48 octets) accompagnée de son hachage, de son TTL, de + son nombre de succès et de sa date de dernier accès ; la valeur n'est + référencée que par un pointeur vers le réservoir de valeurs. Le réservoir + de valeurs contient les valeurs : chaque valeur stockée occupe un bloc + d'octets sérialisés — sa forme compressée lorsque l'entrée est passée par + yac.compress_threshold. + + + La seule exception est celle des valeurs intégrées : les petits scalaires + — &null;, &true;, &false;, la plupart des entiers (ceux qui tiennent sur + 60 bits signés dans une compilation 64 bits), les chaînes jusqu'à 7 octets + et les tableaux vides — sont stockés directement dans le slot, dans le + pointeur qui référencerait autrement un bloc. De telles valeurs + n'occupent aucune place dans le réservoir de valeurs ; seule leur clé en + occupe. + + + Pour un dimensionnement approximatif : + + + + le réservoir de clés contient environ 8 000 clés par Mo ; dimensionner + yac.keys_memory_size + comme le nombre de clés distinctes divisé par 8 000 par Mo, arrondi au + supérieur — la valeur par défaut de 8M contient + environ 64 000 clés ; + + + + + le réservoir de valeurs doit contenir toute valeur susceptible d'être + encore lue ; dimensionner + yac.values_memory_size + comme le nombre de valeurs vivantes multiplié par leur taille + sérialisée moyenne (après compression), et prévoir environ le double : + le réservoir est un anneau, et une valeur ne meurt qu'une fois que le + curseur d'allocation revient l'écraser. + + + + Les valeurs intégrées occupent un slot comme toute autre entrée, mais ne + consomment aucune place dans le réservoir de valeurs : les exclure du + second calcul. + +
+ +
+ Le réservoir de clés (slots) + + yac.keys_memory_size + contient une table de slots de taille fixe — la valeur par défaut de + 8M donne environ 65 536 slots. Chaque clé stockée + occupe exactement un slot ; ce réservoir plafonne donc le nombre + d'entrées pouvant exister simultanément. Contrairement au réservoir de + valeurs, les slots ne sont jamais libérés individuellement. Un slot + expiré — au delà de son TTL, ou la trace laissée par + Yac::delete — est recyclé gratuitement lorsqu'une + nouvelle clé en a besoin. Ce n'est que lorsque les quatre slots candidats + d'un chemin de sondage contiennent des entrées vivantes que l'un d'eux est + expulsé pour faire de la place : un kick (le compteur + kicks de Yac::info). + + + L'expulsion choisit uniquement parmi les quatre candidats vivants du + chemin de sondage en collision : + + + + le moins récemment utilisé (l'atime le plus ancien) + est expulsé ; + + + + + en cas d'égalité, l'entrée la moins souvent lue, puis la première + position de sondage. + + + + + + Une confusion fréquente : slots_used atteignant + slots_size n'est pas une + condition d'erreur. Un cache dont l'ensemble de clés actif est plus grand + que la table de slots tourne simplement à 100 % d'occupation à + partir de là, expulsant et réinsérant selon les besoins. La seule chose + qui dise si le réservoir de clés est correctement dimensionné est le taux + de succès (hits / (hits + miss), calculé sur les + écarts entre deux instantanés de Yac::info + plutôt que sur la moyenne depuis le démarrage). Un compteur + kicks élevé ne signifie en lui-même pas qu'il y a un + problème : la distribution des clés n'est simplement pas uniforme et + certains chemins de sondage entrent plus souvent en collision. Ce n'est + que lorsque le taux de succès et + kicks sont tous deux mauvais que la table est trop + petite pour l'ensemble de clés, et le remède est alors un + yac.keys_memory_size + plus grand. + + + Seconde conséquence du fait que les slots ne sont jamais libérés : les + entrées sans TTL (ttl = 0) qui ne sont plus jamais + lues continuent d'occuper un slot jusqu'à ce qu'une expulsion les + désigne. Si une application stocke de grandes quantités de telles données + à usage unique, leur donner un TTL pour qu'elles expirent et puissent + être recyclées sans déloger des entrées vivantes, ou dimensionner le + réservoir de clés pour l'ensemble de clés complet. + +
+ +
+ Le réservoir de valeurs (segments) + + yac.values_memory_size + est découpé en segments de 4M chacun, gérés comme des anneaux : les + écritures avancent un curseur par segment et la place n'est jamais + libérée entrée par entrée. Lorsqu'une allocation ne tient plus, le + curseur repart au début d'un segment : un recycle (le + compteur recycles de + Yac::info). Un recyclage n'invalide pas le + segment d'un coup : les valeurs écrasées restent lisibles jusqu'à ce que + le curseur revenu au début les écrase effectivement, moment à partir + duquel leurs lectures échouent au contrôle d'intégrité et se + transforment en défauts de cache. + + + Deux tailles comptent pour ce réservoir : le total de + yac.values_memory_size + doit contenir l'ensemble actif des valeurs vivantes, et une entrée seule + ne peut pas dépasser 1 Mo une fois stockée + (YAC_MAX_RAW_COMPRESSED_LEN). Les valeurs plus + grandes sont donc toujours compressées avant d'être stockées ; une valeur + qui ne peut pas descendre en dessous de 1 Mo — le plus souvent parce + qu'il s'agit de données aléatoires — est rejetée et incrémente le + compteur fails. La limite absolue sur la valeur + elle-même est bien plus haute : les valeurs sérialisées au delà de 64 Mo + (YAC_MAX_VALUE_RAW_LEN, soit + (1 << 26) - 1 octets) sont rejetées d'emblée. + +
+ +
+ Dimensionner et surveiller + + Commencer avec les valeurs par défaut et surveiller les compteurs de + Yac::info : ils s'accumulent depuis + start_time, il faut donc comparer deux instantanés + pris à quelque temps d'intervalle : + + + + + taux de succès sain (disons >= 90 %) : le cache va bien, + il n'y a rien à faire, quels que soient les autres compteurs ; + + + + + taux de succès faible et kicks en hausse : le + réservoir de clés est trop petit pour l'ensemble de clés — des entrées + vivantes sont expulsées avant d'avoir été relues. Augmenter + yac.keys_memory_size ; + + + + + recycles fréquents : c'est un vrai problème, pas un + compteur anodin. Un recyclage signifie que l'allocateur de valeurs est + revenu au début et s'apprête à écraser des entrées — tout ce qui est + écrasé meurt avant d'avoir pu être relu, les octets dépensés à le + stocker sont donc perdus et le taux de succès en souffre. Le réservoir + de valeurs est trop petit pour le volume de données vivantes. Par ordre + d'impact : + + + + donner un TTL aux entrées. Les valeurs écrites avec + ttl = 0 restent vivantes indéfiniment, elles + continuent donc d'occuper le réservoir et forcent le curseur à + reboucler plus tôt. Un TTL borne la durée de vie de chaque entrée et + réduit l'ensemble actif que le réservoir doit contenir ; + + + + + augmenter + yac.values_memory_size + pour que le réservoir contienne tout l'ensemble des valeurs vivantes + (penser à prévoir environ le double de l'empreinte vivante : une + valeur ne meurt qu'une fois que le curseur revient l'écraser) ; + + + + + stocker moins par entrée : baisser + yac.compress_threshold + s'il est réglé au dessus du minimum de 1024, afin + que les grandes charges utiles soient compressées, et élaguer les + valeurs qui n'ont pas besoin d'être mises en cache en entier ; + + + + + + + + fails en hausse : des valeurs qui n'ont pas pu être + stockées, le plus souvent une valeur seule dépassant la limite de 1 Mo + par entrée stockée même après compression — découper la valeur. + + + +
+ +
+ + diff --git a/reference/yac/yac/add.xml b/reference/yac/yac/add.xml index 2611f8b443..dc31769b41 100644 --- a/reference/yac/yac/add.xml +++ b/reference/yac/yac/add.xml @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ &reftitle.description; public boolYac::add - stringarraykeys + stringarraykey mixedvalue intttl0 @@ -31,7 +31,7 @@ &reftitle.parameters; - keys + key Une clé string, ou un array de paires @@ -45,7 +45,7 @@ La valeur à stocker. Tout type PHP sauf resource peut être stocké. Utilisé uniquement dans la forme à clé unique ; quand - keys est un tableau, cet argument est le + key est un tableau, cet argument est le ttl optionnel. @@ -69,22 +69,6 @@ est également rejeté (retournant &false;) quand la clé existe déjà et n'a pas expiré. - - - Yac stocke les entrées sans verrou. Sous forte contention, un stockage - peut échouer de façon transitoire ; si la valeur doit finalement être - stockée, réessayer : - -add("key", "value")) { - // réessayer en cas d'échec transitoire -} -?> -]]> - - - @@ -98,11 +82,31 @@ $yac = new Yac(); var_dump($yac->add("foo", "bar")); // bool(true) var_dump($yac->add("foo", "baz")); // bool(false): "foo" existe déjà +?> +]]> + + + + Ajout d'une entrée avec un TTL + +add("short-lived", "value", 5); sleep(6); var_dump($yac->get("short-lived")); // bool(false): expiré +?> +]]> + + + + Ajout de plusieurs entrées en un appel + + valeur en un appel, avec un ttl $yac->add(array("a" => 1, "b" => 2), 60); diff --git a/reference/yac/yac/delete.xml b/reference/yac/yac/delete.xml index 90e39b6b07..1e152e323f 100644 --- a/reference/yac/yac/delete.xml +++ b/reference/yac/yac/delete.xml @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ &reftitle.description; public boolYac::delete - stringarraykeys + stringarraykey intdelay0 @@ -38,7 +38,7 @@ &reftitle.parameters; - keys + key Une clé string, ou un array de clés à @@ -97,13 +97,33 @@ var_dump($yac->delete("jamais")); // bool(false) : jamais stockée var_dump($yac->info()["slots_used"]); // int(1) print_r($yac->dump()); // "foo" est toujours listée ; son ttl // est dans le passé +?> +]]> + + + + Suppression différée + +set("tmp", "value"); var_dump($yac->delete("tmp", 60)); // bool(true) +?> +]]> + + + + Suppression de plusieurs clés en un appel + +set("tmp", "value"); -// supprimer plusieurs clés à la fois retourne true uniquement si toutes -// les clés étaient présentes +// retourne true uniquement si toutes les clés étaient présentes var_dump($yac->delete(array("tmp", "non"))); // bool(false) : "non" absente ?> ]]> diff --git a/reference/yac/yac/dump.xml b/reference/yac/yac/dump.xml index 328018c8f5..3d5abf67f1 100644 --- a/reference/yac/yac/dump.xml +++ b/reference/yac/yac/dump.xml @@ -1,5 +1,5 @@ - + @@ -137,8 +137,9 @@ hits Un compteur de succès par entrée, incrémenté à chaque - Yac::get réussi, remis à zéro quand - l'entrée est écrasée, supprimée ou expire (à partir de yac 2.4.0). + Yac::get réussi et remis à zéro quand + l'entrée est écrasée par un nouveau Yac::set + ou Yac::add (à partir de yac 2.4.0). @@ -236,6 +237,11 @@ Array set("key$i", $i); +} + $page_size = 100; $page_num = 2; diff --git a/reference/yac/yac/get.xml b/reference/yac/yac/get.xml index cf07b6d22c..18ce2a2a45 100644 --- a/reference/yac/yac/get.xml +++ b/reference/yac/yac/get.xml @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ &reftitle.description; public mixedYac::get - stringarraykeys + stringarraykey mixeddefault&null; @@ -23,7 +23,7 @@ &reftitle.parameters; - keys + key Une clé string, ou un array de clés. @@ -79,6 +79,16 @@ $yac = new Yac(); $yac->set("foo", "bar"); var_dump($yac->get("foo")); // string(3) "bar" var_dump($yac->get("missing")); // bool(false): défaut de cache +?> +]]> + + + + Distinguer un false stocké d'un défaut de cache + +get("flag")); // bool(false): la valeur stockée var_dump($yac->get("missing", false)); // bool(false): un défaut de cache, même forme var_dump($yac->get("flag", "__NONE__")); // bool(false): la valeur stockée var_dump($yac->get("missing", "__NONE__")); // string(8) "__NONE__": un défaut de cache +?> +]]> + + + + Récupération de plusieurs clés en un appel + +set("foo", "bar"); $yac->set("foo2", "bar2"); + +// avec un tableau de clés, seules les clés trouvées sont présentes dans le résultat var_dump($yac->get(array("foo", "foo2", "missing"))); // array(2) { ["foo"]=> string(3) "bar" ["foo2"]=> string(4) "bar2" } ?> diff --git a/reference/yac/yac/info.xml b/reference/yac/yac/info.xml index 8e41ca0498..f4da9925d8 100644 --- a/reference/yac/yac/info.xml +++ b/reference/yac/yac/info.xml @@ -1,5 +1,5 @@ - + @@ -135,18 +135,18 @@ print_r($yac->info()); 46137344 - [slots_memory_size] => 4194304 - [values_memory_size] => 41943040 + [memory_size] => 75497472 + [slots_memory_size] => 8388608 + [values_memory_size] => 67108864 [segment_size] => 4194304 - [segment_num] => 10 + [segment_num] => 16 [miss] => 0 [hits] => 0 [fails] => 0 [kicks] => 0 [recycles] => 0 [start_time] => 1725955200 - [slots_size] => 32768 + [slots_size] => 65536 [slots_used] => 1 ) ]]> diff --git a/reference/yac/yac/set.xml b/reference/yac/yac/set.xml index 73abf145a2..0982de6bac 100644 --- a/reference/yac/yac/set.xml +++ b/reference/yac/yac/set.xml @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ &reftitle.description; public boolYac::set - stringarraykeys + stringarraykey mixedvalue intttl0 @@ -30,7 +30,7 @@ &reftitle.parameters; - keys + key Une clé string, ou un array de paires @@ -44,7 +44,7 @@ La valeur à stocker. Tout type PHP sauf resource peut être stocké. Utilisé uniquement dans la forme à clé unique ; quand - keys est un tableau, cet argument est le + key est un tableau, cet argument est le ttl optionnel. @@ -79,11 +79,31 @@ $yac = new Yac(); $yac->set("foo", "bar"); // stocker une valeur unique $yac->set("foo", "baz"); // écraser l'entrée existante +?> +]]> + + + + Stockage d'une entrée avec un TTL + +set("short-lived", "value", 5); sleep(6); var_dump($yac->get("short-lived")); // bool(false): expiré +?> +]]> + + + + Stockage de plusieurs entrées en un appel + + valeur en un appel $yac->set(array("a" => 1, "b" => 2));