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.
Tabla de contenido
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
- Comprender las estructuras de datos de diferentes objetos y sus escenarios de uso.
- Comprender el tiempo y la eficiencia de las conversiones entre diferentes estructuras de datos
- Comprender que diferentes estructuras de datos funcionan de manera diferente
- Comprender el mecanismo de reciclaje de datos de redis y su posible impacto
estructura de datos subyacente
Cadenas dinámicas - SDS
característica
- La esencia es una matriz dinámica de caracteres.
- Los caracteres nulos
'\0'
no se cuentan en bytes usados ni en bytes no usados - Adopte la preasignación de espacio + la estrategia de liberación diferida
- 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
- Liberación diferida significa que no se libera activamente, pero hay API de sds que proporcionan liberación de memoria
- Seguridad binaria.
'\0'
Marque el final de la cadena no con , sino con len - 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)
- Desbordamiento de memoria: uso de espacio no asignado
- Fuga de memoria: el espacio asignado ya no se usa, pero no se libera
- 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)
- 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
- La adquisición de longitud de cadena es O(1) —— lee directamente la variable miembro de la estructura sds
- Borrar cadenas (sdsclear) es O(1) - se realiza directamente
len = 0
, sin restablecer valores de elementos individuales - Liberar el espacio de memoria (sdsfree) es O(n): n es el espacio de memoria asignado. Tenga en cuenta que es diferente de sdsclear
Lista doblemente enlazada - list&listNode
característica
- Polimorfismo : listNode usa
void *
valores almacenados. Diferentes tipos de métodosdup
;match
; establecidosfree
por macros específicaslistSet<Xxx>Method
- La estructura de la lista contiene punteros de longitud y cabeza y cola.
eficacia
- Las operaciones relacionadas con el predecesor y el sucesor son O (1): obtener el predecesor/sucesor, insertar el nodo, eliminar el nodo dado
- Las operaciones que necesitan encontrar un solo nodo son todas O(n): obtener nodos por valor, obtener nodos por índice, eliminar nodos, etc.
Saltar lista - zskipList&zskipListNode
característica
- La estructura contiene directamente los punteros de cabeza y cola, la longitud y el número máximo de capas
- Cada nodo contiene 1 puntero hacia atrás
- Cada nodo genera punteros de rango [1, 32] basados en la ley de potencia
- Los miembros del nodo se ordenan por puntaje
- Cada objeto miembro del nodo es único
eficacia
Inicio——dick&dictht&dictEntrada
característica
- Algoritmo hash usando murmursh2/3 . Después de calcular el valor hash,
&sizemask
determine la ubicación del par clave-valor. - Resuelva conflictos usando una lista enlazada individualmente
- 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á
- 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
- 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
- Durante el refrito, la operación RUD accederá a dos tablas sucesivamente
eficacia
- Eliminar un par clave-valor específico (Delete) es O(1), liberar todos los pares clave-valor es O(n)
- Las operaciones CRUD son O(1)
- La devolución aleatoria de pares clave-valor también es O (1)
- redis/src/dict.h
- redis/src/dict.c
- Familia de funciones MurmurHash y conjunto de pruebas utilizado por redis: aappleby/smhasher
Lista comprimida - zipList
característica
- es un espacio de almacenamiento continuo
- almacenar el desplazamiento del nodo final
- Los primeros 4 bytes registran el número total de bytes ocupados por todo el ziplist
- Marca el final de zipList con 0xFF
para cada nodo
- Los primeros 1 o 5 bytes registran la longitud en bytes del predecesor
- 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
- Las operaciones relacionadas con el predecesor/sucesor son O(1)
: calcule la posición del predecesor/sucesor en función del desplazamiento - Obtener el número total de bytes ocupados por zipList es O(1)
—— almacenado en los primeros 4 bytes de zipList - 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 - Mejor caso O(n) para búsqueda - entero, peor caso O(n^2) - matriz de bytes
- 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
- Almacenar directamente en una tabla lineal, posiblemente int16[], int32[], int64[]
- 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)
- 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
- Agregar o eliminar nuevos elementos es O(n)
- El proceso de búsqueda de una matriz de enteros se puede buscar por la mitad, O (log n)
- 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 *ptr
utilizado 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) |
- Los números de punto flotante también se almacenan con embStr o SDS
INCRBYFLOAT
Las 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 |
- 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
- 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
- Redis utiliza la verificación de tipos para determinar si un comando se puede ejecutar para una clave específica.
Por ejemplo,DEL
se puede ejecutar para todos los tipos, peroGET
solo para String encoding
Redis 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 contieneencoding
variables miembro, que indicanzipList
sidict
recuperación de memoria
El GC de Redis es básicamente lo mismo que la idea central del GC de PHP:
- Agregar variables
refcount
miembro - El nuevo objeto tiene un recuento de referencias de 1
- Se agregó +1 por beber, ya no -1 por ser usado
- Liberar inmediatamente cuando 0
La función de reciclaje se refiere al método de redis/src/object.c
decrRefCount
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:
- 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 %.
- 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.
- Balanceo de carga: se puede manejar con Redis-Cluster u otro middleware