¿Cuáles son las ventajas y desventajas de Redis y Memcached, y cuáles son los principales escenarios de aplicación?
Salvatore Sanfilippo, el autor de Redis, comparó una vez estos dos sistemas de almacenamiento de datos basados en memoria:
Redis admite operaciones de datos del lado del servidor: en comparación con Memcached, Redis tiene Más Las estructuras de datos admiten operaciones de datos más ricas. En Memcached, debe llevar los datos al cliente para realizar cambios similares y luego restablecerlos. Esto aumenta en gran medida la cantidad de E/S de la 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.
Comparación de la eficiencia del uso de la memoria: si usa un almacén clave-valor simple, el uso de memoria de Memcached será mayor, mientras que si Redis usa una estructura hash para almacenar valores clave, su uso de memoria será menor debido a su Capacidades de compresión combinadas. La tasa de uso será mayor que la de Memcached.
Comparación de rendimiento: dado que Redis solo usa un núcleo, mientras que Memcached puede usar múltiples núcleos, al almacenar datos pequeños, el rendimiento promedio de Redis por núcleo es mayor que el de Memcached. Para datos de más de 100k, Memcached funciona mejor que Redis. Aunque Redis se ha optimizado recientemente para almacenar grandes datos, su rendimiento sigue siendo ligeramente peor que el de Memcached.
Específicamente por qué se llegó a la conclusión anterior, la información recopilada es la siguiente:
1. Se admiten diferentes tipos de datos
Memcached solo admite clave-valor simple. Estructuras con diferentes registros de datos, Redis admite tipos de datos mucho más ricos. Hay cinco tipos de datos más utilizados: cadena, hash, lista, conjunto y conjunto ordenado. Redis utiliza internamente objetos redisObject para representar todas las claves y valores. La información más importante de RedisObject se muestra en la figura:
el tipo representa el objeto de valor. La codificación es la forma en que Redis almacena diferentes tipos de datos internamente. Por ejemplo: tipo = cadena significa que el valor se almacena en forma de una cadena normal, entonces la codificación correspondiente puede ser sin formato o int. Si es int, significa redis. en realidad almacena y representa esta cadena: una cadena como "123″"456". El campo vm asignará memoria solo si la función de memoria virtual de Redis está activada (desactivada de forma predeterminada).
1) Cadena
Comandos comunes: set/get/decr/incr/mget, etc.;
Escenarios de aplicación: la cadena es el dato más utilizado tipo, el almacenamiento de clave/valor ordinario se puede clasificar en esta categoría;
Implementación: las cadenas se almacenan como cadenas en redis. Se almacena como una cadena de forma predeterminada y redisObject hace referencia a ella. Cuando encuentre incr, decr y otras operaciones, se convertirá a un tipo numérico para el cálculo. En este momento, el campo de codificación de redisObject es int.
2) Hashing
Comandos comunes: hget/hset/hgetall, etc.
Escenario de aplicación: queremos almacenar los datos de un objeto de información de usuario, incluida la identificación de usuario, el nombre de usuario, la edad y la fecha de nacimiento. Queremos obtener el nombre, la edad o la fecha de nacimiento del usuario a través de la identificación de usuario;
Implementación: el Hash de Redis en realidad se almacena internamente como el valor del HashMap y proporciona una interfaz para el acceso directo a los miembros del mapa. Como se muestra en la figura, la clave es el ID 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 la clave del Mapa interno (Redis llama a la clave del Mapa interno un campo), es decir, los datos del atributo correspondiente se pueden manipular 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 relativamente pequeño, Redis utilizará un método similar a una matriz unidimensional para almacenarlo de forma compacta para ahorrar memoria, en lugar de utilizar la estructura real de HashMap. al valor de codificación de redisObject: zipmap aumentará automáticamente cuando aumente el número de miembros. Cuando aumente el número de miembros, se convertirá automáticamente a un HashMap real. La codificación en este momento es ht.
3) Lista
Comandos comunes: lpush/rpush/lpop/rpop/lrange, etc.;
Escenarios de aplicación: hay muchos escenarios de aplicación para Redis listas También es una de las estructuras de datos más importantes en Redis, como la lista de vigilancia de Twitter. Por ejemplo, la lista de seguidores, la lista de fans, etc. de Twitter se pueden implementar utilizando la estructura de la lista de Redis;
Implementación: la lista de Redis se implementa como una lista doblemente vinculada, que puede admitir búsqueda inversa y recorrido, lo que hace que La operación es más conveniente, pero trae Para evitar una sobrecarga de memoria adicional, muchas cosas implementadas internamente por Redis, incluidas las colas de búfer de envío, etc., utilizan esta estructura de datos.
4) Establecer
Comandos comunes: sadd/spop/smembers/sunion, etc.
Escenario de aplicación: el conjunto de Redis proporciona funciones similares a la lista, que es una lista de funciones, pero lo especial es que el conjunto se puede reordenar automáticamente. Cuando necesita almacenar una lista de datos y no desea almacenar los datos repetidamente, set es una buena opción y set proporciona una interfaz importante para determinar si un determinado miembro está en el conjunto, lo cual la lista no puede proporcionar;
Método de implementación: Set implementa internamente un HashMap cuyo valor siempre está vacío. De hecho, es para ordenar rápidamente el peso del conjunto calculando el hash. Es por eso que set brinda la capacidad 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 aplicación de los conjuntos ordenados de Redis son similares para configurar, pero diferente La diferencia es que el conjunto no se ordenará automáticamente, y el usuario puede proporcionar al conjunto ordenado parámetros de prioridad (puntuación) adicionales y clasificación por inserción, es decir, clasificación automática. Una estructura de datos de conjuntos ordenados es el camino a seguir cuando necesita una lista de conjuntos ordenados que no se repiten. Por ejemplo, la línea de tiempo pública de Twitter puede almacenar el tiempo de publicación como una puntuación, de modo que cuando la obtenga, será automáticamente. ordenado por tiempo.
Método de implementación: el conjunto ordenado de Redis utiliza HashMap y una tabla de salto (SkipList) internamente para garantizar que el almacenamiento de datos ordenado se coloque en el mapeo de los miembros a las puntuaciones, y la tabla de salto se almacene en todos los miembros. En cuanto a la clasificación, HashMap utiliza la estructura de la tabla de salto para obtener una eficiencia de búsqueda relativamente alta al almacenar puntuaciones.
2. Diferentes mecanismos de gestión de memoria
En Redis, no todos los datos se almacenan en la memoria. Ésta es una de las mayores diferencias en comparación con Memcached. Cuando la memoria física se agota, Redis puede intercambiar valores que no se han utilizado durante mucho tiempo en el disco.
Redis solo almacena en caché toda la información clave. Si Redis descubre que el uso de la memoria excede un cierto umbral, activará una operación de intercambio. Redis intercambiará valores de acuerdo con "swappability = age*log (size_in_memory)", y Redis calculará qué valores correspondientes a las claves deben intercambiarse en el disco. Luego, los valores correspondientes a estas claves se conservan en el disco y los valores en la memoria se borran. Esta característica permite a Redis guardar más datos que la propia memoria de la máquina. Por supuesto, la propia memoria de la máquina debe poder acomodar todos los valores clave; después de todo, estos datos no se ven afectados por la operación de intercambio. Al mismo tiempo, cuando Redis intercambia datos en la memoria al disco, el subproceso principal que proporciona servicios y el subproceso que realiza la operación de intercambio usarán esta parte de la memoria, por lo que si actualiza los datos que necesita Para ser intercambiado, Redis bloqueará esta operación única y no podrá realizar modificaciones 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 y espera hasta que se hayan cargado todos los archivos de intercambio. Esta estrategia es más adecuada para operaciones por lotes cuando la cantidad de clientes es pequeña. Pero si Redis se usa en aplicaciones de red a gran escala, esto obviamente no puede resolver la situación de gran concurrencia. Por lo tanto, cuando Redis se está ejecutando, podemos configurar el tamaño del grupo de subprocesos de E/S para 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 desventajas obvias: en primer lugar, la falta de coincidencia entre malloc y free del desarrollador puede provocar fácilmente pérdidas de memoria y, en segundo lugar, las llamadas frecuentes pueden provocar; como resultado, una gran cantidad de fragmentos de memoria no se pueden reciclar ni reutilizar, lo que finalmente reduce la utilización de la memoria, como llamada al sistema, su sobrecarga del sistema es mucho mayor que la de las llamadas a funciones ordinarias. Por lo tanto, para mejorar la eficiencia de la administración de la memoria, los administradores de memoria eficientes no utilizan directamente llamadas malloc/free. Tanto Redis como Memcached utilizan mecanismos de administración de memoria diseñados por ellos mismos, pero existen diferencias significativas en los métodos de implementación. Los dos mecanismos de administración de memoria se presentarán a continuación.
Memcached utiliza el mecanismo Slab Allocation de forma predeterminada para administrar la memoria. La idea principal es dividir la memoria asignada en bloques de longitudes específicas de acuerdo con tamaños predefinidos para almacenar registros de datos clave-valor de las longitudes correspondientes, por lo tanto completamente. Resolver problemas de fragmentación de memoria.
El mecanismo de asignación de losa solo está diseñado para almacenar datos externos, lo que significa que todos los registros de datos clave-valor se almacenan en la memoria. El mecanismo de asignación de Slab solo está diseñado para almacenar datos externos, es decir, todos los registros de datos clave-valor se almacenan en el sistema de asignación de Slab, mientras que otras solicitudes de memoria de Memcached se solicitan a través de malloc/free normal, debido a la cantidad y frecuencia de estas. solicitudes Se determina que no afectarán el rendimiento de todo el sistema. El principio de asignación de losa es bastante simple. Como se muestra en la figura, primero solicita un gran bloque de memoria del sistema operativo, lo divide en bloques de memoria de diferentes tamaños y luego divide los bloques de memoria del mismo tamaño en grupos de clases Slab, uno de los cuales se usa para almacenar claves. -datos de valor.la unidad más pequeña. El tamaño de cada clase de losa se puede controlar estableciendo un factor de crecimiento cuando se inicia Memcached. Suponiendo un valor de factor de crecimiento de 1,25 en el diagrama, si el tamaño del primer conjunto de fragmentos es de 88 bytes, entonces el tamaño del segundo conjunto de fragmentos será 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 buscará la lista de fragmentos gratuitos que se pueden usar para almacenar los datos mediante consultas. la lista de fragmentos gratuitos en la clase Slab guardada por Memcached. Cuando un registro de base de datos caduca o se descarta, la parte ocupada por el registro se reciclará y se volverá a agregar a la lista libre.
Del proceso anterior, podemos ver que el sistema de administración de memoria de Memcached es muy 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 fragmento se le asigna una longitud específica de espacio de memoria, los datos de longitud variable no pueden utilizar completamente este espacio. Por ejemplo, si almacena en caché 100 bytes de datos en un fragmento de 128 bytes, los 28 bytes restantes se desperdiciarán.
La gestión de la memoria de Redis se completa a través de los archivos de código fuente zmalloc.h y zmalloc.c. Redis asigna un bloque de memoria y luego almacena el tamaño del bloque de memoria en el encabezado del bloque de memoria para facilitar la administración de la memoria. Como se muestra en la figura, real_ptr es el puntero que devuelve Redis después de llamar a malloc. Redis guardará el tamaño del bloque de memoria en el encabezado del bloque de memoria. El tamaño de la memoria ocupada por tamaño se denomina longitud del tipo size_t y luego devuelve ret_ptr. Con ret_ptr, el programa puede calcular fácilmente el valor de real_ptr y luego pasar real_ptr a free, liberando así la memoria.
Redis rastrea todas las asignaciones de memoria definiendo una matriz de longitud 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 de 16 bytes de longitud. zmalloc.c tiene una variable estática used_memory que realiza un seguimiento del tamaño total de la memoria asignada actualmente. Entonces, en general, Redis utiliza mallc/free empaquetado, que es mucho más simple en comparación con el enfoque de administración de memoria de Memcached.
3. Compatibilidad con la persistencia de datos
Redis es un sistema de almacenamiento basado en memoria, pero en sí mismo admite la persistencia de datos en memoria y proporciona dos estrategias de persistencia principales: memcached no admite la persistencia de datos. operaciones.
1) Instantánea RDB
Redis admite un mecanismo de persistencia que almacena una instantánea de los datos actuales como un archivo de datos, es decir, una instantánea RDB. Pero, ¿cómo genera instantáneas una base de datos escrita continuamente? Al generar una instantánea, el proceso actual se bifurcará de un proceso secundario y luego todos los datos se incluirán en un bucle en el proceso secundario y los datos se escribirán en el archivo RDB. Podemos configurar el tiempo para la generación de instantáneas RDB a través del comando guardar de Redis, como configurar una instantánea para que se genere cada 10 minutos, o configurar una instantánea para que se genere después de 1000 escrituras, o se pueden ejecutar varias reglas al mismo tiempo. Estas reglas se definen en el archivo de configuración de Redis. También puede usar el comando Redis CONFIG SET para configurar las reglas mientras Redis se está ejecutando. Esto no requiere reiniciar Redis.
Los archivos RDB de Redis no se romperán porque están escritos 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 se le cambiará el nombre. a archivos RDB a través de la llamada al sistema atomicrename para que los archivos Redis RDB estén siempre disponibles cuando ocurra una falla. Al mismo tiempo, los archivos Redis RDB también forman parte de la implementación interna de la sincronización maestro-esclavo de Redis.
RDB también tiene sus desventajas, es decir, una vez que hay un problema con la base de datos, los datos guardados en nuestro archivo RDB no serán nuevos. Se perderán todos los datos desde la última generación del archivo RDB hasta el cierre de Redis. Esto puede tolerarse bajo ciertas condiciones comerciales.
2) Registro AOF
El nombre completo del registro AOF es archivo de solo agregar, lo que significa agregar al archivo de registro. A diferencia de los binlogs de bases de datos normales, los archivos AOF son texto sin formato identificable y su contenido son comandos estándar de Redis uno tras otro. Solo los comandos que modifican datos se agregan 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 una función llamada reescritura AOF. Su función es regenerar un archivo AOF. La operación registrada en el nuevo archivo AOF será solo una vez, a diferencia de un archivo antiguo, que puede registrar múltiples operaciones con el mismo valor. El proceso de generación es similar a RDB. También bifurca un proceso, atraviesa directamente los datos y escribe en un nuevo archivo temporal AOF. Durante el proceso de escritura del nuevo archivo, todos los registros de operaciones de escritura aún se escribirán en el archivo AOF antiguo original y también se registrarán en el búfer de memoria. Una vez completada la operación de cambio de nombre, todos los registros del búfer se escriben 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 nuevo.
AOF es una operación de escritura de archivos cuyo propósito es escribir el registro de operaciones en el disco, por lo que también encontrará el proceso de operación de escritura que describimos anteriormente. Después de llamar a AOF para una operación de escritura en Redis, el tiempo necesario para llamar a fsync para escribirlo en el disco está controlado por la opción appendfsync, y la seguridad aumentará gradualmente con las siguientes tres configuraciones de appendfsync.
appendfsync no Cuando appendfsync se establece en no, Redis no llamará activamente a fsync para sincronizar el contenido del registro AOF en el disco, por lo que 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 y escribe los datos del búfer en el disco.
appendfsync eachsec Cuando appendfsync se establece en eachsec, Redis realiza una llamada fsync de forma predeterminada cada segundo para escribir los datos del búfer en el disco. Pero cuando se llama a fsync durante más de un segundo, Redis adoptará una estrategia para retrasar fsync y esperar otro segundo. Es decir, fsync se ejecutará después de dos segundos y no importa cuánto tiempo tarde, se ejecutará fsync. En este punto, la operación de escritura actual está bloqueada porque el descriptor de archivo está bloqueado durante fsync. Entonces, la conclusión es que en la mayoría de los casos Redis realizará una sincronización cada segundo y, en el peor de los casos, cada dos segundos. Esta operación, conocida como confirmación de grupo en la mayoría de los sistemas de bases de datos, es una combinación de múltiples operaciones de escritura que escriben el registro en el disco a la vez.
appednfsync siempre Cuando appendfsync está configurado en siempre, se llamará a fsync una vez para cada operación de escritura. En este momento, los datos son los más seguros, pero debido a que fsync debe ejecutarse cada vez, su rendimiento será de. por supuesto verse afectado.
Para las necesidades comerciales generales, se recomienda utilizar el método 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 AOF. registros.
4. Gestión de clústeres diferente
Memcached es un sistema de almacenamiento en búfer de datos con memoria completa que admite la persistencia de datos, pero, después de todo, la memoria completa 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 acomodar. Si la cantidad de datos que deben procesarse excede el tamaño de la memoria física de una sola máquina, es necesario establecer un clúster distribuido para ampliar la capacidad de almacenamiento.
Memcached en sí no admite la distribución, por lo que solo puede implementar el almacenamiento distribuido de Memcached en el cliente a través de algoritmos distribuidos como el hash consistente. La siguiente figura muestra la arquitectura de la implementación del almacenamiento distribuido de Memcached. Cuando el cliente envía datos al clúster de Memcached, el nodo de destino de los datos primero se calcula mediante el algoritmo distribuido incorporado y luego los datos se envían directamente al nodo para su almacenamiento. Sin embargo, cuando el cliente consulta datos, también calcula el nodo donde se encuentran los datos de la consulta y luego envía una solicitud de consulta directamente a ese nodo para obtener los datos.
En comparación con Memcached, que solo utiliza almacenamiento distribuido del lado del cliente, Redis prefiere construir almacenamiento distribuido en el lado del servidor. La última versión de Redis ya admite almacenamiento distribuido y Redis Cluster es una versión avanzada de Redis que se distribuye, permite un único punto de falla, no tiene un nodo central y puede escalar linealmente. La siguiente figura muestra la arquitectura de almacenamiento distribuido de Redis Cluster. Los nodos se comunican entre sí mediante protocolos binarios y los nodos se comunican con los clientes mediante protocolos ascii. En términos de estrategia de ubicación de datos, Redis Cluster divide el campo numérico de la clave completa 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 actualmente por Redis Cluster es 4096. El algoritmo distribuido utilizado por Redis Cluster también es muy simple: crc16 (clave)% HASH_SLOTS_NUMBER.
Para garantizar la disponibilidad de datos bajo un único punto de falla, Redis Cluster presenta un nodo maestro y un nodo esclavo. En un clúster de Redis, cada nodo maestro tiene 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.
Publicado el 09-02-2017
241 comentarios
Compartir
Recoger agradecimientos y cerrar
Zhang Guangyu
Ninguno
Diez personas estuvieron de acuerdo con esta respuesta
En primer lugar, las características de Redis
Redis tiene estructuras de datos ricas. Se adapta al negocio y es muy conveniente de usar (como Timeline, JobQueue, etc.).
Redis admite la persistencia de datos. Aunque no es tan perfecto como una base de datos, es completamente suficiente para escenarios de aplicaciones como Internet.
Características de Memcached
Almacenamiento en caché puro, lo que significa que generalmente solo EXPIRA el caché y no modifica (ni AGREGA) el caché.
Cuando se utiliza el caché como comparación de rendimiento
Ambos están cuidadosamente diseñados para mantener un rendimiento de 100.000 veces para 0-300 clientes en el caso de GET/SET/segundo simultáneo o más.
El rendimiento de Memcached es mucho mejor (varias veces) que el de Redis, que es más fácil de entender. Pero los cuellos de botella suelen producirse en lugares como el cliente o la red.
Aquí está la línea base