Redis 10 veces más uso de memoria que datos

Tengo una pequeña pregunta.

Estoy tratando de almacenar una lista de palabras en redis. El rendimiento es genial.

Mi enfoque es hacer un conjunto llamado “palabras” y agregar cada palabra nueva a través de “sadd”.

Aquí está el problema cuando se agrega un archivo que es 15.9mb y contiene aproximadamente un millón de palabras, el proceso redis-server consume 160 MB de memoria RAM. ¿Cómo puedo usar 10 veces la memoria? ¿Hay alguna forma mejor de abordar este problema?

Gracias por adelantado

Bueno, esto se espera de cualquier almacenamiento de datos eficiente: las palabras tienen que estar indexadas en la memoria en una estructura de datos dinámica de celdas vinculadas por punteros. El tamaño de los metadatos de la estructura, los punteros y la fragmentación interna del asignador de memoria es la razón por la cual los datos requieren mucha más memoria que un archivo plano correspondiente.

Un conjunto Redis se implementa como una tabla hash. Esto incluye:

  • una serie de indicadores que crecen geométricamente (poderes de dos)
  • una segunda matriz puede ser necesaria cuando el reajuste incremental está activo
  • celdas de lista de enlace único que representan las entradas en la tabla hash (3 punteros, 24 bytes por entrada)
  • Envoltorios de objetos Redis (uno por valor) (16 bytes por entrada)
  • datos reales ellos mismos (cada uno de ellos con un prefijo de 8 bytes para tamaño y capacidad)

Todos los tamaños anteriores se dan para la implementación de 64 bits. Teniendo en cuenta la sobrecarga del asignador de memoria, resulta que Redis toma al menos 64 bytes por elemento del conjunto (en la parte superior de los datos) para una versión reciente de Redis utilizando el asignador jemalloc (> = 2.4)

Redis proporciona optimizaciones de memoria para algunos tipos de datos, pero no cubren conjuntos de cadenas. Si realmente necesita optimizar el consumo de memoria de los conjuntos, existen trucos que puede usar. No haría esto por solo 160 MB de RAM, pero si tuviera datos más grandes, esto es lo que puede hacer.

Si no necesita las capacidades de unión, intersección y diferencia de conjuntos, entonces puede almacenar sus palabras en objetos hash. El beneficio es que los objetos hash pueden ser optimizados automáticamente por Redis usando zipmap si son lo suficientemente pequeños. El mecanismo de zipmap ha sido reemplazado por ziplist en Redis> = 2.6, pero la idea es la misma: usar una estructura de datos serializada que pueda caber en las memorias caché de la CPU para obtener tanto un rendimiento como una huella de memoria compacta.

Para garantizar que los objetos hash sean lo suficientemente pequeños, los datos podrían distribuirse de acuerdo con algún mecanismo hash. Suponiendo que necesita almacenar elementos de 1M, agregar una palabra podría implementarse de la siguiente manera:

  • hash it modulo 10000 (hecho en el lado del cliente)
  • Palabras HMSET: [hashnum] [palabra] 1

En lugar de almacenar:

words => set{ hi, hello, greetings, howdy, bonjour, salut, ... } 

puedes almacenar:

 words:H1 => map{ hi:1, greetings:1, bonjour:1, ... } words:H2 => map{ hello:1, howdy:1, salut:1, ... } ... 

Para recuperar o verificar la existencia de una palabra, es la misma (hash it y use HGET o HEXISTS).

Con esta estrategia, se puede realizar un ahorro significativo de memoria siempre que el módulo del hash se elija de acuerdo con la configuración de zipmap (o ziplist para 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 

Cuidado: el nombre de estos parámetros ha cambiado con Redis> = 2.6.

Aquí, el módulo 10000 para elementos de 1M significa 100 elementos por objeto hash, lo que garantizará que todos ellos estén almacenados como zipmaps / ziplists.

En cuanto a mis experimentos, es mejor almacenar sus datos dentro de una tabla / diccionario hash. El mejor caso al que llegué después de una gran cantidad de puntos de referencia es almacenar dentro de sus entradas de datos hashtable que no excedan de 500 claves.

Intenté el conjunto de cadenas estándar / get, para 1 millón de claves / valores, el tamaño era de 79 MB. Es muy grande en caso de que tenga números grandes como 100 millones que usarán alrededor de 8 GB.

Probé hashes para almacenar los mismos datos, para el mismo millón de claves / valores, el tamaño era cada vez más pequeño 16 MB.

Pruébalo en caso de que alguien necesite el código de evaluación comparativa, envíame un correo

¿ BGSAVE persistir en la base de datos ( BGSAVE por ejemplo), apagar el servidor y volver a hacer una copia de seguridad? Debido al comportamiento de fragmentación, cuando vuelve a aparecer y rellena sus datos del archivo RDB guardado, puede llevar menos memoria.

Además: ¿Con qué versión de Redis trabajas? Eche un vistazo a esta publicación de blog : dice que la fragmentación se ha resuelto parcialmente a partir de la versión 2.4.