Diseño e implementación de Redis: estructura de datos de redisObject y tipo de datos de Redis

Estructura de datos de RedisObject y tipo de datos de Redis

RedisObject es el núcleo del sistema de tipos Redis. Cada clave y valor en la base de datos, así como los parámetros procesados ​​por Redis, se representan como este tipo de datos.

La definición de redisObject se encuentra en redis.h:

/*
 * Redis 对象
 */
typedef struct redisObject {

    // 类型
    unsigned type:4;

    // 对齐位
    unsigned notused:2;

    // 编码方式
    unsigned encoding:4;

    // LRU 时间(相对于 server.lruclock)
    unsigned lru:22;

    // 引用计数
    int refcount;

    // 指向对象的值
    void *ptr;

} robj;

Tipo, codificación y ptr son los tres atributos más importantes.

type registra el tipo del valor que posee el objeto. Su valor puede ser una de las siguientes constantes (la definición se encuentra en redis.h):

/*
 * 对象类型
 */
#define REDIS_STRING 0  // 字符串
#define REDIS_LIST 1    // 列表
#define REDIS_SET 2     // 集合
#define REDIS_ZSET 3    // 有序集
#define REDIS_HASH 4    // 哈希表

codificación registra la codificación del valor contenido por el objeto, su valor puede ser una de las siguientes constantes (la definición se encuentra en redis.h):

/*
 * 对象编码
 */
#define REDIS_ENCODING_RAW 0            // 编码为字符串
#define REDIS_ENCODING_INT 1            // 编码为整数
#define REDIS_ENCODING_HT 2             // 编码为哈希表
#define REDIS_ENCODING_ZIPMAP 3         // 编码为 zipmap
#define REDIS_ENCODING_LINKEDLIST 4     // 编码为双端链表
#define REDIS_ENCODING_ZIPLIST 5        // 编码为压缩列表
#define REDIS_ENCODING_INTSET 6         // 编码为整数集合
#define REDIS_ENCODING_SKIPLIST 7       // 编码为跳跃表

ptr es un puntero a la estructura de datos que realmente contiene el valor. Esta estructura de datos está determinada por el atributo de tipo y el atributo de codificación.

Por ejemplo, si el atributo de tipo de un redisObject es REDIS_LIST y el atributo de codificación es REDIS_ENCODING_LINKEDLIST, entonces el objeto es una lista de Redis, su valor se almacena en una lista vinculada de doble extremo, y el puntero ptr apunta a la lista vinculada de doble extremo;

Por otro lado, si el atributo type de un redisObject es REDIS_HASH y el atributo de codificación es REDIS_ENCODING_ZIPMAP, entonces el objeto es una tabla hash de Redis, su valor se almacena en un zipmap y el puntero ptr apunta al zipmap; y así sucesivamente.

La siguiente figura muestra la relación entre redisObject, todos los tipos de datos de Redis y todos los métodos de codificación de Redis (implementación subyacente):

1233356-8e81ee9e3444879f.png

Verificación de tipo de comando y polimorfismo
Con la existencia de la estructura redisObject, es mucho más simple realizar operaciones de verificación de tipo y operaciones polimórficas en la codificación al ejecutar comandos que procesan tipos de datos.

Al ejecutar un comando que trata con tipos de datos, Redis realiza los siguientes pasos:

  • De acuerdo con la clave dada, busque el redisObject correspondiente en el diccionario de la base de datos; si no se encuentra, devuelve NULL.
  • Compruebe si el atributo de tipo de redisObject coincide con el tipo requerido para ejecutar el comando. Si no coincide, se devuelve un error de tipo.
  • De acuerdo con la codificación especificada por el atributo de codificación de redisObject, seleccione la función de operación adecuada para procesar la estructura de datos subyacente.
  • El resultado de la operación de la estructura de datos devuelta se utiliza como el valor de retorno del comando.

Como ejemplo, lo siguiente muestra el proceso completo de ejecución del comando LPOP en la tecla:

1233356-34fc288ad09bb908.png

Compartir objetos

Algunos objetos son muy comunes en Redis. Por ejemplo, el valor de retorno del comando es OK, ERROR, WRONGTYPE y otros caracteres. Además, algunos enteros de rango pequeño, como enteros de un solo dígito, decenas y cien dígitos son muy comunes.

Para aprovechar esta situación común, Redis utiliza un modo Flyweight internamente: al preasignar algunos objetos de valor común y compartir estos objetos entre múltiples estructuras de datos, el programa evita el problema de la asignación repetida y también ahorra algo de CPU Tiempo

Los objetos de valor redistribuido previamente son los siguientes:

Los valores de retorno de varios comandos, como OK devuelto cuando la ejecución es exitosa, ERROR devuelto cuando se ejecuta el error, WRONGTYPE devuelto cuando el tipo es incorrecto, QUEUED devuelto cuando el comando está en cola, y así sucesivamente.
Incluyendo 0, todos los enteros menores que redis.h / REDIS_SHARED_INTEGERS (el valor predeterminado de REDIS_SHARED_INTEGERS es 10000)
Debido a que el valor de respuesta del comando se devuelve directamente al cliente, sus valores no necesitan compartirse; por otro lado, si un comando El valor de entrada es un objeto entero menor que REDIS_SHARED_INTEGERS, luego, cuando este objeto se va a guardar en la base de datos, Redis liberará el valor original y apuntará el puntero de valor al objeto compartido.

Como ejemplo, la siguiente figura muestra tres listas, todas con punteros a un objeto de valor en la matriz de objetos compartidos:

1233356-faea59bdc4f82c26.png

Los valores de las tres listas son:

列表 A : [20130101, 300, 10086] ,
列表 B : [81, 12345678910, 999] ,
列表 C : [100, 0, -25, 123] 。

Recuento de referencias y destrucción de objetos.

Cuando redisObject se usa como clave o valor de la base de datos, en lugar de usarse para almacenar parámetros, la vida útil del objeto es muy larga, porque el lenguaje C en sí no tiene un mecanismo relevante para liberar memoria automáticamente. Si solo confía en la memoria del programador para objetar El seguimiento y la destrucción son básicamente imposibles.

Por otro lado, como se mencionó anteriormente, un objeto compartido puede ser referenciado por múltiples estructuras de datos. En este momento, preguntas como "¿Cuántas veces se hace referencia a este objeto?"

Para resolver los dos problemas anteriores, el sistema de objetos de Redis utiliza tecnología de recuento de referencias para mantener y destruir objetos. Su mecanismo operativo es el siguiente:

Cada estructura de redisObject tiene un atributo de recuento que indica cuántas veces se ha referenciado este objeto.

Cuando se crea un nuevo objeto, su propiedad de recuento se establece en 1.
Al compartir un objeto, Redis incrementa el recuento del objeto en uno.
Después de usar un objeto, o después de desreferenciar un objeto compartido, el programa disminuye el recuento del objeto en uno.
Cuando el recuento de un objeto cae a 0, se liberará la estructura redisObject y la memoria de la estructura de datos a la que hace referencia.

Resumen

Redis utiliza su propio mecanismo de objeto implementado para implementar la determinación de tipo, el polimorfismo de comando y la recolección de basura en función del recuento de referencias.

Una clave de tipo Redis puede tener múltiples implementaciones subyacentes.
Redis asignará previamente algunos objetos de datos de uso común y compartirá estos objetos para reducir el uso de memoria y evitar la asignación frecuente de memoria para objetos pequeños.

Diseño e implementación de Redis:

https://redisbook.readthedocs.io/en/latest/index.html

Versión anotada del código fuente de Redis 2.6:

https://github.com/to-be-architect/annotated_redis_source


Comunidad de desarrolladores de Kotlin

1233356-4cc10b922a41aa80

La cuenta pública de la primera comunidad de desarrolladores de Kotlin en China, que comparte e intercambia principalmente temas relacionados, como el lenguaje de programación Kotlin, Spring Boot, Android, React.js / Node.js, programación funcional e ideas de programación.

Cuanto más ruidoso es el mundo, más pensamiento pacífico se necesita.

1665 artículos originales publicados · 1067 elogiados · 750,000 vistas

Supongo que te gusta

Origin blog.csdn.net/universsky2015/article/details/105242575
Recomendado
Clasificación