Et bien c'est ce que l'on attend de tout stockage de données efficace :les mots doivent être indexés en mémoire dans une structure de données dynamique de cellules reliées par des pointeurs. La taille des métadonnées de structure, des pointeurs et de la fragmentation interne de l'allocateur de mémoire est la raison pour laquelle les données occupent beaucoup plus de mémoire qu'un fichier plat correspondant.
Un ensemble Redis est implémenté sous forme de table de hachage. Cela inclut :
- un tableau de pointeurs croissant géométriquement (puissances de deux)
- un deuxième tableau peut être nécessaire lorsque le rehachage incrémentiel est actif
- cellules de liste à liaison simple représentant les entrées de la table de hachage (3 pointeurs, 24 octets par entrée)
- Encapsuleurs d'objets Redis (un par valeur) (16 octets par entrée)
- les données réelles elles-mêmes (chacune d'elles étant précédée de 8 octets pour la taille et la capacité)
Toutes les tailles ci-dessus sont données pour l'implémentation 64 bits. En tenant compte de la surcharge de l'allocateur de mémoire, il en résulte que Redis prend au moins 64 octets par élément défini (en plus des données) pour une version récente de Redis utilisant l'allocateur jemalloc (>=2.4)
Redis fournit des optimisations de mémoire pour certains types de données, mais elles ne couvrent pas les ensembles de chaînes. Si vous avez vraiment besoin d'optimiser la consommation de mémoire des ensembles, il existe des astuces que vous pouvez utiliser. Je ne ferais pas cela pour seulement 160 Mo de RAM, mais si vous avez des données plus volumineuses, voici ce que vous pouvez faire.
Si vous n'avez pas besoin des capacités d'union, d'intersection et de différence des ensembles, vous pouvez stocker vos mots dans des objets de hachage. L'avantage est que les objets de hachage peuvent être optimisés automatiquement par Redis à l'aide de zipmap s'ils sont suffisamment petits. Le mécanisme zipmap a été remplacé par ziplist dans Redis>=2.6, mais l'idée est la même :utiliser une structure de données sérialisée pouvant tenir dans les caches du processeur pour obtenir à la fois des performances et une empreinte mémoire compacte.
Pour garantir que les objets de hachage sont suffisamment petits, les données peuvent être distribuées selon un mécanisme de hachage. En supposant que vous ayez besoin de stocker 1 million d'éléments, l'ajout d'un mot peut être implémenté de la manière suivante :
- hachage modulo 10000 (fait côté client)
- Mots HMSET :[hashnum] [mot] 1
Au lieu de stocker :
words => set{ hi, hello, greetings, howdy, bonjour, salut, ... }
vous pouvez stocker :
words:H1 => map{ hi:1, greetings:1, bonjour:1, ... }
words:H2 => map{ hello:1, howdy:1, salut:1, ... }
...
Pour récupérer ou vérifier l'existence d'un mot, c'est pareil (hachez-le et utilisez HGET ou HEXISTS).
Avec cette stratégie, un gain de mémoire important peut être réalisé à condition que le modulo du hash soit choisi en fonction de la configuration zipmap (ou ziplist pour Redis>=2.6) :
# Hashes are encoded in a special way (much more memory efficient) when they
# have at max a given number of elements, and the biggest element does not
# exceed a given threshold. You can configure this limits with the following
# configuration directives.
hash-max-zipmap-entries 512
hash-max-zipmap-value 64
Attention :le nom de ces paramètres a changé avec Redis>=2.6.
Ici, modulo 10000 pour 1M d'éléments signifie 100 éléments par objets de hachage, ce qui garantira qu'ils seront tous stockés sous forme de zipmaps/ziplists.