Redis ralentizado? Entonces prueba esto, si no funciona, solo golpéame

En algunos sistemas de servicios de red, el rendimiento de Redis puede ser un tema más importante que el rendimiento de las bases de datos del disco duro como MySQL. Por ejemplo, Weibo, el popular Weibo [1] y las últimas relaciones con los usuarios se almacenan en Redis, y una gran cantidad de consultas llegan a Redis en lugar de MySQL.

Entonces, para el servicio de Redis, ¿qué optimización del rendimiento podemos hacer? En otras palabras, ¿qué desperdicio de desempeño se debe evitar?

Los fundamentos del rendimiento de Redis

Antes de discutir la optimización, debemos saber que el servicio Redis en sí tiene algunas características, como la operación de un solo subproceso. A menos que se modifique el código fuente de Redis, estas características son fundamentales para que pensemos en la optimización del rendimiento.

Entonces, ¿cuáles son las características básicas de Redis que debemos considerar? La introducción del proyecto de Redis resume sus características:

Redis es una base de datos en memoria que persiste en el disco. El modelo de datos es clave-valor, pero se admiten muchos tipos diferentes de valores.

Primero, Redis usa la memoria virtual proporcionada por el sistema operativo para almacenar datos. Además, este sistema operativo generalmente se refiere a Unix. Redis también se puede ejecutar en Windows, pero requiere un manejo especial. Si su sistema operativo utiliza espacio de intercambio, es posible que los datos de Redis se almacenen en el disco duro.

En segundo lugar, Redis admite la persistencia y puede guardar datos en el disco duro. En muchos casos, es realmente necesario que realicemos la persistencia para lograr la copia de seguridad, la recuperación de datos y otros requisitos. Pero la persistencia no ocurre en el vacío, también consume algunos recursos.

En tercer lugar, Redis utiliza el método de valor clave para leer y escribir, y el valor puede contener muchos tipos diferentes de datos; además, la capa inferior de un tipo de datos se almacena en diferentes estructuras. Las diferentes estructuras de almacenamiento determinan la complejidad y la sobrecarga de rendimiento de la adición, eliminación, modificación y consulta de datos.

Finalmente, lo que no se menciona en la descripción anterior es, la mayoría de las veces, el hilo único de Redis [2] a (un solo hilo), es decir, al mismo tiempo ocupa solo la CPU, solo una instrucción de operación, la lectura y escritura en paralelo No existe. La respuesta al retraso causado por muchas operaciones se puede encontrar aquí.

En cuanto a la última característica, por qué Redis es de un solo subproceso, pero tiene un buen rendimiento (de acuerdo con la Ley de Amdahl, tiene más sentido optimizar el proceso que consume mucho tiempo). Las dos oraciones se resumen a continuación: Redis usa múltiples canales Mecanismo de multiplexación de E / S [3] , al procesar las solicitudes del cliente, no bloquea el hilo principal; Redis simplemente ejecuta (la mayoría de las instrucciones) una instrucción en menos de  1 microsegundo [4] , de modo que una CPU de un solo núcleo puede tardar un segundo Al procesar 1 millón de instrucciones (correspondientes a cientos de miles de solicitudes), no es necesario implementar subprocesos múltiples (la red es el cuello de botella [5] ).

Optimizar la latencia de la red

El blog oficial de Redis decía en varios lugares que es más probable que el cuello de botella en el rendimiento sea la red [6] , entonces, ¿cómo optimizamos el retraso en la red?

En primer lugar, si usa una implementación independiente (el servicio de aplicación y Redis están en la misma máquina), usar la comunicación entre procesos de Unix para solicitar el servicio de Redis es más rápido que la LAN localhost (bucle de retorno de nombre científico). El documento oficial [7] lo dice, piénsalo, debería ser así en teoría.

Sin embargo, la escala comercial de muchas empresas no puede ser compatible con una implementación independiente, por lo que aún debe utilizarse TCP.

La comunicación entre el cliente y el servidor de Redis generalmente utiliza enlaces largos TCP. Si el cliente necesita esperar a que Redis devuelva el resultado y luego envíe la siguiente instrucción después de enviar la solicitud, varias solicitudes del cliente y Redis constituyen la siguiente relación:

Redis ralentizado?  Entonces prueba esto, si no funciona, solo golpéame

 

(Nota: si no es que la clave que desea enviar es extremadamente larga, un paquete TCP puede contener completamente el comando Redis, por lo que solo se extrae un paquete push)

En estas dos solicitudes, el cliente debe experimentar un período de tiempo de transmisión de la red.

Pero si es posible, puede utilizar comandos de varias teclas para fusionar solicitudes. Por ejemplo, dos teclas GET se pueden fusionar con MGET key1 y key2. De esta manera, en la comunicación real, también se reduce el número de solicitudes y, naturalmente, se mejora el retraso.

Si no puede utilizar el comando de varias teclas para fusionar, como un SET, un GET no se puede fusionar. ¿Cómo hacer?

Hay al menos dos métodos en Redis que pueden combinar varias instrucciones en una solicitud, uno es MULTI / EXEC y el otro es script. El primero era originalmente un método para construir transacciones de Redis, pero de hecho es posible fusionar varias instrucciones en una sola solicitud. El proceso de comunicación es el siguiente. En cuanto a la secuencia de comandos, es mejor utilizar la clave hash sha1 de la secuencia de comandos en caché para llamar la secuencia de comandos, de modo que el volumen de comunicación sea menor.

Redis ralentizado?  Entonces prueba esto, si no funciona, solo golpéame

 

Esto realmente puede reducir el tiempo de transmisión de la red, ¿verdad? Pero en este caso, se debe requerir que las claves involucradas en esta transacción / script estén en el mismo nodo, así que considérelo apropiado.

Si hemos considerado los métodos anteriores y todavía no hay forma de fusionar varias solicitudes, también podemos considerar fusionar varias respuestas. Por ejemplo, para fusionar 2 mensajes de respuesta:

Redis ralentizado?  Entonces prueba esto, si no funciona, solo golpéame

 

De esta manera, teóricamente, se puede ahorrar el tiempo de transmisión de la red para una respuesta. Esto es lo que hace la tubería. Dé un ejemplo de cliente ruby ​​usando pipeline:

require 'redis'
@redis = Redis.new()
@redis.pipelined do
    @redis.get 'key1'
    @redis.set 'key2' 'some value'
end# => [1, 2]

Se dice que algunos clientes de idiomas incluso usan pipeline de forma predeterminada para optimizar el problema de retraso, como node_redis.

Además, no se puede colocar cualquier cantidad de mensajes de respuesta en un paquete TCP. Si hay demasiadas solicitudes y los datos de respuesta son muy largos (por ejemplo, obtener una cadena larga), TCP se seguirá transmitiendo en paquetes, pero utilizando la canalización, aún es posible Reducir el número de transmisiones.

La canalización es diferente de los otros métodos anteriores en que no es atómica. Entonces, en un clúster en estado de clúster, es más probable que implemente tuberías que esos métodos atómicos.

Para resumir:

  1. Utilice la comunicación entre procesos de Unix, si es una implementación independiente
  2. Utilice el comando de varias teclas para combinar varios comandos para reducir el número de solicitudes, si es posible
  3. Utilice transacciones, secuencias de comandos para fusionar solicitudes y respuestas
  4. Usar canalización para fusionar la respuesta

Tenga cuidado con las operaciones de larga duración

En el caso de una gran cantidad de datos, el tiempo de ejecución de algunas operaciones será relativamente largo, como KEYS *, LRANGE mylist 0 -1, y otras instrucciones con una complejidad de algoritmo de O (n). Debido a que Redis solo usa un hilo para la consulta de datos, si estas instrucciones toman mucho tiempo, bloqueará Redis y causará mucho retraso.

Aunque los documentos oficiales dicen que la consulta de KEYS * es muy rápida, (en un portátil normal) solo se necesitan 40 milisegundos para escanear 1 millón de claves (ver: https://redis.io/commands/keys), pero decenas de milisegundos Para un sistema con requisitos de alto rendimiento, no es corto, sin mencionar si hay cientos de millones de claves (una máquina puede almacenar cientos de millones de claves, por ejemplo, una clave de 100 bytes, 100 millones de claves solo 10GB) , Mas tiempo.

Por lo tanto, trate de no utilizar estas instrucciones de ejecución lenta en el código del entorno de producción, como también mencionó el autor de Redis en el blog [8] . Además, los estudiantes de operación y mantenimiento deben tratar de no usarlo al consultar Redis. Incluso, el libro de Redis Essential recomienda usar KEYS de comando de cambio de nombre para prohibir el uso de este comando que consume mucho tiempo.

Además de estas instrucciones, transacciones y secuencias de comandos que requieren mucho tiempo en Redis, debido a que se pueden combinar varios comandos en un proceso de ejecución atómico, Redis también puede llevar mucho tiempo, lo que requiere atención.

Si desea averiguar las "instrucciones lentas" que se utilizan en el entorno de producción, puede utilizar SLOWLOG GET count para ver el recuento más reciente de instrucciones con un tiempo de ejecución largo. En cuanto a cuánto tiempo es, se puede definir configurando slowlog-log-slower-than in redis.conf.

Además, un posible comando lento que no se menciona en muchos lugares es DEL, pero se menciona en el comentario [9] del archivo redis.conf . Para abreviar, cuando DEL es un objeto de gran tamaño, puede llevar mucho tiempo (o incluso algunos segundos) recuperar la memoria correspondiente, por lo que se recomienda utilizar la versión asincrónica de DEL: UNLINK. Este último iniciará un nuevo hilo para eliminar la clave de destino sin bloquear el hilo original.

Además, cuando una clave caduca, Redis generalmente necesita eliminarla sincrónicamente. Una forma de eliminar claves es verificar las claves con un tiempo de vencimiento establecido 10 veces por segundo. Estas claves se almacenan en una estructura global y se puede acceder a ellas con server.db-> expires. La forma de comprobarlo es:

  1. Saca 20 llaves al azar
  2. Elimina los caducados.
  3. Si más del 25% de solo 20 claves (es decir, más de 5) están vencidas, Redis cree que hay bastantes claves vencidas, así que continúe repitiendo el paso 1 hasta que se cumpla la condición de salida: algunas de las claves eliminadas No hay tantas claves en el pasado.

El impacto en el rendimiento aquí es que si muchas claves realmente caducan al mismo tiempo, entonces Redis realmente las eliminará en un bucle, ocupando el hilo principal.

En este sentido , la sugerencia del autor de Redis [10] es tener cuidado con el comando EXPIREAT, porque es más probable que las claves caduquen al mismo tiempo. También he visto algunas sugerencias para establecer una cantidad de fluctuación aleatoria para el tiempo de vencimiento de las claves. Finalmente, redis.conf también proporciona un método para cambiar la operación de eliminación de caducidad de clave a asíncrona, es decir, establecer lazyfree-lazy-expire yes en redis.conf.

Optimice la estructura de datos y utilice el algoritmo correcto

La eficiencia de agregar, eliminar, modificar y verificar un tipo de datos (como una cadena, una lista) está determinada por su estructura de almacenamiento subyacente.

Cuando usamos un tipo de datos, podemos prestar atención a su estructura de almacenamiento subyacente y su algoritmo, y evitar el uso de métodos que son demasiado complejos. Dé dos ejemplos:

  1. La complejidad de tiempo de ZADD es O (log (N)), que es más complicado que agregar un nuevo elemento a otros tipos de datos, así que utilícelo con cuidado.
  2. Si la cantidad de campos del valor del tipo Hash es limitada, es probable que se use la estructura ziplist para el almacenamiento, y la eficiencia de consulta de la ziplist puede no ser tan eficiente como la tabla hash con la misma cantidad de campos. Si es necesario, puede ajustar la estructura de almacenamiento de Redis.

Además de las consideraciones de rendimiento del tiempo, a veces también necesitamos ahorrar espacio de almacenamiento. Por ejemplo, la estructura ziplist mencionada anteriormente ahorra espacio de almacenamiento que la estructura hashtable (el autor de Redis Essentials inserta 500 campos en el hash de la estructura hashtable y ziplist respectivamente, y cada campo y valor es una cadena de aproximadamente 15 dígitos. Como resultado, El espacio utilizado por la estructura de la tabla hash es 4 veces mayor que el de la lista zip). Pero para estructuras de datos que ahorran espacio, la complejidad del algoritmo puede ser muy alta. Por lo tanto, debemos hacer concesiones frente a problemas específicos. Bienvenido a seguir la cuenta oficial: blog de Zhu Xiaosi, respuesta: 1024, puede obtener información exclusiva de redis.

¿Cómo hacer mejores compensaciones? Creo que tengo que profundizar en la estructura de almacenamiento de Redis para sentirme a gusto. Hablaremos de este contenido la próxima vez.

Los tres puntos anteriores son consideraciones a nivel de programación, y debe prestarles atención al escribir programas. Los siguientes puntos también afectarán el rendimiento de Redis, pero para solucionarlos, no es solo el ajuste del nivel de código, sino también la consideración de arquitectura y operación y mantenimiento.

Considere si el sistema operativo y el hardware afectan el rendimiento

El entorno externo en el que se ejecuta Redis, es decir, el sistema operativo y el hardware, obviamente también afectará el rendimiento de Redis. En el documento oficial se dan algunos ejemplos:

  1. CPU: las diversas CPU de Intel son mejores que las de la serie AMD Opteron
  2. Virtualización: las máquinas físicas son mejores que las máquinas virtuales, principalmente porque en algunas máquinas virtuales, el disco duro no es un disco duro local y el software de monitoreo hace que la instrucción de la bifurcación sea lenta (la bifurcación se usa para la persistencia), especialmente cuando se usa Xen para la virtualización. .
  3. Gestión de memoria: en el sistema operativo Linux, para permitir que el búfer de búsqueda de traducción, o TLB, administre más espacio de memoria (TLB solo puede almacenar en caché un número limitado de páginas), el sistema operativo aumenta el tamaño de algunas páginas de memoria, como 2 MB o 1 GB, En lugar de los 4096 bytes habituales, estas páginas de gran memoria se denominan páginas enormes. Al mismo tiempo, para facilitar a los programadores el uso de estas grandes páginas de memoria, se ha implementado un mecanismo de páginas grandes transparentes (THP) en el sistema operativo para hacer que las páginas de memoria grandes sean transparentes para ellos y puedan utilizarse como páginas de memoria normales. Pero este mecanismo no es requerido por la base de datos. Puede ser porque THP hará que el espacio de memoria sea compacto y continuo. Como el documento mongodb [11] indica claramente, la base de datos necesita un espacio de memoria escaso, así que por favor Desactiva la función THP. Redis no es una excepción, pero la razón dada en el blog oficial de Redis es: el uso de grandes páginas de memoria ralentizará la bifurcación cuando bgsave; si estas páginas de memoria se modifican en el proceso original después de la bifurcación, deben copiarse. (Es decir, copia sobre escritura), dicha copia consumirá mucha memoria (después de todo, las personas son páginas enormes, copiar una copia consume mucho dinero). Por lo tanto, desactive la función de páginas enormes transparentes en el sistema operativo.
  4. Espacio de intercambio: cuando algunas páginas de memoria se almacenan en el archivo de espacio de intercambio y Redis necesita solicitar esos datos, el sistema operativo bloqueará el proceso de Redis y luego sacará la página deseada del espacio de intercambio y la colocará en la memoria. Esto implica el bloqueo de todo el proceso, por lo que puede causar un problema de demora. Una solución es prohibir el uso de espacio de intercambio (como se sugiere en Redis Essentials, si el espacio de memoria es insuficiente, utilice otros métodos).

Considere el costo de la persistencia

Una función importante de Redis es la persistencia, que consiste en copiar datos al disco duro. Basado en la persistencia, Redis tiene recuperación de datos y otras funciones.

Pero mantener esta función persistente también tiene una sobrecarga de rendimiento.

Primero que nada, RDB es completamente persistente.

Este método de persistencia empaqueta todos los datos de Redis en un archivo rdb y lo coloca en el disco duro. Sin embargo, el proceso original para realizar el proceso de persistencia RDB bifurca un proceso hijo y la llamada al sistema de la bifurcación lleva tiempo. Según un experimento realizado por Redis Lab hace 6 años [12] , un nuevo AWS EC2 m1.small ^ El 13, se necesitaron más de 700 milisegundos para bifurcar un proceso de Redis que ocupa 1 GB de memoria. Durante este tiempo, Redis no pudo procesar la solicitud.

Aunque las máquinas actuales deberían ser mejores que las de entonces, también debería tenerse en cuenta la sobrecarga de la horquilla. Por esta razón, utilice un intervalo de persistencia de RDB razonable, no con demasiada frecuencia .

A continuación, analizamos otro método de persistencia: persistencia incremental AOF.

Este método de persistencia guardará las instrucciones que envió al servidor de redis en forma de texto (el formato sigue el protocolo de redis). Durante este proceso, se llamarán dos llamadas al sistema, una es escritura (2), la sincronización se completa y la otra es fsync (2), completar de forma asincrónica.

Ambos pueden ser la causa del problema de demora:

  1. La escritura puede estar bloqueada porque el búfer de salida está lleno o porque el kernel está sincronizando los datos del búfer con el disco duro.
  2. La función de fsync es asegurar que los datos escritos en el archivo aof por escritura caigan en el disco duro. En un disco duro de 7200 rpm, puede haber una demora de aproximadamente 20 milisegundos y el consumo es bastante grande. Más importante aún, la escritura puede bloquearse mientras fsync está en progreso.

Entre ellos, el bloqueo de escritura parece aceptable, porque no hay mejor manera de escribir datos en un archivo. Pero para fsync, Redis permite tres configuraciones, la que elija depende de su equilibrio entre la puntualidad y el rendimiento de la copia de seguridad:

  1. always: cuando appendfsync se establece en always, fsync se ejecutará sincrónicamente con las instrucciones del cliente, por lo que es más probable que cause problemas de demora, pero la puntualidad de la copia de seguridad es la mejor.
  2. everysec: fsync se ejecuta de forma asincrónica cada segundo, el rendimiento de redis será mejor en este momento, pero fsync aún puede bloquear la escritura, lo cual es una opción de compromiso.
  3. no: redis no iniciará activamente fsync (no nunca fsync, eso es poco probable), pero el kernel decide cuándo realizar fsync

Utilice una arquitectura distribuida: separación de lectura y escritura, fragmentación de datos

Arriba, todos nos basamos en un único servicio Redis para la optimización. A continuación, consideramos el uso de arquitectura distribuida para garantizar el rendimiento de Redis cuando la escala del sitio web aumenta.

En primer lugar, bajo qué circunstancias hay que (o mejor) utilizar una arquitectura distribuida:

  1. La cantidad de datos es tan grande que es imposible que un solo servidor contenga memoria, como 1 T
  2. Necesidad de alta disponibilidad de servicio
  3. La presión de solicitud única es demasiado alta

Para resolver estos problemas, puede utilizar la fragmentación de datos o la separación maestro-esclavo, o ambas (es decir, la estructura maestro-esclavo también se establece en el nodo del clúster utilizado para la fragmentación).

Esta arquitectura puede agregar nuevos puntos de entrada para mejorar el rendimiento:

  1. Envíe instrucciones lentas a algunas bibliotecas esclavas para su ejecución
  2. Ponga la función de persistencia en una biblioteca esclava poco utilizada
  3. Fragmentar algunas listas grandes

Los dos primeros se basan en la función de un solo subproceso de Redis, utilizando otros procesos (o incluso máquinas) para complementar el rendimiento.

Por supuesto, el uso de una arquitectura distribuida también puede tener un impacto en el rendimiento. Por ejemplo, las solicitudes deben reenviarse y los datos deben replicarse y distribuirse continuamente. (Por verificar)

Epílogo

De hecho, hay muchas cosas que también afectan el rendimiento de Redis, como el refrito activo (refrito de la tabla principal de claves, 10 veces por segundo, apagarlo puede mejorar un poco el rendimiento), pero este blog lleva mucho tiempo escrito. Además, lo más importante no es recopilar los problemas que han sido planteados por otros y luego memorizar las soluciones; sino dominar los principios básicos de Redis, y resolver nuevos problemas de forma constante.

Supongo que te gusta

Origin blog.csdn.net/AMSRY/article/details/108532595
Recomendado
Clasificación