Refine el objeto hash de la estructura de datos de alto rendimiento de Redis

antecedentes

La última sección habló sobre la estructura de cadenas de alto rendimiento SDS de Redis , hoy analizamos el objeto hash de redis.

Objeto hash

Introducción

  • Hay dos métodos de codificación (implementación subyacente) para objetos hash de redis, codificación de diccionario y codificación de lista comprimida. Cuando se utiliza la codificación de diccionario, el programa guarda la clave de la tabla hash como la clave del diccionario y el valor del hash como el valor del diccionario El valor clave del diccionario es de tipo cadena.
  • La longitud de las cadenas de clave y valor de todos los pares clave-valor almacenados en el objeto hash es inferior a 64 bytes y la cantidad de pares clave-valor almacenados en el objeto hash es inferior a 512. Se utiliza la lista zip y la tabla hash ( Codificación de diccionario)

Entendimiento profundo

ZipList (lista comprimida)

  1. La lista comprimida de redis es un espacio de memoria continuo, y los elementos se almacenan uno al lado del otro, sin ningún espacio redundante.
  2. Código fuente
struct ziplist<T> {
int32 zlbytes; // 整个压缩列表占用字节数
int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个
节点
int16 zllength; // 元素个数
T[] entries; // 元素内容列表,挨个挨个紧凑存储
int8 zlend; // 标志压缩列表的结束,值恒为 0xFF
}


/**
*entry对象源码
*/
struct entry {
int<var> prevlen; // 前一个 entry 的字节长度
int<var> encoding; // 元素类型编码
optional byte[] content; // 元素内容
}
  1. La lista comprimida admite recorrido bidireccional, por lo que existe el campo zltail_offset, que puede ubicar rápidamente el último elemento. Luego busque en orden inverso (O (1))
  2. Prevlen significa la longitud del campo anterior. Algunas personas tienen preguntas. ¿Por qué es la longitud de la entrada anterior? ¿Por qué no es la tuya? De hecho, tiene otra función que usar al comprimir el recorrido de flashback de la lista. Localice rápidamente la ubicación del siguiente elemento. Debido a que es un espacio de almacenamiento continuo, puede determinar la ubicación de una entrada conociendo la ubicación del elemento actual + la dirección de este espacio. ¿Por qué esto es tan? Porque el tamaño de la entrada es diferente. Si son iguales, pueden comportarse de acuerdo con la siguiente tabla (comprensión personal, señale si hay errores), y prevlen es un entero de longitud variable. El funcionamiento normal de redis utiliza diferentes tipos de datos para diferentes longitudes. Guardar memoria
  3. Codificación significa el tipo de codificación del elemento Con este campo se puede determinar la configuración del contenido del elemento y la asignación del tamaño de la memoria. Una forma de evitar el desperdicio de asignación de memoria. Vea el contenido específico a continuación
1、00xxxxxx 最大长度位 63 的短字符串,后面的 6 个位存储字符串的位数,剩余的字
节就是字符串的内容。
2、01xxxxxx xxxxxxxx 中等长度的字符串,后面 14 个位来表示字符串的长度,剩余的
字节就是字符串的内容。
3、10000000 aaaaaaaa bbbbbbbb cccccccc dddddddd 特大字符串,需要使用额外 4 个字节
来表示长度。第一个字节前缀是 10,剩余 6 位没有使用,统一置为零。后面跟着字符串内
容。不过这样的大字符串是没有机会使用的,压缩列表通常只是用来存储小数据的。
4、11000000 表示 int16,后跟两个字节表示整数。
5、11010000 表示 int32,后跟四个字节表示整数。
6、11100000 表示 int64,后跟八个字节表示整数。
7、11110000 表示 int24,后跟三个字节表示整数。
8、11111110 表示 int8,后跟一个字节表示整数。
9、11111111 表示 ziplist 的结束,也就是 zlend 的值 0xFF。
10、1111xxxx 表示极小整数,xxxx 的范围只能是 (0001~1101), 也就是 1~13,因为
0000、1110、1111 都被占用了。读取到的 value 需要将 xxxx 减 1,也就是整数 0~12 就是
最终的 value。
  1. Hay dos requisitos previos para seleccionar una lista comprimida para objetos hash. Uno de ellos es que el tamaño del valor clave sea menor que 64. ¿Por qué es menor que 64 y el par clave-valor es menor que 512? No lo diré específicamente. Puede combinar SDS. Piense en el método de expansión en, no hay espacio redundante en la lista comprimida. Se producirá una expansión frecuente durante la expansión. Además, copiar datos después de ocupar un gran espacio es una pérdida de rendimiento. Entonces, cuando la cantidad de datos es grande, se elige otra estructura de datos, que es hashtable (diccionario)

HashTable (diccionario)

Introducción

  • La implementación de hashtable en redis y hashMap en java es similar, y ambos se implementan a través de matrices y listas vinculadas. Es decir, la forma clave-valor. Por supuesto, también utiliza el método de dirección en cadena para resolver conflictos de hash (puede pensar en varias formas de resolver conflictos de hash). Cuando diferentes claves crean el mismo valor de hash, vlue se coloca en la lista vinculada, como se muestra a continuación.

  • Todavía hay una gran diferencia entre los detalles y el hashMap en java. Los ejemplos incluyen el proceso de expansión, el algoritmo hash de valor clave, etc. A continuación, haremos un producto detallado basado en el código fuente.
    Inserte la descripción de la imagen aquí

  • La explicación oficial: un diccionario, también conocido como mapa o matriz asociativa, es una estructura de datos abstracta compuesta por un conjunto de pares clave-valor y cada par clave-valor Las claves son diferentes, el programa puede agregar nuevos pares clave-valor al diccionario, o buscar, actualizar o eliminar según las claves

La estructura subyacente del diccionario usa el dict in redis. No solo se usa el dict en la parte inferior del objeto hash, sino que también la estructura key-vlue se usa globalmente en redis, que es la forma de un diccionario, y la capa inferior de la estructura de datos de Zset también se basa en la estructura dict en redis. Echemos un vistazo a su código fuente:

// resdis 全局使用的字典结构
struct RedisDb {
dict* dict; // all keys key=>value
dict* expires; // all expired keys key=>long(timestamp)
...}
// 有序集合的底层数据结构
struct zset {
dict *dict; // all values value=>score
zskiplist *zsl;
}
2. Análisis profundo de la estructura del dictado
  • Código fuente:
/*
 * 字典
 *
 * 每个字典使用两个哈希表,用于实现渐进式 rehash
 */
typedef struct dict {

    // 特定于类型的处理函数
    dictType *type;

    // 类型处理函数的私有数据
    void *privdata;

    // 哈希表(2 个)
    dictht ht[2];

    // 记录 rehash 进度的标志,值为 -1 表示 rehash 未进行
    int rehashidx;

    // 当前正在运作的安全迭代器数量
    int iterators;

} dict;
  • Puede ver que hay dos tablas hash en las variables miembro de la clase, normalmente una tiene un valor y la otra no tiene valor. El problema que encontramos en la lista comprimida es el problema de rendimiento en la expansión. Estas dos tablas hash son para resolver el problema de expansión. Reubique gradualmente durante la expansión y la contracción. Cuando finalice la reubicación, se eliminará la tabla hash anterior y se reemplazará la tabla hash nueva.
  • Entonces echemos un vistazo más de cerca a hashtable (hashtable en Java es una versión segura para subprocesos de hashMap en Java). La tabla hash aquí es similar al mapa hash en Java, y la forma de resolver los conflictos de hash es mediante el agrupamiento. Matriz unidimensional, lista enlazada bidimensional. Pero aún existen algunas diferencias en la expansión.
struct dictEntry {
void* key;
void* val;
dictEntry* next; // 链接下一个 entry
}
struct dictht {
dictEntry** table; // 二维
long size; // 第一维数组的长度
long used; // hash 表中的元素个数
...
}
  • Echemos un vistazo a cómo se realiza el hash en redis.
    1. La expansión del diccionario grande consume mucho tiempo. Necesita volver a solicitar una nueva matriz y luego volver a adjuntar todos los elementos de la lista vinculada del diccionario antiguo a la nueva matriz. Esto La complejidad del tiempo de proceso es O (n). Como redis de un solo subproceso, ¿cómo puede redis perder tiempo aquí? Entonces adoptó el método de procesamiento progresivo (cuando se trata del método progresivo, ¿puede pensar en su consulta por lotes progresiva basada en el escaneo de teclas y las claves)? Haga clic aquí para ver el proceso de refrito . La idea es la ejecución de pequeños pasos que mencionamos anteriormente.
  • La estructura Set también se implementa a través de un diccionario, pero no todos los valores son NULL, ¿has pensado en algo? ¿Es el hashSet en Java similar a esto? .

para resumir

  1. Hay dos implementaciones subyacentes de objetos hash, hashtable (diccionario) y ziplist (lista enlazada comprimida)
  2. Debido a que la lista vinculada comprimida es un espacio continuo, el rendimiento es significativo cuando la cantidad de datos es pequeña al principio, pero el problema de la expansión lenta ocurrirá cuando la cantidad de datos sea grande.
  3. El diccionario resuelve el problema de la expansión de la lista comprimida mediante el método double-hahstable y el método hash progresivo.
  4. Redis estructura de datos de alto rendimiento, podemos ver que tiene muchos detalles sobre los detalles, como diferentes tamaños de números, diferentes tipos de campo, y el mismo objeto selecciona diferentes tipos de almacenamiento de acuerdo con el tamaño. (Guardar memoria)

Supongo que te gusta

Origin blog.csdn.net/weixin_40413961/article/details/107948498
Recomendado
Clasificación