Resumen de los puntos de conocimiento de Redis: estructura de datos

Este artículo no es un artículo de enseñanza, es solo para una descripción general rápida de los puntos de conocimiento.

prefacio

El tipo de superficie y el tipo de fondo de Redis

Para mejorar el rendimiento de lectura y escritura, en el caso de diferentes tipos de datos y volúmenes de datos, es necesario utilizar diferentes métodos de almacenamiento y gestión. Para mejorar el rendimiento del servidor y garantizar la facilidad de uso de redis. redis distingue la relación entre la estructura de datos subyacente y el objeto redis . Por ejemplo: cuando la cantidad de datos de HashMap es pequeña, la capa inferior se almacena en la estructura ziplist en lugar de dict; cuando el conjunto es una colección de enteros puros, use intset en lugar de dict, y así sucesivamente . Aquí, la estructura de datos y los objetos de superficie de la implementación subyacente se resumen por separado y, finalmente, se resuelve la relación entre ellos. Para aclarar el principio de implementación del tipo de datos redis, es muy importante garantizar la eficiencia de ejecución de los códigos que involucran la interacción redis en el desarrollo diario.

objetivo de aprendizaje

  1. Comprender las estructuras de datos de diferentes objetos y sus escenarios de uso.
  2. Comprender el tiempo y la eficiencia de las conversiones entre diferentes estructuras de datos
  3. Comprender que diferentes estructuras de datos funcionan de manera diferente
  4. Comprender el mecanismo de reciclaje de datos de redis y su posible impacto

estructura de datos subyacente

Cadenas dinámicas - SDS

característica

  1. La esencia es una matriz dinámica de caracteres.
  2. Los caracteres nulos '\0'no se cuentan en bytes usados ​​ni en bytes no usados
  3. Adopte la preasignación de espacio + la estrategia de liberación diferida
  4. Cuando el tamaño modificado es inferior a 1 MB, el espacio preasignado es igual al espacio utilizado; cuando es mayor a 1 MB, el espacio preasignado es 1 MB
  5. Liberación diferida significa que no se libera activamente, pero hay API de sds que proporcionan liberación de memoria
  6. Seguridad binaria. '\0'Marque el final de la cadena no con , sino con len
  7. En la implementación, además de existir de forma independiente, SDS también se anidará en otros tipos de objetos Redis para realizar objetos Redis específicos (por ejemplo, dict usa sds para almacenar valores de elementos específicos e implementar un mapa hash)
  1. Desbordamiento de memoria: uso de espacio no asignado
  2. Fuga de memoria: el espacio asignado ya no se usa, pero no se libera
  3. Solicitar espacio de memoria es una operación lenta: hay cambios entre el modo de usuario y el modo kernel y la programación de recursos externos (intercambio y cambio de pila; aplicación de memoria, etc. La velocidad de programación de la memoria es mucho más lenta que la velocidad informática de la CPU)
  4. Puede ser para mejorar aún más la utilización de la memoria redis. A partir de redis 3.2, la estructura sdshdr original se divide aún más en sdshdr5; sdshdr8; sdshdr16; sdshdr32; sdshdr64 para almacenar cadenas de diferentes longitudes. Entre ellos, sdshdr5 dice en su código fuente que nunca se utilizará

eficacia

  1. La adquisición de longitud de cadena es O(1) —— lee directamente la variable miembro de la estructura sds
  2. Borrar cadenas (sdsclear) es O(1) - se realiza directamente len = 0, sin restablecer valores de elementos individuales
  3. Liberar el espacio de memoria (sdsfree) es O(n): n es el espacio de memoria asignado. Tenga en cuenta que es diferente de sdsclear
  1. redis/src/sds.h
  2. redis/src/sds.c

Lista doblemente enlazada - list&listNode

característica

  1. Polimorfismo : listNode usa void *valores almacenados. Diferentes tipos de métodos dup; match; establecidosfree por macros específicaslistSet<Xxx>Method
  2. La estructura de la lista contiene punteros de longitud y cabeza y cola.

eficacia

  1. Las operaciones relacionadas con el predecesor y el sucesor son O (1): obtener el predecesor/sucesor, insertar el nodo, eliminar el nodo dado
  2. Las operaciones que necesitan encontrar un solo nodo son todas O(n): obtener nodos por valor, obtener nodos por índice, eliminar nodos, etc.
  1. redis/src/adlist.h
  2. redis/src/adlist.c

Saltar lista - zskipList&zskipListNode

característica

  1. La estructura contiene directamente los punteros de cabeza y cola, la longitud y el número máximo de capas
  2. Cada nodo contiene 1 puntero hacia atrás
  3. Cada nodo genera punteros de rango [1, 32] basados ​​en la ley de potencia
  4. Los miembros del nodo se ordenan por puntaje
  5. Cada objeto miembro del nodo es único

eficacia

  1. redis/src/servidor.h
  2. redis/src/t_zset.c

Inicio——dick&dictht&dictEntrada

característica

  1. Algoritmo hash usando murmursh2/3 . Después de calcular el valor hash, &sizemaskdetermine la ubicación del par clave-valor.
  2. Resuelva conflictos usando una lista enlazada individualmente
  3. Cuando el factor de carga es superior a 1 o 0,5 (activar RDB), se expandirá y repetirá, y cuando sea inferior a 0,1, reducirá el espacio y repetirá
  4. El dict almacena dos punteros de tabla hash dicttht[0] y dicttht[1] en una matriz, que se utilizan para ejecutar rehash en escala de grises
  5. Durante el proceso de refrito, los datos antiguos dictht[0] solo disminuyen pero no aumentan , y el proceso de migración específico se divide en cada proceso CRUD. Contar el progreso del refrito actual por r ehashidx
  6. Durante el refrito, la operación RUD accederá a dos tablas sucesivamente

eficacia

  1. Eliminar un par clave-valor específico (Delete) es O(1), liberar todos los pares clave-valor es O(n)
  2. Las operaciones CRUD son O(1)
  3. La devolución aleatoria de pares clave-valor también es O (1)
  1. redis/src/dict.h
  2. redis/src/dict.c
  3. Familia de funciones MurmurHash y conjunto de pruebas utilizado por redis: aappleby/smhasher

Lista comprimida - zipList

característica

  1. es un espacio de almacenamiento continuo
  2. almacenar el desplazamiento del nodo final
  3. Los primeros 4 bytes registran el número total de bytes ocupados por todo el ziplist
  4. Marca el final de zipList con 0xFF

para cada nodo

  1. Los primeros 1 o 5 bytes registran la longitud en bytes del predecesor
  2. Cuando el precursor tiene menos de 254 bytes, el primer byte del nodo actual almacena la longitud del precursor. De lo contrario, el primer byte es 0xFE y la longitud del precursor se registra en los siguientes 4 bytes

eficacia

  1. Las operaciones relacionadas con el predecesor/sucesor son O(1)
    : calcule la posición del predecesor/sucesor en función del desplazamiento
  2. Obtener el número total de bytes ocupados por zipList es O(1)
    —— almacenado en los primeros 4 bytes de zipList
  3. El mejor caso para obtener el número total de nodos en zipList es O(1), y el peor caso es O(n)
    —— el número de nodos es mayor que 65535
  4. Mejor caso O(n) para búsqueda - entero, peor caso O(n^2) - matriz de bytes
  5. El mejor caso para insertar, crear, eliminar es O(n), el peor caso es O(n^2)
    - puede desencadenar actualizaciones encadenadas

conjunto entero - intset

característica

  1. Almacenar directamente en una tabla lineal, posiblemente int16[], int32[], int64[]
  2. Cuando un entero nuevo excede el rango representado por el tipo actual, se activará una operación de actualización. Los elementos nuevos solo estarán en la cabeza (demasiado pequeños) o en la cola (demasiado grandes)
  3. No hay una operación de degradación (tenga en cuenta que es diferente de la implementación de dict, cuando el factor de carga de dict es inferior a 0,1, se activará la contracción y el refrito)

eficacia

  1. Agregar o eliminar nuevos elementos es O(n)
  2. El proceso de búsqueda de una matriz de enteros se puede buscar por la mitad, O (log n)
  3. Obtener el número de bytes y el número de elementos es O(1), que es diferente de zipList

objeto de superficie

El artículo anterior presentó la estructura de datos principal de Redis, pero en la implementación específica. Para String, List, Set, ZSet y HashMap son en realidad objetos de Redis. Redis define la estructura RedisObject, que contiene un void *ptrutilizado para apuntar a una estructura de datos específica. En diferentes condiciones, el mismo objeto también se puede almacenar y administrar con diferentes estructuras de datos (es decir, las variables miembro ptr del mismo RedisObject apuntan a diferentes estructuras de datos). A continuación, una breve descripción de los diferentes RedisObjects.
Definición de RedisObject:

typedef struct redisObject {
    
    
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS;
    int refcount;
    void *ptr;
} robj;

Cadena

condición método de almacenamiento
<= 32 bytes EmbStr
> 32 bytes SDS
Entero, puede ser representado por largo int (largo)
  1. Los números de punto flotante también se almacenan con embStr o SDS
  2. INCRBYFLOATLas operaciones en tipo int activarán la conversión de tipo

El llamado embStr consiste en utilizar un espacio de almacenamiento continuo para almacenar estructuras redisObject y SDS al mismo tiempo. La diferencia clave con SDS es que SDS necesita ejecutar dos solicitudes de memoria, mientras que embStr solo necesita una .

Lista

condición método de almacenamiento
La longitud de todos los elementos < list-max-ziplist-value(Byte)
&&
el número de elementos < list-max-ziplist-entries(Byte)
zipLista
otro lista enlazada

Colocar

condición método de almacenamiento
Todos los elementos se pueden representar mediante int largo
&&
número de elementos < set-max-intset-entries(Byte)
recuadro
otro dictar

Conjunto Z

condición método de almacenamiento
Longitud de todos los elementos < zset-max-ziplist-value
&&
número de elementos < zset-max-ziplist-entries (Byte)
zipLista
otro skipList + dict

Cuando hay una gran cantidad de datos, use dos estructuras diferentes skipList y dict para almacenar los mismos datos. Es beneficioso mantener la eficiencia de búsqueda en O(1), mientras que las operaciones relacionadas con la clasificación (ZRANGE, ZRANK) mantienen O(n)

mapa hash

condición método de almacenamiento
Longitud de todos los elementos < zset-max-ziplist-value
&&
número de elementos < zset-max-ziplist-entries (Byte)
zipLista
otro dictar
  1. En zipList, la clave también es un elemento de zipList, y el elemento donde se encuentra la clave y el elemento donde se encuentra el valor siempre están juntos
  2. Tanto la clave como el valor se almacenan como StringObject. Para conocer las características de almacenamiento de StringObject, consulte la descripción del objeto String en este artículo.

polimorfismo

El polimorfismo de Redis se implementa en función de la verificación de tipos y la verificación de métodos de codificación. Permite que el mismo comando realice las operaciones correspondientes en diferentes objetos Redis y métodos de codificación

  1. Redis utiliza la verificación de tipos para determinar si un comando se puede ejecutar para una clave específica.
    Por ejemplo, DELse puede ejecutar para todos los tipos, pero GETsolo para String
  2. encodingRedis juzga cómo realizar una operación específica en un objeto de Redis comprobando el método de codificación (como un objeto HashMap, cuya estructura contiene encodingvariables miembro, que indican zipListsidict

recuperación de memoria

El GC de Redis es básicamente lo mismo que la idea central del GC de PHP:

  1. Agregar variables refcountmiembro
  2. El nuevo objeto tiene un recuento de referencias de 1
  3. Se agregó +1 por beber, ya no -1 por ser usado
  4. Liberar inmediatamente cuando 0

La función de reciclaje se refiere al método de redis/src/object.cdecrRefCount

Pero además, Redis también gestiona los objetos Redis a través de LRU.Puedes ver que la estructura RedisObject definida anteriormente se unsigned lru:LRU_BITS;utiliza para registrar la última hora de acceso. Cuando la memoria utilizada por Redis alcanza maxmemory. El lru más pequeño (cuanto más tiempo no se haya utilizado), se liberará primero.

Esto nos recuerda que para evitar la pérdida de datos, además de garantizar el normal funcionamiento a nivel de hardware del servidor Redis, es necesario tomar las siguientes medidas:

  1. Asegúrese de que el espacio de almacenamiento no esté lleno; durante las horas de menor actividad, es más apropiado mantener el uso de la memoria de Redis por debajo del 30 %.
  2. Para los datos que deben persistir, la operación de persistencia debe realizarse a tiempo y debe haber mecanismos de registro, monitoreo y alarma correspondientes para evitar la pérdida de datos no persistentes.
  3. Balanceo de carga: se puede manejar con Redis-Cluster u otro middleware

Supongo que te gusta

Origin blog.csdn.net/qq_23937195/article/details/108911772
Recomendado
Clasificación