¿Un artículo analiza cómo se diseñan la estructura de datos y el sistema de objetos de Redis?

Redis es un sistema de almacenamiento de valor clave de código abierto que utiliza seis estructuras de datos de bajo nivel para construir un sistema de objetos que contiene objetos de cadena, objetos de lista, objetos hash, objetos de colección y objetos de colección ordenados. Hoy veremos 12 imágenes para comprender completamente su estructura de datos y el principio de realización del sistema de objetos.

El contenido de este artículo es el siguiente :

  • Primero, introduzca seis estructuras de datos básicas: cadenas dinámicas, listas vinculadas, diccionarios, listas de omisión, conjuntos de enteros y listas comprimidas.

  • En segundo lugar, introduzca el objeto de cadena (String), el objeto de lista (List), el objeto hash (Hash), el objeto set (Set) y el objeto set ordenado (ZSet) en el sistema de objetos de Redis.

  • Finalmente, presentamos el espacio clave de Redis y la implementación de la clave de caducidad (caducar).

Estructura de datos

1. Cadenas dinámicas simples

Redis utiliza SDS de cadena dinámica para representar valores de cadena. La siguiente figura muestra una estructura SDS con un valor de Redis:

  • len: indica la longitud verdadera de la cadena (excluyendo el terminador NULL).

  • alloc: indica la capacidad máxima de la cadena (sin incluir el último byte extra).

  • banderas: siempre ocupan un byte. Los 3 bits más bajos se utilizan para indicar el tipo de encabezado.

  • buf: matriz de caracteres.

La estructura de SDS puede reducir el número de reasignaciones de memoria causadas por la modificación de la cadena, que depende de los dos mecanismos de preasignación de memoria y liberación de espacio diferido.

Cuando la SDS necesita ser modificada y la SDS necesita expandirse, Redis no solo asignará el espacio necesario para la modificación a la SDS, sino que también asignará espacio adicional no utilizado para la SDS .

  • Si la longitud de la SDS (es decir, el valor del atributo len) es inferior a 1 MB después de la modificación, Redis asigna previamente el espacio no utilizado del mismo tamaño que el atributo len.

  • Si la longitud de la SDS es superior a 1 MB después de la modificación, Redis asignará 1 MB de espacio no utilizado.

Por ejemplo, después de la modificación, la longitud de la len del SDS es de 20 bytes, menos de 1 MB, luego Redis asignará previamente 20 bytes de espacio, y la longitud real de la matriz de SDS (excluyendo el último byte) se convierte en 20 + 20 = 40 bytes. Cuando la longitud de la lente de SDS es mayor que 1 MB, solo se asignará 1 MB de espacio.

De manera similar, cuando SDS acorta la longitud de la cadena que guarda, no libera inmediatamente los bytes adicionales, sino que espera su uso posterior.

2. Lista vinculada

Las listas vinculadas se usan ampliamente en Redis. Por ejemplo, una de las implementaciones subyacentes de los objetos de lista son las listas vinculadas. Además de los objetos de lista vinculados, funciones como publicar y suscribirse, consultas lentas y monitores también usan listas vinculadas.

La lista vinculada de Redis es una lista doblemente vinculada, el diagrama esquemático se muestra arriba. La lista vinculada es la estructura de datos más común, por lo que no entraré en detalles aquí.

Los atributos de miembro dup, free y match de la estructura de listas vinculadas de Redis son las funciones específicas de tipo necesarias para implementar listas vinculadas polimórficas:

  • La función dup se usa para copiar el valor almacenado en el nodo de la lista vinculada para una copia profunda.

  • La función libre se utiliza para liberar el valor guardado por el nodo de la lista vinculada.

  • La función de coincidencia se utiliza para comparar si el valor almacenado en el nodo de la lista vinculada es igual a otro valor de entrada.

3. Diccionario

Los diccionarios se usan ampliamente para implementar diversas funciones de Redis, incluido el espacio clave y los objetos hash. El diagrama esquemático se muestra a continuación.

Redis utiliza el algoritmo MurmurHash2 para calcular el valor hash de la clave, y utiliza el método de dirección de cadena para resolver el conflicto clave. Múltiples pares clave-valor asignados al mismo índice se conectarán en una lista unidireccional vinculada.

4. mesa de salto

Redis utiliza tablas de salto como una de las implementaciones subyacentes de los objetos de colección ordenados. Guarda elementos en una lista jerárquica vinculada de manera ordenada. La eficiencia es comparable a la de las operaciones de árbol equilibrado, como buscar, eliminar y agregar, se puede completar en el tiempo logarítmico esperado y, en comparación con los árboles equilibrados, la tabla de salto La implementación es mucho más simple y más intuitiva.

El diagrama esquemático de la tabla de salto se muestra en la figura anterior. Aquí, solo habla brevemente sobre su idea central, y no lo explica en detalle.

Como se muestra en el diagrama esquemático, zskiplistNode es un nodo de la lista de salto, su ele es el valor del elemento retenido, el puntaje es el puntaje, los nodos están ordenados de acuerdo con su valor de puntaje, y la matriz de nivel es la encarnación de su llamada lista jerárquica vinculada.

El tamaño de la matriz de nivel de cada nodo es diferente. El valor en la matriz de nivel es el puntero al siguiente nodo y el valor de intervalo. El valor de intervalo es la diferencia entre las puntuaciones de los dos nodos. Cuanto mayor sea el valor de la matriz de nivel, mayor será el valor de rango y el valor de la matriz de nivel inferior, menor será el valor de rango.

La matriz de niveles es como una regla con diferentes escalas. Al medir la longitud, primero use la escala grande para estimar el rango, y luego continúe usando la escala reducida para aproximar con precisión.

Al consultar el valor de un elemento en la tabla de salto, comience con el nivel superior del primer nodo. Por ejemplo, al consultar el elemento o2 en la tabla de salto anterior, comience con el nodo o1 primero, porque el puntero del encabezado de zskiplist apunta a él.

Comience por consultar desde su nivel [3] y descubra que su extensión es 2, y la puntuación del nodo o1 es 1.0, por lo que la suma es 3.0, que es mayor que el valor de puntuación de o2 de 2.0. Por lo tanto, podemos saber que el nodo o2 está entre los nodos o1 y o3. En este momento, se usó la regla con una escala pequeña. Use el puntero del nivel [1] para encontrar el nodo o2 con éxito.

5. Conjunto de enteros

La colección de conjuntos es una de las implementaciones subyacentes de los objetos de colección. Cuando una colección contiene solo elementos con valores enteros y el número de elementos en esta colección no es grande, Redis usará colecciones de enteros como la implementación subyacente de los objetos de colección.

Como se muestra en la figura anterior, la codificación del conjunto de enteros indica su tipo, int16t, int32t o int64_t. Cada elemento es un elemento de matriz en la matriz de contenido, y cada elemento está organizado en orden desde el más pequeño hasta el más grande en la matriz, y la matriz no contiene ningún elemento duplicado. El atributo de longitud es el número de elementos contenidos en el conjunto de enteros.

6. Lista comprimida

La lista zip de la cola comprimida es una de las implementaciones subyacentes de los objetos de lista y los objetos hash. Cuando se cumplen ciertas condiciones, tanto el objeto de lista como el objeto hash se implementan con la cola comprimida como la capa inferior.

Redis desarrolla la cola comprimida para ahorrar memoria, y es una estructura de datos secuencial compuesta por una serie de bloques de memoria continua especialmente codificados. Sus valores de atributo son:

  • zlbytes: la longitud es de 4 bytes, que registra los bytes de memoria de toda la matriz comprimida.

  • zltail: la longitud es de 4 bytes y registra el número de bytes desde la dirección de inicio de la cola de compresión hasta el final de la cola de compresión. Este atributo se puede usar para determinar directamente la dirección del nodo de cola.

  • zllen: la longitud es de 2 bytes, incluido el número de nodos. Cuando el valor del atributo es menor que INT16_MAX, el valor es el número total de nodos; de lo contrario, es necesario recorrer toda la cola para determinar el número total.

  • zlend: la longitud es de 1 byte, un valor especial, que se utiliza para marcar el final de la cola de compresión.

Cada entrada de nodo en el medio consta de tres partes:

  • previous_entry_length: la longitud del nodo anterior en la lista comprimida, y realiza el cálculo del puntero con la dirección actual para calcular la dirección inicial del nodo anterior.

  • codificación: el tipo y la longitud de los datos guardados por el nodo

  • contenido: el valor del nodo, que puede ser una matriz de bytes o un entero.

Objeto

Se introdujeron 6 tipos de estructuras de datos de bajo nivel. Redis no utiliza directamente estas estructuras de datos para implementar bases de datos de valores clave, sino que crea un sistema de objetos basado en estas estructuras de datos. Este sistema incluye objetos de cadena, objetos de lista, objetos hash y colecciones Objetos y colecciones ordenadas de estos cinco tipos de objetos, cada objeto utiliza al menos una de las estructuras de datos subyacentes mencionadas anteriormente.

Redis determina qué estructura de datos usa el objeto en función de diferentes escenarios de uso y tamaños de contenido, optimizando así la eficiencia y la huella de memoria del objeto en diferentes escenarios.

La definición de la estructura Redis RedisObject se muestra a continuación.

typedef struct redisObject {

    unsigned type:4;

    unsigned encoding:4;

    unsigned lru:LRU_BITS; 

    int refcount;

    void *ptr;

} robj;
复制代码

Donde type es el tipo de objeto, incluidos REDISSTRING, REDISLIST, REDISHASH, REDISSET y REDIS_ZSET.

La codificación se refiere a la estructura de datos utilizada por el objeto, el conjunto completo es el siguiente.

1. Objeto de cadena

Primero observamos la implementación de objetos de cadena, como se muestra en la siguiente figura.

Si un objeto de cadena guarda un valor de cadena, y la longitud es mayor de 32 bytes, entonces el objeto de cadena se guardará usando SDS, y la codificación del objeto se establecerá en bruto, como se muestra en la parte superior de la figura. Si la longitud de la cadena es inferior a 32 bytes, el objeto de cadena se guardará utilizando la codificación embstr.

La codificación embstr es un método de codificación optimizado especialmente utilizado para guardar cadenas cortas. La composición de esta codificación es la misma que la codificación sin procesar. Ambas usan la estructura redisObject y la estructura sdshdr para almacenar las cadenas, como se muestra en la parte inferior de la figura anterior.

Pero la codificación sin formato llamará a dos asignaciones de memoria para crear las dos estructuras anteriores por separado, mientras que embstr asigna un espacio continuo a través de una asignación de memoria, y el espacio contiene dos estructuras a la vez.

embstr solo necesita una asignación de memoria y, en la misma memoria contigua, mejor uso de las ventajas que ofrece la memoria caché, pero embstr es de solo lectura y no se puede modificar. Cuando se agrega un objeto de cadena codificado por embstr, Redis ahora lo convertirá a codificación sin formato antes de continuar.

2. Lista de objetos

La codificación del objeto de lista puede ser ziplist o lista enlazada. El diagrama esquemático se muestra a continuación.

Cuando el objeto de lista puede cumplir las dos condiciones siguientes al mismo tiempo, el objeto de lista usa la codificación ziplist:

  • La longitud de todos los elementos de cadena almacenados en el objeto de lista es inferior a 64 bytes.

  • El número de elementos guardados en el objeto de la lista es inferior a 512.

Los objetos de lista que no pueden cumplir estas dos condiciones deben codificarse en una lista enlazada o convertirse en un código de lista enlazada.

3. Objeto hash

La codificación del objeto hash puede usar ziplist o dict. El diagrama esquemático se muestra a continuación.

Cuando el objeto hash usa la cola de compresión como la implementación subyacente, el programa inserta los pares clave-valor en la cola de compresión uno al lado del otro, con el nodo sosteniendo la clave primero y el nodo sosteniendo el valor segundo. Como se muestra en la parte superior de la figura a continuación, el hash tiene dos pares clave-valor, a saber, el nombre: Tom y la edad: 25.

Cuando el objeto hash puede cumplir las dos condiciones siguientes al mismo tiempo, el objeto hash se codifica mediante ziplist:

  • La longitud de la cadena de clave y valor de todos los pares clave-valor almacenados en el objeto hash es inferior a 64 bytes.

  • El número de pares clave-valor almacenados en el objeto hash es inferior a 512.

Los objetos hash que no pueden cumplir estas dos condiciones deben usar codificación dict o convertirse a codificación dict.

4. objetos de colección

Establecer la codificación de objetos puede usar intset o dict.

El objeto de colección codificado en intset utiliza el conjunto de enteros como la implementación de la capa inferior, y todos los elementos se almacenan en el conjunto de enteros.

Al codificar con dict, cada clave del diccionario es un objeto de cadena, y cada objeto de cadena es un elemento establecido, y todos los valores del diccionario están establecidos en NULL. Como se muestra a continuación.

Cuando el objeto de colección puede cumplir las dos condiciones siguientes al mismo tiempo, el objeto se codifica mediante intset:

  • Todos los elementos almacenados en el objeto de colección son valores enteros.

  • El número de elementos almacenados en el objeto de colección no supera los 512.

De lo contrario, use dict para codificar.

5. Objetos de colección ordenados

La codificación de un conjunto ordenado puede ser ziplist o skiplist.

Cuando una colección ordenada se codifica con ziplist, cada elemento de la colección está representado por dos nodos de lista comprimidos uno al lado del otro: el primer nodo es el valor del elemento y el segundo nodo es el puntaje del elemento, que es el valor de la comparación de clasificación.

Los elementos de la colección en la lista comprimida se ordenan de acuerdo con la puntuación de menor a mayor, como se muestra en la parte superior de la figura a continuación.

Los conjuntos ordenados se codifican usando skiplist usando la estructura zset como la implementación subyacente. Una estructura zet contiene un diccionario y una tabla de salto.

Entre ellos, la tabla de salto guarda todos los elementos de acuerdo con la puntuación de pequeño a grande, cada nodo de la tabla de salto guarda un elemento y su valor de puntuación es la puntuación del elemento. El diccionario crea un mapeo de miembros a puntajes.Las claves del diccionario son los valores de los miembros de la colección, y los valores del diccionario son los puntajes de los miembros de la colección. El diccionario se puede usar para encontrar el puntaje de un miembro dado en la complejidad O (1). Como se muestra a continuación.

Los objetos de valor del elemento de recopilación en la tabla de salto y el diccionario se comparten, por lo que no se consume memoria adicional.

Cuando el objeto conjunto ordenado puede cumplir las dos condiciones siguientes al mismo tiempo, el objeto se codifica mediante ziplist:

  • El número de elementos almacenados en un conjunto ordenado es inferior a 128;

  • La longitud de todos los elementos almacenados en un conjunto ordenado es inferior a 64 bytes.

De lo contrario, use la codificación skiplist.

6. Espacio clave de la base de datos

El servidor Redis tiene múltiples bases de datos Redis, y cada información de Redis tiene su propio espacio de valores clave independiente. Cada base de datos Redis usa dict para almacenar todos los pares clave-valor en la base de datos.

La clave del espacio clave es la clave de la base de datos.Cada clave es un objeto de cadena, y el objeto de valor puede ser uno de un objeto de cadena, un objeto de lista, un objeto de tabla hash, un objeto de colección y un objeto de colección ordenado.

Además del espacio clave, Redis también utiliza una estructura dict para guardar el tiempo de caducidad de la clave, que es el valor clave en el espacio clave y el valor es el tiempo de caducidad, como se muestra en la figura anterior.

A través del diccionario vencido, Redis puede determinar directamente si una clave está vencida. Primero, verifique si la clave existe en el diccionario vencido. Si existe, compare el tiempo de vencimiento de la clave con la marca de tiempo del servidor actual. .

Autor original: tecnologías de carretera Zhang huevos para perros

Reimpreso de: cuenta pública WeChat

Enlace original: mp.weixin.qq.com/s/gQnuynv6X ...


Progresar juntos, aprender y compartir

Todos son bienvenidos a prestar atención a mi cuenta pública [el viento y las olas son tranquilos y silenciosos ], una gran cantidad de artículos relacionados con Java, materiales de aprendizaje se actualizarán y la información recopilada también se colocará en él.

Si crees que es bueno escribir, ¡simplemente dale me gusta y agrega atención! ¡Presta atención, no te pierdas, sigue actualizando! ! !

Supongo que te gusta

Origin juejin.im/post/5e9c56e4f265da47b35c7d5f
Recomendado
Clasificación