2012-04-04 10 views
17

Tengo una pequeña pregunta.Redis 10x más uso de memoria que los datos

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'.

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

Gracias de antemano

Respuesta

75

Bueno, esto se espera de cualquier almacenamiento de datos eficiente: las palabras tienen que ser 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 matriz de punteros creciente geométricamente (potencias de dos)
  • puede ser necesaria una segunda matriz cuando rehashing incremental es activo
  • celdas de la lista simplemente enlazada que representan las entradas en la tabla hash (3 punteros, 24 bytes por entrada)
  • envolturas objeto Redis (uno por valor) (16 bytes por entrada)
  • propios datos reales (cada uno de ellos precedido de 8 bytes para el tamaño y capacidad)

Todos los tamaños anteriores se proporcionan para la implementación de 64 bits. Contabilizando 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 usando el asignador jemalloc (> = 2.4)

Redis proporciona memory optimizations 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, 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.Asumiendo que requiere para almacenar artículos 1 M, añadiendo una palabra podría ser implementado de la siguiente manera:

  • hash 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, ... } 

que puede 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, el ahorro de memoria significativa se puede hacer siempre que el módulo de la hash es elige en función de la configuración 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 han cambiado con Redis> = 2.6.

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

+0

respuesta fascinante y detallada; No lo sabía. Gracias @Didier! –

+0

Bien, muchas gracias, estoy bastante seguro de que esto resolverá mis problemas. Y sí para 160mb está bien, pero espero trabajar con hasta 1GB de datos de palabra simple y no quiero que aumente a 10gb. Muchas gracias de nuevo, agradezco la respuesta detallada. – cwoebker

+2

@Didier - ¡Excelente respuesta! Sin embargo, un par de correcciones a) Las entradas Hashtable son una sola lista vinculada, no doble, la sobrecarga de 24 bytes es correcta, aunque b) El contenedor de objetos Redis no se aplica a cada entrada de conjunto/hash. Solo se aplica al par clave/valor de nivel superior, por lo que la sobrecarga es constante c) Es posible que desee indicar que el zipmap está en desuso en 2.6/inestable, y que la lista zip hace lo equivalente. –

2

¿Usted intentó persistir la base de datos (BGSAVE por ejemplo), cerrando la caída de servidor y obtener 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.

También: ¿Con qué versión de Redis trabajas? Eche un vistazo a this blog post - dice que la fragmentación se ha resuelto parcialmente a partir de la versión 2.4.

4

En cuanto a mis experimentos, es mejor almacenar sus datos dentro de una tabla de hash/diccionario. 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.

Probé 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.

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

tener una oportunidad en el caso si alguien necesita el código de la evaluación comparativa, mándenme un correo

Cuestiones relacionadas