Solución de problemas de la aplicación Redis

16. Solución de problemas de la aplicación Redis

16.1  Penetración de caché

16.1.1  Descripción del problema

Los datos correspondientes a la clave no existen en la fuente de datos. Cada vez que no se puede obtener una solicitud de esta clave desde el caché, la solicitud se presionará en la fuente de datos, lo que puede abrumar la fuente de datos. Por ejemplo, usar una identificación de usuario inexistente para obtener información del usuario, ni el caché ni la base de datos, si los piratas informáticos aprovechan esta vulnerabilidad para atacar, la base de datos puede verse abrumada.

 16.1.2  Soluciones

Un dato que no debe existir en el caché y no se puede consultar, porque el caché se escribe pasivamente cuando falla, y para la tolerancia a fallas, si los datos no se pueden encontrar desde la capa de almacenamiento, no se escribirán en el caché, lo que dará como resultado estos datos inexistentes Cada solicitud tiene que ir a la capa de almacenamiento para consultar, lo que pierde el significado de almacenamiento en caché.

solución:

Almacenamiento en caché de valores nulos: si los datos devueltos por una consulta están vacíos (ya sea que los datos no existan o no), aún almacenamos en caché el resultado vacío (nulo), y el tiempo de vencimiento del resultado nulo se establecerá muy corto, no más de cinco minutos

Configure la lista accesible (lista blanca):

Use el tipo de mapas de bits para definir una lista accesible. La identificación de la lista se usa como el desplazamiento de los mapas de bits. Cada visita se compara con la identificación en el mapa de bits. Si la identificación de acceso no está en los mapas de bits, será interceptada y el acceso es No permitido.

Adopte el filtro Bloom : (Bloom filter (Bloom Filter) fue propuesto por Bloom en 1970. En realidad, es un vector binario muy largo (mapa de bits) y una serie de funciones de asignación aleatorias (funciones hash).

Los filtros Bloom se pueden usar para recuperar si un elemento está en un conjunto. Su ventaja es que la eficiencia del espacio y el tiempo de consulta son mucho mayores que el algoritmo general, y la desventaja es que existe una cierta tasa de identificación errónea y dificultad en la eliminación. )

Haga un hash de todos los datos posibles en mapas de bits lo suficientemente grandes, y los datos que no deben existir serán interceptados por estos mapas de bits, evitando así la presión de consulta en el sistema de almacenamiento subyacente.

Monitoreo en tiempo real: cuando se descubre que la tasa de aciertos de Redis comienza a disminuir rápidamente, es necesario verificar los objetos de acceso y los datos a los que se accede, y cooperar con el personal de operación y mantenimiento para establecer una lista negra para restringir los servicios.

16.2  Desglose de caché

16.2.1  Descripción del problema

 16.2.2  Soluciones

Se puede acceder a la clave en un momento determinado con alta simultaneidad, que es un dato muy "caliente". En este momento, se debe considerar un problema: el problema de que el caché se "descomponga".

Resolver el problema:

(1) Datos populares preestablecidos: antes del acceso máximo a redis, almacene algunos datos populares en redis por adelantado y aumente la duración de estas claves de datos populares

(2) Ajuste en tiempo real: controle qué datos son populares en el sitio y ajuste el tiempo de caducidad de la clave en tiempo real

(3) Usar candados:

  1. Es decir, cuando el caché no es válido (considerando que el valor extraído está vacío), no debe cargar db inmediatamente.
  2. Primero use algunas operaciones de la herramienta de caché con un valor de retorno de operación exitoso (como SETNX de Redis) para establecer una clave de exclusión mutua
  3. Cuando la operación regrese con éxito, realice la operación de carga de base de datos, reinicie el caché y finalmente elimine la clave de exclusión mutua;
  4. Cuando la operación falla, demuestra que hay un subproceso que carga la base de datos y el subproceso actual duerme durante un período de tiempo antes de volver a intentar todo el método de obtención de caché.

16.3  Avalancha de caché 

16.3.1  Descripción del problema

Los datos correspondientes a la clave existen, pero caducan en redis. Si hay una gran cantidad de solicitudes simultáneas en este momento, estas solicitudes generalmente cargarán datos de la base de datos backend y los configurarán nuevamente en el caché cuando encuentren que el caché ha caducado La base de datos backend está aplastada.

La diferencia entre la avalancha de caché y la avería del caché es que aquí es para muchos cachés de claves, mientras que el primero es el acceso normal a una determinada clave.

 momento de invalidación de caché

16.3.2  Soluciones 

¡El impacto del efecto de avalancha en el sistema subyacente cuando el caché no es válido es terrible!

solución:

Cree una arquitectura de caché de varios niveles:

caché nginx + caché redis + otros cachés (ehcache, etc.)

Usar bloqueos o colas :

Utilice bloqueos o colas para asegurarse de que no haya una gran cantidad de subprocesos leyendo y escribiendo en la base de datos al mismo tiempo, para evitar que una gran cantidad de solicitudes simultáneas caigan en el sistema de almacenamiento subyacente cuando ocurra una falla. No apto para alta concurrencia

Establezca el indicador de caducidad para actualizar el caché:

Registre si los datos almacenados en caché caducan (establezca la cantidad de anticipo). Si caduca, se activará para notificar a otro subproceso para actualizar el caché de clave real en segundo plano.

Distribuya los tiempos de caducidad de la memoria caché:

Por ejemplo, podemos agregar un valor aleatorio basado en el tiempo de vencimiento original, como 1-5 minutos aleatorios, de modo que la tasa de repetición del tiempo de vencimiento de cada caché se reduzca y sea difícil desencadenar eventos de falla colectivos. .

16.4  Bloqueos distribuidos

16.4.1  Descripción del problema

Con las necesidades del desarrollo comercial, después de que el sistema de implementación original de una sola máquina se convierta en un sistema de clúster distribuido, debido a que el sistema distribuido es multiproceso, multiproceso y distribuido en diferentes máquinas, esto hará que el control de concurrencia se bloquee en el situación de implementación independiente original La estrategia falla y la API de Java pura no puede proporcionar la capacidad de distribuir bloqueos. Para resolver este problema, se necesita un mecanismo de exclusión mutua entre JVM para controlar el acceso a los recursos compartidos.¡Este es el problema que deben resolver los bloqueos distribuidos!

La implementación principal de bloqueos distribuidos:

1. Realice un bloqueo distribuido basado en la base de datos

2. Basado en caché (Redis, etc.)

3. Basado en Zookeeper

Cada solución de bloqueo distribuido tiene sus propias ventajas y desventajas:

1. Rendimiento: Redis es el más alto

2. Confiabilidad: el cuidador del zoológico es el más alto

Aquí, implementamos bloqueos distribuidos basados ​​en redis.

16.4.2 Solución: use redis para implementar bloqueos distribuidos

redis: comando

# establecer sku: 1: información "OK" NX PX 10000

EX segundo: establezca el tiempo de caducidad de la clave en segundos. El segundo valor de la clave SET EX es equivalente al segundo valor de la clave SETEX.

PX milisegundos: establezca el tiempo de caducidad de la clave en milisegundos milisegundos. El valor de la clave SET PX en milisegundos es equivalente al valor de la clave PSETEX en milisegundos.

NX: configure la clave solo si la clave no existe. El valor de la clave SET NX es equivalente al valor de la clave SETNX.

XX: establezca la clave solo si ya existe.

 

1. Múltiples clientes adquieren bloqueos simultáneamente (setnx)

2. La adquisición es exitosa, ejecute la lógica de negocios {obtenga datos de la base de datos, colóquelos en caché}, libere el bloqueo (del) después de que se complete la ejecución

3. Otros clientes esperan el reintento

16.4.3  Escritura de código

Devoluciones: establecer si 0

@GetMapping("testLock")
public void testLock(){
    //1获取锁,setne
    Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");
    //2获取锁成功、查询num的值
    if(lock){
        Object value = redisTemplate.opsForValue().get("num");
        //2.1判断num为空return
        if(StringUtils.isEmpty(value)){
            return;
        }
        //2.2有值就转成成int
        int num = Integer.parseInt(value+"");
        //2.3把redis的num加1
        redisTemplate.opsForValue().set("num", ++num);
        //2.4释放锁,del
        redisTemplate.delete("lock");

    }else{
        //3获取锁失败、每隔0.1秒再获取
        try {
            Thread.sleep(100);
            testLock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Reinicie, sirva el clúster y pase la prueba de estrés de la puerta de enlace:

ab -n 10 00 -c 100 http://192.168.140.1:8080/test/testLock

Ver el valor de num en redis:

Básicamente realizado.

Problema: setnx acaba de adquirir el bloqueo y la lógica comercial es anormal, lo que hace que el bloqueo no se libere

Solución: establezca el tiempo de caducidad y libere automáticamente el bloqueo.

16.4.4  Optimización para establecer el tiempo de caducidad del bloqueo

Hay dos formas de establecer el tiempo de caducidad:

1. Primero piense en establecer el tiempo de caducidad a través de expire (falta de atomicidad: si ocurre una excepción entre setnx y expire, el bloqueo no se puede liberar)

2. Especifique el tiempo de caducidad al configurar (recomendado)

 

 Establecer tiempo de caducidad:

La prueba de estrés definitivamente tampoco es un problema. autotest

Problema: Es posible que se liberen los bloqueos de otros servidores.

Escenario: Si el tiempo de ejecución de la lógica de negocios es de 7s. El proceso de ejecución es el siguiente

  1. La lógica empresarial index1 no se ha ejecutado y el bloqueo se libera automáticamente después de 3 segundos.
  1. index2 adquiere el bloqueo, ejecuta la lógica empresarial y el bloqueo se libera automáticamente después de 3 segundos.
  2. index3 adquiere el bloqueo y ejecuta la lógica empresarial
  3. Una vez que se completa la ejecución de la lógica de negocios index1, se llama del para liberar el bloqueo. En este momento, se libera el bloqueo de index3, lo que hace que otros liberen el negocio index3 después de solo un segundo de ejecución.

Al final, es igual a la situación de no bloqueo.

Solución: cuando setnx adquiere un bloqueo, establezca un valor único específico (por ejemplo: uuid); obtenga este valor antes de liberarlo y juzgue si es su propio bloqueo

 16.4.5  UUID optimizado para evitar la eliminación accidental

 

16.4.6  El script LUA optimizado garantiza la atomicidad de la eliminación

@GetMapping("testLockLua")
public void testLockLua() {
    //1 声明一个uuid ,将做为一个value 放入我们的key所对应的值中
    String uuid = UUID.randomUUID().toString();
    //2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!
    String skuId = "25"; // 访问skuId 为25号的商品 100008348542
    String locKey = "lock:" + skuId; // 锁住的是每个商品的数据
// 3 获取锁
    Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS);

    // 第一种: lock 与过期时间中间不写任何的代码。
    // redisTemplate.expire("lock",10, TimeUnit.SECONDS);//设置过期时间
    // 如果true
    if (lock) {
        // 执行的业务逻辑开始
        // 获取缓存中的num 数据
        Object value = redisTemplate.opsForValue().get("num");
        // 如果是空直接返回
        if (StringUtils.isEmpty(value)) {
            return;
        }
        // 不是空 如果说在这出现了异常! 那么delete 就删除失败! 也就是说锁永远存在!
        int num = Integer.parseInt(value + "");
        // 使num 每次+1 放入缓存
        redisTemplate.opsForValue().set("num", String.valueOf(++num));
        /*使用lua脚本来锁*/
        // 定义lua 脚本
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        // 使用redis执行lua执行
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        // 设置一下返回值类型 为Long
        // 因为删除判断的时候,返回的0,给其封装为数据类型。如果不封装那么默认返回String 类型,
        // 那么返回字符串与0 会有发生错误。
        redisScript.setResultType(Long.class);
        // 第一个要是script 脚本 ,第二个需要判断的key,第三个就是key所对应的值。
        redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid);
    } else {
        // 其他线程等待
        try {
            // 睡眠
            Thread.sleep(1000);
            // 睡醒了之后,调用方法。
            testLockLua();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 Explicación detallada del script Lua:

 Utilizado correctamente en el proyecto:

  1. Defina la clave, se debe definir la clave para cada sku, es decir, cada sku tiene un candado.

String locKey = "lock:" +skuId; // Se bloquean los datos de cada producto

Bloqueo booleano = redisTemplate .opsForValue().setIfAbsent(locKey, uuid, 3 ,TimeUnit. SECONDS );

16.4.7  Resumen

1.  Bloquear

// 1.redis中获取锁, establecer  k1 v1 px 20000 nx
String uuid = UUID. UUID aleatorio ().toString();
Bloqueo booleano = esto . redisTemplate .opsForValue()
      .setIfAbsent( "bloquear" , uuid, 2 , Unidad de tiempo. SEGUNDOS );

2. Usa lua para liberar el candado

// 2. Liberar el bloqueo  del
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end " ;
// Establecer el tipo de datos devuelto por el script lua DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); // Establecer el tipo de retorno del script lua en Long redisScript.setResultType(Long.class ); redisScript.setScriptText( script) ; redisTemplate.execute (redisScript, Arrays. asList ( "lock" ), uuid);




3. Reintentar

Hilo. dormir ( 500 );
pruebaBloquear();

Para garantizar que los bloqueos distribuidos estén disponibles, al menos debemos asegurarnos de que la implementación de los bloqueos cumpla con las siguientes cuatro condiciones :

- Exclusión mutua. En cualquier momento, solo un cliente puede mantener el candado.

- No se producirá ningún interbloqueo. Incluso si un cliente falla mientras mantiene el bloqueo y no lo desbloquea activamente, puede garantizar que otros clientes puedan bloquearlo más tarde.

- El problema debería acabar con él. El bloqueo y desbloqueo debe ser el mismo cliente, y el propio cliente no puede desbloquear el bloqueo agregado por otros.

- El bloqueo y desbloqueo debe ser atómico.

 

Supongo que te gusta

Origin blog.csdn.net/fgwynagi/article/details/130050673
Recomendado
Clasificación