Red de conocimiento informático - Problemas con los teléfonos móviles - La diferencia entre redis y memcached

La diferencia entre redis y memcached

Salvatore Sanfilippo, el autor de Redis, comparó una vez estos dos sistemas de almacenamiento de datos basados ​​en memoria:

1. Redis admite operaciones de datos del lado del servidor: en comparación con Memcached, Redis tiene más estructuras de datos y un soporte más rico. operaciones. Normalmente, en Memcached es necesario llevar los datos al cliente para realizar modificaciones similares y luego volver a configurarlos. Esto aumenta en gran medida la cantidad de E/S de red y el volumen de datos. En Redis, estas operaciones complejas suelen ser tan eficientes como GET/SET normales. Por lo tanto, si necesita un caché que pueda admitir estructuras y operaciones más complejas, Redis sería una buena opción.

2. Comparación de la eficiencia de utilización de la memoria: si se utiliza un almacenamiento de valores-clave simple, Memcached tiene una mayor utilización de la memoria, mientras que si Redis usa una estructura hash para el almacenamiento de valores-clave, su utilización de la memoria es menor debido a su La compresión combinada será mayor que la de Memcached.

3. Comparación de rendimiento: debido a que Redis solo usa un núcleo y Memcached puede usar múltiples núcleos, en promedio, el rendimiento de Redis en el almacenamiento de datos pequeños en cada núcleo es mayor que el de Memcached. El rendimiento de Memcached es superior al de Redis en datos de más de 100k. Aunque Redis ha optimizado recientemente su rendimiento para almacenar grandes datos, todavía es ligeramente inferior a Memcached.

¿Por qué apareció la conclusión anterior? Los siguientes son los datos recopilados:

1, admite diferentes tipos de datos.

A diferencia de Memcached, que solo admite registros de datos clave-valor simples, Redis admite tipos de datos más completos. Los tipos de datos más utilizados constan principalmente de cinco tipos: cadenas, hashes, listas, conjuntos y conjuntos ordenados. Redis utiliza objetos redisObject internamente para representar todas las claves y valores. La información principal de redisObject se muestra en la figura:

El tipo representa el tipo de datos específico de un objeto de valor y la codificación es el método de almacenamiento de diferentes tipos de datos en redis. Por ejemplo, tipo = cadena significa que el valor almacena una cadena normal y la codificación correspondiente puede ser sin formato o int. Si es un int, significa que la cadena realmente se almacena y representa en redis según el tipo numérico, siempre que la cadena en sí pueda representarse mediante un valor numérico, como "123", "456". Solo cuando la función de memoria virtual de Redis esté activada, el dominio vm realmente asignará memoria. Está desactivado de forma predeterminada.

1) Cadena

Comandos comunes: set/get/decr/incr/mget, etc.

Escenario de aplicación: la cadena es el tipo de datos más utilizado y el almacenamiento de clave/valor ordinario se puede clasificar en esta categoría;

Implementación: las cadenas almacenadas en redis de forma predeterminada son las cadenas a las que se hace referencia. por redisObjeto. Cuando encuentre incr, decr y otras operaciones, se convertirá a un tipo numérico para el cálculo. El campo de codificación de redisObject es int.

2) Hash

Comandos de uso común: hget/hset/hgetall, etc.

Escenario de aplicación: queremos almacenar datos de un objeto de información de usuario, incluida la ID de usuario, el nombre de usuario, la edad y la fecha de nacimiento. Esperamos obtener el nombre, la edad o la fecha de nacimiento del usuario a través de la ID de usuario;

Implementación: El Hash de Redis es en realidad el valor almacenado internamente en un HashMap, que proporciona una interfaz para acceso directo a los miembros del Mapa. Como se muestra en la imagen, la clave es la identificación del usuario y el valor es el mapa. La clave de este mapa es el nombre del atributo del miembro y el valor es el valor del atributo. De esta manera, los datos se pueden modificar y acceder directamente a través de su clave asignada internamente (la clave asignada internamente en Redis se llama campo), es decir, los datos del atributo correspondiente se pueden operar a través de la clave (ID de usuario) + campo (etiqueta de atributo). ). Actualmente hay dos formas de implementar HashMap: cuando el número de miembros de HashMap es pequeño, para ahorrar memoria, Redis utilizará un método similar a una matriz unidimensional para el almacenamiento compacto en lugar de utilizar la estructura real de HashMap.

En este momento, la codificación del valor correspondiente de redisObject es zipmap. Cuando aumenta el número de miembros, automáticamente se convertirá en un HashMap real. La codificación en este momento es ht.

3) Lista

Comandos de uso común: lpush/rpush/lpop/rpop/lrange, etc.

Escenarios de aplicación: la lista de Redis tiene muchos escenarios de aplicación y también es una de las estructuras de datos más importantes de Redis. Por ejemplo, la lista de seguidores y la lista de fans de Twitter se pueden implementar a través de la estructura de la lista de Redis;

p>

Implementación: la implementación de la lista Redis es una lista enlazada bidireccional, que puede admitir búsqueda inversa y recorrido. Es más conveniente de operar, pero genera cierta sobrecarga de memoria adicional. Muchas implementaciones dentro de Redis, incluidas las colas de búfer de envío, también utilizan esta estructura de datos.

4) Configuración

Comandos comunes: sadd/spop/semembers/union, etc.

Escenario de aplicación: las funciones proporcionadas por el conjunto de Redis son similares a las proporcionadas por la lista, pero lo especial es que el conjunto se puede copiar automáticamente. Cuando necesita almacenar una lista de datos y no desea duplicar los datos, set es una buena opción y proporciona una interfaz importante para determinar si un miembro está en la colección, algo que la lista no puede proporcionar.

Implementación: La implementación interna de set es un HashMap y su valor siempre es nulo. De hecho, se trata de organizar rápidamente elementos duplicados mediante el cálculo de hash, razón por la cual set puede proporcionar una forma de determinar si un miembro está en el conjunto.

5) Conjunto ordenado

Comandos comunes: zadd/zrange/zrem/zcard, etc.

Escenarios de aplicación: los escenarios de uso de los conjuntos ordenados de Redis son similares a los conjuntos. La diferencia es que los conjuntos no se ordenan automáticamente, mientras que los conjuntos ordenados pueden ordenar miembros proporcionando una puntuación de parámetro adicional y se insertan. orden, es decir, clasificación automática. Cuando necesite una lista de conjuntos ordenada y no duplicada, puede elegir la estructura de datos del conjunto ordenado. Por ejemplo, la línea de tiempo pública de Twitter se puede almacenar según la hora de publicación, de modo que se pueda ordenar automáticamente por hora cuando se recupere.

Método de implementación: la clasificación de Redis utiliza HashMap y SkipList para garantizar el almacenamiento y ordenamiento de los datos. HashMap es una asignación de miembros a puntuaciones, mientras que la lista de omisión almacena todos los miembros y la clasificación se basa en las puntuaciones almacenadas en HashMap. El uso de la estructura de lista de omisión puede lograr una mayor eficiencia de búsqueda y la implementación es relativamente simple.

2. El mecanismo de gestión de la memoria es diferente.

En Redis, no todos los datos se almacenan en la memoria. Ésta es la mayor diferencia en comparación con Memcached. Cuando se agota la memoria física, Redis puede intercambiar en el disco algunos valores que no se han utilizado durante mucho tiempo. Redis solo almacenará en caché la información de todas las claves. Si Redis descubre que el uso de memoria excede un cierto umbral, se activará una operación de intercambio. Redis calculará qué valores correspondientes a las claves deben intercambiarse en el disco en función de "Intercambiabilidad = edad * registro (tamaño _ en _ memoria)". Los valores correspondientes a estas claves se guardan en el disco y se borran de la memoria. Esta característica permite a Redis guardar más datos que el tamaño de la memoria de su propia máquina. Por supuesto, la memoria de la propia máquina debe poder contener todas las claves. Al fin y al cabo, estos datos no se intercambian. Al mismo tiempo, cuando Redis intercambia los datos de la memoria al disco, el subproceso principal que proporciona el servicio y el subproceso que realiza la operación de intercambio * * * compartirán esta parte de la memoria, por lo que si los datos que necesita ser intercambiado está actualizado, Redis bloqueará esta operación hasta que el subproceso secundario complete la operación de intercambio. Al leer datos de Redis, si el valor correspondiente a la clave de lectura no está en la memoria, Redis necesita cargar los datos correspondientes del archivo de intercambio y luego devolverlos al solicitante. Aquí hay un problema con el grupo de subprocesos de E/S. De forma predeterminada, Redis bloquea, es decir, no responde hasta que se hayan cargado todos los archivos de intercambio. Esta estrategia es más adecuada para situaciones en las que el número de clientes es pequeño y se realizan operaciones por lotes. Sin embargo, si Redis se aplica a aplicaciones de sitios web a gran escala, obviamente no podrá afrontar situaciones de gran concurrencia.

Entonces Redis nos ejecuta para establecer el tamaño del grupo de subprocesos de E/S y realizar operaciones simultáneas en solicitudes de lectura que necesitan cargar los datos correspondientes del archivo de intercambio, reduciendo así el tiempo de bloqueo.

Para los sistemas de bases de datos basados ​​en memoria, como Redis y Memcached, la eficiencia de la gestión de la memoria es un factor clave que afecta el rendimiento del sistema. La función Malloc/free en el lenguaje C tradicional es el método más utilizado para asignar y liberar memoria, pero este método tiene fallas importantes: primero, la falta de coincidencia entre Malloc y free puede causar fácilmente pérdidas de memoria a los desarrolladores; segundo, las llamadas frecuentes pueden causar pérdidas; provoca pérdidas de memoria. Esto da como resultado una gran cantidad de fragmentos de memoria que no se pueden reciclar ni reutilizar, lo que reduce la utilización de la memoria y, finalmente, como llamada al sistema, su sobrecarga del sistema es mucho mayor que la de una llamada de función general. Por lo tanto, para mejorar la eficiencia de la administración de la memoria, las soluciones de administración de memoria eficientes no utilizarán directamente malloc/llamadas gratuitas. Tanto Redis como Memcached utilizan su propio mecanismo de gestión de memoria, pero existen grandes diferencias en sus métodos de implementación. Sus mecanismos de gestión de memoria se presentarán por separado a continuación.

Memcached utiliza el mecanismo de asignación Slab de forma predeterminada para administrar la memoria. La idea principal es dividir la memoria asignada en bloques de una longitud específica de acuerdo con el tamaño preespecificado para almacenar registros de datos clave-valor. longitud correspondiente, resolviendo así completamente el problema de los desechos de memoria. El mecanismo de asignación de Slab solo está diseñado para almacenar datos externos, lo que significa que todos los datos clave-valor se almacenan en el sistema de asignación de Slab, y otras solicitudes de memoria de Memcached se solicitan a través de malloc/free ordinario, porque estas solicitudes El número y la frecuencia determine que no afectarán el rendimiento de todo el sistema. El principio de distribución de placas es simple. Como se muestra en la figura, primero solicita un gran bloque de memoria del sistema operativo y lo divide en bloques de varios tamaños, y luego divide los bloques del mismo tamaño en grupos de clases Slab. Entre ellos, Chunk es la unidad más pequeña utilizada para almacenar datos clave-valor. El tamaño de cada clase Slab se puede controlar estableciendo un factor de crecimiento cuando se inicia Memcached. Suponiendo que el valor del factor de crecimiento en la figura es 1,25, si el tamaño del primer fragmento es de 88 bytes, el tamaño del segundo fragmento es de 112 bytes, y así sucesivamente.

Cuando Memcached recibe datos del cliente, primero seleccionará la clase Slab más apropiada según el tamaño de los datos recibidos y luego consultará la lista Uchikoga Chunks de la clase Slab guardada por Memcached para encontrar el Clase de losa que se puede utilizar Chunk para almacenar datos. Cuando la base de datos caduca o se descarta, los bloques ocupados por los registros se pueden recuperar y agregar a la lista libre nuevamente. Como se puede ver en el proceso anterior, el sistema de administración de memoria de Memcached es eficiente y no causará fragmentación de la memoria, pero su mayor desventaja es que genera una pérdida de espacio. Debido a que a cada bloque se le asigna una longitud específica de espacio de memoria, los datos de longitud variable no pueden utilizar completamente este espacio. Como se muestra en la figura, 100 bytes de datos se almacenan en caché en fragmentos de 128 bytes y los 28 bytes restantes se desperdician.

La administración de memoria de Redis se implementa principalmente a través de los dos archivos zmalloc.h y zmalloc.c en el código fuente. Para facilitar la administración de la memoria, después de que Redis asigne una parte de la memoria, el tamaño será. almacenado en el encabezado de un bloque de memoria. Como se muestra en la figura, real_ptr es el puntero devuelto por redis después de llamar a malloc. Redis almacena el tamaño del bloque de memoria en el encabezado. Se conoce el tamaño de la memoria ocupada por el tamaño y es la longitud del tipo size_t, y luego devuelve ret_ptr. Cuando es necesario liberar memoria, ret_ptr se pasa al administrador de memoria. A través de ret_ptr, el programa puede calcular fácilmente el valor de real_ptr y luego pasar real_ptr para liberar la memoria.

Redis registra todas las asignaciones de memoria definiendo una matriz con una longitud de ZMALLOC_MAX_ALLOC_STAT. Cada elemento de la matriz representa el número de bloques de memoria asignados por el programa actual y el tamaño del bloque de memoria es el subíndice del elemento. En el código fuente, esta matriz es zmalloc_allocations. Zmalloc_allocations[16] representa el número de bloques de memoria asignados con una longitud de 16 bytes.

Hay una variable estática used_memory en zmalloc.c, que se utiliza para registrar el tamaño total de memoria asignada. Entonces, en general, Redis usa mallc/free empaquetado, que es mucho más simple que el método de administración de memoria de Memcached.

3. Soporte de persistencia de datos

Aunque Redis es un sistema de almacenamiento basado en memoria, admite la persistencia de datos de memoria y proporciona dos estrategias de persistencia principales: instantáneas RDB y registros AOF. Memcached no admite la persistencia de datos.

1) Instantánea RDB

Redis admite un mecanismo de persistencia que guarda una instantánea de los datos actuales como un archivo de datos, es decir, una instantánea RDB. Pero, ¿cómo generar instantáneas para una base de datos que escribe constantemente? Redis utiliza el mecanismo de copia en escritura del comando fork. Al generar una instantánea, separe el proceso actual del subproceso, luego recorra todos los datos en el subproceso y escriba los datos en el archivo RDB. Podemos configurar el tiempo de generación de instantáneas RDB a través del comando guardar de Redis, como configurar 10 minutos para generar una instantánea, configurar 1000 escrituras para generar una instantánea o implementar múltiples reglas juntas. Estas reglas se definen en el archivo de configuración de Redis. También puede establecer reglas mientras Redis se ejecuta mediante el comando Redis CONFIG SET sin reiniciar Redis.

El archivo Redis RDB no se dañará porque sus operaciones de escritura se realizan en un nuevo proceso. Cuando se genera un nuevo archivo RDB, el proceso hijo generado por Redis primero escribirá los datos en un archivo temporal y luego cambiará el nombre del archivo temporal a un archivo RDB mediante la llamada al sistema de cambio de nombre atómico, de modo que cuando ocurra una falla, Redis Los archivos RDB siempre están disponibles. Al mismo tiempo, el archivo RDB de Redis también forma parte de la sincronización maestro-esclavo dentro de Redis. RDB tiene sus desventajas, es decir, una vez que hay un problema con la base de datos, los datos almacenados en nuestros archivos RDB ya no son nuevos y todos los datos de los archivos RDB de la generación anterior hasta el tiempo de inactividad de Redis se perderán. En algunas industrias esto se tolera.

2) Registro AOF

El nombre completo del registro AOF es un archivo adjunto, que es un archivo de registro adicional. A diferencia del binlog de una base de datos general, el archivo AOF es un texto sin formato reconocible y su contenido son comandos estándar de Redis uno tras otro. Sólo aquellos comandos que resulten en la modificación de datos se agregarán al archivo AOF. Cada comando que modifica datos generará un registro y el archivo AOF se hará cada vez más grande, por lo que Redis proporciona otra función llamada reescritura AOF. Su función es regenerar un archivo AOF. Los registros en el nuevo archivo AOF se operarán solo una vez, a diferencia del archivo anterior, que puede registrar múltiples operaciones sobre el mismo valor. Su proceso de generación es similar al RDB y también es un proceso de bifurcación. Atraviesa directamente los datos y escribe un nuevo archivo temporal AOF. Durante el proceso de escritura de un archivo nuevo, todos los registros de operaciones de escritura aún se escribirán en el archivo AOF antiguo y se registrarán en el búfer de memoria. Cuando se complete la operación de reproducción, todos los registros en el búfer se escribirán en el archivo temporal a la vez. Luego llame al comando de cambio de nombre atómico para reemplazar el archivo AOF antiguo con el archivo AOF nuevo.

AOF es una operación de escritura de archivos. El propósito es escribir el registro de operaciones en el disco, por lo que también encontraremos el proceso de operación de escritura que mencionamos anteriormente. Después de llamar a escribir en AOF en Redis, la opción appendfsync controla el tiempo que lleva llamar a fsync para escribirlo en el disco. Las siguientes tres configuraciones de appendfsync son cada vez más fuertes.

Appendfsync no Cuando Appendfsync se establece en no, Redis no llamará activamente a fsync para sincronizar el contenido del registro AOF con el disco, por lo que todo esto depende completamente de la depuración del sistema operativo. Para la mayoría de los sistemas operativos Linux, fsync se ejecuta cada 30 segundos para escribir los datos del búfer en el disco.

Appendfsync eachsec Cuando Appendfsync está configurado en eachsec, Redis realizará una llamada fsync de forma predeterminada cada segundo para escribir los datos del búfer en el disco. Sin embargo, cuando esta llamada fsync tarda más de 1 segundo.

Redis adoptará la estrategia de retrasar fsync y esperará un segundo más. En otras palabras, fsync se ejecutará después de dos segundos, y esta vez fsync se ejecutará sin importar cuánto tiempo tarde. En este punto, debido a que el descriptor de archivo se bloqueará durante fsync, se bloqueará la operación de escritura actual. Entonces, la conclusión es que en la mayoría de los casos Redis sincronizará cada segundo. En el peor de los casos, la operación fsync se realizará cada dos segundos. Esta operación se denomina confirmación de grupo en la mayoría de los sistemas de bases de datos, que consiste en combinar los datos de múltiples operaciones de escritura y escribir el registro en el disco al mismo tiempo.

Appednfsync siempre Cuando appendfsync está configurado en siempre, se llamará a fsync una vez para cada operación de escritura y los datos estarán más seguros en este momento. Por supuesto, su rendimiento también se verá afectado porque debe ejecutarse cada vez.

Para las necesidades comerciales generales, se recomienda utilizar RDB para la persistencia, porque la sobrecarga de RDB es mucho menor que la de los registros AOF. Para aplicaciones que no pueden tolerar la pérdida de datos, se recomienda utilizar el registro AOF.

4. Gestión diferente de clusters.

Memcached es un sistema de almacenamiento en búfer de datos con memoria completa. Aunque Redis admite la persistencia de datos, la memoria llena es la esencia de su alto rendimiento. Como sistema de almacenamiento basado en memoria, el tamaño de la memoria física de la máquina es la cantidad máxima de datos que el sistema puede contener. Si la cantidad de datos a procesar excede el tamaño de la memoria física de una sola máquina, es necesario construir un clúster distribuido para ampliar la capacidad de almacenamiento.

Memcached en sí no admite la distribución, por lo que el almacenamiento distribuido de Memcached solo se puede implementar en el cliente a través de algoritmos distribuidos como el hash consistente. La siguiente figura muestra la arquitectura de implementación de almacenamiento distribuido de Memcached. Antes de enviar datos al clúster de Memcached, el cliente primero calculará el nodo de destino de los datos a través del algoritmo distribuido incorporado y luego los datos se enviarán directamente al nodo para su almacenamiento. Sin embargo, cuando el cliente consulta datos, también necesita calcular el nodo donde se encuentran los datos de la consulta y luego enviar directamente una solicitud de consulta al nodo para obtener los datos.

En comparación con Memcached, que solo puede implementar almacenamiento distribuido en el lado del cliente, Redis prefiere construir almacenamiento distribuido en el lado del servidor. La última versión de Redis ya admite funciones de almacenamiento distribuido. El clúster de Redis es una versión avanzada de Redis que implementa la distribución y permite puntos únicos de falla. No tiene nodo central y es linealmente escalable. La siguiente figura muestra la arquitectura de almacenamiento distribuido de Rediscoluster. Los nodos se comunican mediante protocolos binarios y los nodos y los clientes se comunican mediante protocolos ascii. En términos de estrategia de ubicación de datos, Redis Cluster divide todo el campo de valor clave en 4096 ranuras hash. Cada nodo puede almacenar una o más ranuras hash, lo que significa que la cantidad máxima de nodos admitidos por Redis Cluster es 4096. El algoritmo distribuido utilizado por el clúster de Redis también es muy simple: CRC 16 (clave)% hash _ ranuras _ número.

Para garantizar la disponibilidad de datos bajo un único punto de falla, el clúster de Redis introduce nodos maestros y nodos esclavos. En un clúster de Redis, cada nodo maestro tendrá dos nodos esclavos correspondientes para redundancia. De esta manera, el tiempo de inactividad de dos nodos cualesquiera en todo el clúster no provocará falta de disponibilidad de datos. Cuando el nodo maestro sale, el clúster seleccionará automáticamente un nodo esclavo para que se convierta en el nuevo nodo maestro.