sql >> Base de Datos >  >> NoSQL >> Redis

Introducción a las estructuras de datos de Redis:hashes

Los hashes de Redis son (¡intuitivamente!) hashes que asignan nombres de cadenas a valores de cadenas. Básicamente, son contenedores con nombre de campos únicos y sus valores. Son la manera perfecta de representar un objeto como una estructura de datos de Redis. Como era de esperar, proporcionan operaciones básicas de tiempo constante como obtener, establecer, existe, etc. También se proporcionan un montón de operaciones avanzadas. La lista completa de comandos hash está aquí.

Vamos a dar una vuelta desde el redis-cli .

# hmset key field value [field value ...] :  Insert elements in a hash. O(N), N is # of field being set
127.0.0.1:6379> hmset std:101 name "John Smith" dob "01-01-2000" gender M active 0 cgpa 2.9
OK

# hgetall key : key all keys and values in the hash. O(N), N is size of hash
127.0.0.1:6379> hgetall std:101
 1) "name"
 2) "John Smith"
 3) "dob"
 4) "01-01-2000"
 5) "gender"
 6) "M"
 7) "active"
 8) "0"
 9) "cgpa"
10) "2.9"
127.0.0.1:6379> hmset std:102 name "Jane" name "Ann"
OK
# If duplicates are found, only the last set is valid
127.0.0.1:6379> hgetall std:102
1) "name"
2) "Ann"

# hexists key field: does field exist in the hash with key. O(1)
127.0.0.1:6379> hexists std:102 cgpa
(integer) 0

# hincrby key field increment: Increment the integer field by increment. O(1)
127.0.0.1:6379> hincrby std:101 active 1
(integer) 1

# hget key field : the value for field in the hash stored at key. O(1)
127.0.0.1:6379> hget std:101 active
1) "1"
# If field doesn't exist, hincrby sets it to 0 and then applies increment
127.0.0.1:6379> hincrby std:102 active 2
(integer) 2

# hmget key field [field ...]: the values of the fields requested for the hash with key. O(N), N is # of keys requested
127.0.0.1:6379> hmget std:102 active
1) "2"

# hincrbyfloat key field increment: Increment the float value in field by increment. O(1) 
127.0.0.1:6379> HINCRBYFLOAT std:101 cgpa 1.0
"3.9"

# HSETNX key field value: set field to value if not alread set. O(1)
127.0.0.1:6379> hsetnx std:102 cgpa 4.0
(integer) 1
127.0.0.1:6379> hget std:102 cgpa
"4.0"

# hlen key: number of fields in the hash. O(1)
127.0.0.1:6379> hlen std:101
(integer) 5

# hkeys key : all fields in the hash. O(N), N is size of hash
127.0.0.1:6379> hkeys std:101
1) "name"
2) "dob"
3) "gender"
4) "active"
5) "cgpa"

Como es de esperar de nuestro alojamiento para Redis™* como servidor de estructura de datos, vemos que Redis proporciona operaciones bastante útiles y avanzadas en hashes.

Internos

Al igual que los conjuntos de Redis, los hashes de Redis también se implementan como diccionarios. Los diccionarios en Redis se implementan como tablas hash que usan la función hash MurmurHash2 y crecen a través del cambio de tamaño incremental. Las colisiones hash se manejan mediante el encadenamiento. Se pueden encontrar más detalles en la implementación de Redis del diccionario en dict.c.
Al igual que con los conjuntos, existe una optimización de almacenamiento para hashes más pequeños. Esta estructura de datos se llama ziplist (los hashes se optimizaron usando una estructura de datos diferente llamada zipmap antes de Redis 2.6) en la implementación de Redis. Es esencialmente una lista doblemente enlazada especialmente codificada que está optimizada para ahorrar memoria. Los datos, así como los punteros, se almacenan en línea. Ziplist también se utiliza para optimizar el almacenamiento de listas y conjuntos ordenados más pequeños. Un hash cuando se aplana en una lista de este tipo se parece a [clave1, valor1, clave2, valor2, ...]. ¿Cómo es esto más eficiente que las teclas simples? Los hashes con pocas claves se pueden empaquetar inteligentemente en esta estructura similar a una matriz lineal (es decir, la lista zip) mientras se garantiza el rendimiento amortizado de O(1) para obtener y establecer. Obviamente, esto no puede seguir el ritmo a medida que aumentan los campos hash. A medida que crece el hash, se convierte en la estructura de diccionario estándar para mantener el rendimiento O(1) y se pierde el ahorro de espacio. Los parámetros de configuración de Redis que controlan esta transformación son:

  • listar-max-ziplist-entradas predeterminado (512):cambie a la representación estándar si el hash supera este límite.
  • list-max-ziplist-value predeterminado (64):cambie a la representación estándar si el elemento más grande en el hash supera este límite.

Se pueden entender más detalles a partir del código y los comentarios de la implementación que se encuentran aquí. Los ahorros de memoria derivados del uso de esta optimización especial son significativos. Hablaremos de ello con más detalle en la siguiente sección.

Optimización de memoria

Una de las recomendaciones más conocidas para ahorrar memoria al usar Redis es usar hashes en lugar de cadenas simples. Este es un caso de uso importante para utilizar el poder de los hashes de Redis en aplicaciones del mundo real. De la documentación oficial de Redis sobre optimización de memoria:

Use hashes cuando sea posible

Los valores hash pequeños están codificados en un espacio muy pequeño, por lo que debe intentar representar sus datos usando valores hash cada vez que sea posible. Por ejemplo, si tiene objetos que representan a los usuarios en una aplicación web, en lugar de usar diferentes claves para nombre, apellido, correo electrónico, contraseña, use un solo hash con todos los campos obligatorios.

Esa publicación continúa proponiendo una forma de mapear un rango de objetos en un conjunto de hashes para aprovechar los ahorros de memoria. Instagram, en una publicación de blog muy popular, describe el uso de una técnica similar que les ayudó a lograr órdenes de magnitud de ahorros potenciales. Otro blog que intenta medir los beneficios de la optimización es este.

Aplicaciones

  • Los hashes de Redis se adaptan naturalmente a almacenar objetos:sesiones, usuarios, visitantes, etc. Esto lo convierte en una de las estructuras de datos clave proporcionadas por Redis.
  • En su forma optimizada para memoria, es una excelente opción para almacenar en caché grandes cantidades de datos.

Un almacén de direcciones de objetos

Dado que la optimización de la memoria es un caso de uso importante para los hashes, analicemos un ejemplo similar a la implementación de Instagram para mostrar cómo utilizar las funciones de ahorro de memoria de los hashes de Redis. Digamos que tenemos una gran implementación de almacenamiento direccionable por contenido (CAS) con cientos de millones de objetos almacenados. La ubicación de cada objeto es una cadena hash. Tenemos la intención de desarrollar un sistema de búsqueda para averiguar la ubicación del objeto dada su identificación. La forma típica de hacer esto en Redis será usar una cadena.

set object:14590860 "007f80f0a62408..."
set object:11678 "009f80abcd0a60..."
...

Este enfoque funciona bien. Sin embargo, dado que la cantidad de objetos que tenemos es enorme, terminaremos necesitando mucha memoria para Redis. Queremos hacerlo mejor. Tomemos el enfoque hash optimizado para memoria para este problema. Tendremos que elegir los valores correctos para list-max-ziplist-entries y list-max-ziplist-value . El valor correcto para list-max-ziplist-value es cualquiera que sea la longitud máxima de la cadena hash de la dirección de almacenamiento. El valor de list-max-ziplist-entries debe mantenerse lo suficientemente bajo y dependerá de la cantidad total de cubos de hash que deseemos crear. Será mejor averiguarlo empíricamente. Por ej. para 100 millones de objetos, podríamos elegir usar 100k hash. Las entradas máximas en ese caso serán 100m / 100k =1000. La lógica de la aplicación para decidir en qué hash entra la dirección de almacenamiento de un objeto puede ser:dividir la ID del objeto por 100k y descartar el resto. Por lo tanto, el ID de objeto 14590860 entrará en hash (14590860/100k) =145, es decir,


hset object:145 14590860 "007f80f0a62408..."
hget object:145 14590860
> "007f80f0a62408..."

Esta implementación no solo será mucho más liviana en la memoria, sino que también debería proporcionar una buena localidad de caché.

Estas son nuestras otras publicaciones en la serie de estructuras de datos de Redis.

  • Conjuntos de Redis
  • Mapas de bits de Redis
  • Conjuntos ordenados de Redis