¿Quiere hacer cosas en producción? Entonces prueba estos comandos de Redis

Preste atención, no se pierda; ¡continúe actualizando las tecnologías y la información relacionadas con Java! ! !
¡El contenido proviene de las contribuciones de amigos del grupo! ¡Gracias por el apoyo!

Xiao Z cometió un doble error recientemente.

La cosa es así, hace algún tiempo, la transacción de producción de la pequeña empresa z ocasionalmente reportaba un error. Después de algunas investigaciones, la razón final fue porque se agotó el tiempo de ejecución del comando de Redis.

Pero lo desconcertante es que la transacción de producción solo usa el comando simple conjunto de Redis, que es razonable e imposible de ejecutar tan lentamente.

Entonces, ¿qué está causando este problema?

Para descubrir este problema, verificamos y analizamos el registro lento reciente de Redis, y finalmente descubrimos que el comando que consume más tiempo son las teclas XX *

Al ver el prefijo de la llave operada por este comando, Xiao Z se dio cuenta de que esta era la aplicación de la que era responsable. Sin embargo, Xiaoz lo comprobó: aunque su código no usaba activamente el comando keys, el marco subyacente lo usaba indirectamente, por lo que existía el problema de hoy.

causas del problema

La aplicación de la que Xiaoz es responsable es una aplicación de gestión en segundo plano. La gestión de autoridad usa el marco Shiro. Debido a que hay múltiples nodos, es necesario usar una sesión distribuida, por lo que Redis se usa para almacenar información de sesión aquí.

Dado que Shiro no proporcionó directamente Redis para almacenar los componentes de la sesión, Xiaoz tuvo que usar Github, un componente de código abierto shiro-redis.

Dado que el marco de Shiro necesita verificar periódicamente si la sesión es válida, la capa inferior de Shiro llamará a SessionDAO # getActiveSessions para obtener toda la información de la sesión.

Shiro-redis simplemente hereda la interfaz SessionDAO, y la capa inferior usa el comando keys para encontrar todas las claves de sesión almacenadas en Redis.

public Set<byte[]> keys(byte[] pattern){
    checkAndInit();
    Set<byte[]> keys = null;
    Jedis jedis = jedisPool.getResource();
    try{
        keys = jedis.keys(pattern);
    }finally{
        jedis.close();
    }
    return keys;
}

Encuentre la causa del problema, la solución es relativamente simple, encuentre la solución en github, actualice shiro-redis a la última versión.

En esta versión, shiro-redis usa un comando de escaneo en lugar de claves para solucionar este problema.

public Set<byte[]> keys(byte[] pattern) {
    Set<byte[]> keys = null;
    Jedis jedis = jedisPool.getResource();

    try{
        keys = new HashSet<byte[]>();
        ScanParams params = new ScanParams();
        params.count(count);
        params.match(pattern);
        byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY;
        ScanResult<byte[]> scanResult;
        do{
            scanResult = jedis.scan(cursor,params);
            keys.addAll(scanResult.getResult());
            cursor = scanResult.getCursorAsBytes();
        }while(scanResult.getStringCursor().compareTo(ScanParams.SCAN_POINTER_START) > 0);
    }finally{
        jedis.close();
    }
    return keys;

}

Aunque el problema se resolvió con éxito, Xiao Z todavía estaba un poco desconcertado.

  • ¿Por qué el comando de teclas ralentiza la ejecución de otros comandos?
  • ¿Por qué la consulta del comando Keys es tan lenta?
  • ¿Por qué no hay ningún problema con el comando Escanear?

El principio de ejecución de comandos de Redis

Primero, veamos la primera pregunta. ¿Por qué el comando de teclas ralentiza la ejecución de otros comandos?

Para responder a esta pregunta, veamos primero la ejecución de un comando en el cliente Redis:

Inserte la descripción de la imagen aquí

Desde la perspectiva del cliente, la ejecución de un comando se divide en tres pasos:

  1. enviar comando
  2. Ejecutando una orden
  3. Devolver resultado

Pero este es solo el proceso que el propio cliente piensa, pero de hecho, al mismo tiempo, puede haber muchos clientes enviando comandos a Redis, y todos sabemos que Redis usa un modelo de un solo subproceso.

Para procesar todas las solicitudes de los clientes al mismo tiempo, Redis utiliza internamente un método de cola para hacer cola para su ejecución.

Inserte la descripción de la imagen aquí

Entonces, el cliente realmente necesita cuatro pasos para ejecutar un comando:

  1. enviar comando
  2. Cola de comandos
  3. Ejecutando una orden
  4. Devolver resultado

Dado que Redis ejecuta comandos en un solo hilo, las tareas solo se pueden ejecutar de forma secuencial desde la cola.

Siempre que la velocidad de ejecución del comando en este proceso sea demasiado lenta, otras tareas en la cola tienen que esperar, lo que al cliente externo le parece que Redis está bloqueado y no se recibe respuesta.

Por lo tanto, cuando utilice Redis, no ejecute instrucciones que requieran una ejecución prolongada, ya que esto puede hacer que Redis bloquee y afecte la ejecución de otras instrucciones.

Principio KEYS

A continuación, comience a responder la segunda pregunta, ¿por qué la consulta del comando Keys es tan lenta?
Redis usa una estructura de diccionario en la capa inferior, que es similar a la capa inferior de Java HashMap.
Inserte la descripción de la imagen aquí

El comando keys debe devolver todas las claves de Redis que coincidan con el patrón dado. Para lograr esto, Redis tiene que atravesar la matriz subyacente de la tabla hash ht [0] en el diccionario. Esta vez la complejidad es "O (N)" (N Es el número de claves en Redis).

Si la cantidad de claves en Redis es pequeña, la velocidad de ejecución seguirá siendo rápida. Cuando el número de claves de Redis aumente gradualmente, llegando al nivel de millones, decenas de millones o incluso cientos de millones, la velocidad de ejecución será muy lenta.

El siguiente es un experimento realizado localmente por Xiaoz. Usando el script lua para agregar claves de 10W a Redis, y luego usando claves para consultar todas las claves, esta consulta probablemente bloqueará más de diez segundos.

eval "for i=1,100000  do redis.call('set',i,i+1) end" 0

Aquí Xiaoz usa Docker para implementar Redis y el rendimiento puede ser un poco peor.

Principio SCAN

Finalmente, veamos la tercera pregunta: ¿Por qué no hay ningún problema con el comando de escaneo?

Esto se debe a que el comando de exploración utiliza un " iterador basado en cursor " de tecnología negra .

Cada vez que se llama al comando de escaneo, Redis devolverá un nuevo cursor y un cierto número de teclas al usuario. La próxima vez que desee continuar obteniendo las claves restantes, debe pasar este cursor al comando de escaneo para continuar con el proceso de iteración anterior.

En pocas palabras, el comando de escaneo usa paginación para consultar redis.

El siguiente es un ejemplo del proceso iterativo del comando de escaneo:

El comando de exploración utiliza cursores para dividir ingeniosamente una consulta completa en varias veces, lo que reduce la complejidad de la consulta.

Aunque la complejidad de tiempo del comando de escaneo es la misma que la de las teclas, ambos son "O (N)", pero dado que el comando de escaneo solo necesita devolver una pequeña cantidad de claves, la velocidad de ejecución será muy rápida.

Finalmente, aunque el comando scan soluciona la escasez de claves, también introduce otros defectos:

  • El mismo elemento puede devolverse varias veces, lo que requiere que nuestra aplicación aumente la capacidad de manejar elementos repetidos.
  • Si se agrega un elemento a redis durante el proceso de iteración, o se elimina durante el proceso de iteración, ese elemento se devolverá o no.

Estos defectos deben tenerse en cuenta en nuestro desarrollo.

Además de escanear, redis tiene varios otros comandos para la iteración incremental:

  • sscan: se usa para iterar las claves de la base de datos en la base de datos actual, se usa para resolver el posible problema de bloqueo de los smembers
  • El comando hscan se usa para iterar los pares clave-valor en la clave hash para resolver el posible problema de bloqueo de hgetall.
  • El comando zscan: se usa para iterar los elementos en un conjunto ordenado (incluidos los miembros de los elementos y las puntuaciones de los elementos) y se usa para generar zrange, lo que puede causar problemas de bloqueo.

para resumir

Redis usa un solo hilo para ejecutar comandos de operación. Todos los clientes envían comandos, y Redis ahora los pondrá en la cola y luego sacará los comandos correspondientes de la cola en orden.

Si alguna tarea se ejecuta con demasiada lentitud, afectará a otras tareas en la cola, de esta forma, desde la perspectiva del cliente externo, el retraso en obtener una respuesta de Redis parecerá bloqueado.

Por lo tanto, no ejecute instrucciones que puedan causar bloqueo, como claves, smembers, hgetall y zrange en producción. Si realmente necesita ejecutarlas, puede usar el comando de escaneo correspondiente para recorrer progresivamente, lo que puede prevenir efectivamente problemas de bloqueo.

Inserte la descripción de la imagen aquí

Si necesita recibir el material de la entrevista de Java anterior, puede hacer clic aquí para recibirlo gratis

Inserte la descripción de la imagen aquí

Si necesita recibir el material de la entrevista de Java anterior, puede hacer clic aquí para recibirlo gratis

Supongo que te gusta

Origin blog.csdn.net/yueyunyin/article/details/108710585
Recomendado
Clasificación