Solución de avalancha de caché: bloques de código sincronizados

1. ¿Qué es la avalancha de caché?

La avalancha de caché se refiere a una situación en la que una gran cantidad de datos almacenados en caché en el servidor de caché falla al mismo tiempo o recibe una gran cantidad de solicitudes al mismo tiempo, lo que hace que todas las solicitudes caigan en la base de datos, lo que provoca problemas de rendimiento de la base de datos e incluso de la base de datos. falta del tiempo.

Las soluciones de avalancha de caché incluyen principalmente lo siguiente:

  1. Aleatorizar la configuración del tiempo de vencimiento de los datos almacenados en caché : para evitar que una gran cantidad de datos almacenados en caché se invaliden al mismo tiempo, puede establecer el tiempo de vencimiento de los datos almacenados en caché en un tiempo aleatorio, lo que puede escalonar efectivamente el tiempo de vencimiento de los datos almacenados en caché y evitar invalidación simultánea.

  2. Agregar un caché de segundo nivel : Agregar un caché de segundo nivel al sistema de aplicaciones puede almacenar en caché los datos activos en el caché de segundo nivel para evitar que una gran cantidad de solicitudes caigan directamente en la base de datos. Al mismo tiempo, el tiempo de vencimiento del caché de segundo nivel debe establecerse más largo que el del caché de primer nivel para aprovechar al máximo el período de validez de los datos almacenados en caché.

  3. Calentamiento de datos : antes de que el sistema se conecte, algunos datos se pueden almacenar en caché con anticipación, lo que puede evitar que una gran cantidad de solicitudes caigan directamente en la base de datos después de que el sistema se conecte.

  4. Limitación y degradación actuales : limite la cantidad de solicitudes durante los períodos pico para evitar que el sistema se vea abrumado. Al mismo tiempo, algunas solicitudes menos importantes se pueden degradar, como devolver valores predeterminados o mensajes amigables, para garantizar la disponibilidad de todo el sistema.

  5. Implementación distribuida : distribuya servidores de caché en varias máquinas para evitar puntos únicos de falla y mejorar la disponibilidad del sistema y la tolerancia a fallas.

2. Demostrar un caso sencillo

En circunstancias normales, para evitar que las solicitudes concurrentes abrumen la base de datos, se agrega una capa de caché para interceptar las solicitudes y reducir la presión de la base de datos. Sin embargo, a veces debido a tasas de aciertos de caché inestables, una gran cantidad de solicitudes simultáneas acceden a la base de datos, lo que provoca presión excesiva en la base de datos, grande y por lo tanto colapsada. Al final, la base de datos aún falla, entonces, ¿cómo resolver este problema? Aquí hay un método simple, el código es el siguiente:

Defina un bloque de código comercial DoubleCacheServiceImpl para determinar primero si existen datos en el caché. Si no se consulta la base de datos, los datos consultados finalmente se almacenan en el caché, como se muestra a continuación:

@Service
@Slf4j
public class DoubleCacheServiceImpl implements DoubleCacheService{
    
    

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Person doubleCache(String key) throws Exception {
    
    
        
        Object o = redisTemplate.opsForValue().get(key);

        if (Objects.nonNull(o)) {
    
    
            log.info(">>>>>>从 Redis 缓存中获取结果 person = {}",o);
            return (Person) o;
        }

        Person person = jdbcTemplate.queryForObject("select id, user_name,age from test_user where id=?",new Object[]{
    
    key}, new BeanPropertyRowMapper<>(Person.class));
        log.info(">>>>>>从数据库中获取结果 person = {}",person);

        redisTemplate.opsForValue().set(key,person);
        redisTemplate.expire(key, 30,TimeUnit.SECONDS);
        return person;
    }
}

Al definir la capa Controlador, se simula 50 concurrencias a través de CountDownLatch, el código es el siguiente:

@RestController
@Slf4j
public class DoubleCacheController {
    
    

    @Autowired
    private DoubleCacheService doubleCacheService;

    @RequestMapping("/doubleCache")
    public void doubleCache(String key) {
    
    

        CountDownLatch countDownLatch = new CountDownLatch(COUNT);

        for (int i = 0; i < COUNT; i++) {
    
    
            new Thread(()->{
    
    
                try {
    
    
                    // 准备 50 个线程
                    countDownLatch.await();

                    doubleCacheService.doubleCache(key);

                } catch (Exception e) {
    
    
                    log.info(">>>>>>出现异常,msg={}",e.getMessage());
                    throw new RuntimeException(e);
                }

            }).start();
            countDownLatch.countDown();
        }
    }
}

El resultado resultante es el siguiente:

2023-01-12 18:36:15.057  INFO 65094 --- [      Thread-29] c.g.c.rabbitmq.DoubleCacheServiceImpl    : >>>>>>从数据库中获取结果 person = Person(id=31, userName=薛擎熙, age=31)
2023-01-12 18:36:15.057  INFO 65094 --- [      Thread-45] c.g.c.rabbitmq.DoubleCacheServiceImpl    : >>>>>>从数据库中获取结果 person = Person(id=31, userName=薛擎熙, age=31)
2023-01-12 18:36:15.057  INFO 65094 --- [      Thread-54] c.g.c.rabbitmq.DoubleCacheServiceImpl    : >>>>>>从数据库中获取结果 person = Person(id=31, userName=薛擎熙, age=31)
2023-01-12 18:36:15.057  INFO 65094 --- [      Thread-49] c.g.c.rabbitmq.DoubleCacheServiceImpl    : >>>>>>从数据库中获取结果 person = Person(id=31, userName=薛擎熙, age=31)
2023-01-12 18:36:15.059  INFO 65094 --- [      Thread-24] c.g.c.rabbitmq.DoubleCacheServiceImpl    : >>>>>>从数据库中获取结果 person = Person(id=31, userName=薛擎熙, age=31) 

de">>>>>>Obtener resultados de la base de datos"Se puede ver en esta pantalla que las 50 solicitudes fueron enviadas a la base de datos. La base de datos puede soportar 50 solicitudes. Ahora intente cambiar a 500 solicitudes . Las máquinas con bajo rendimiento pueden solicitar demasiadas conexiones .

En la lógica de la clase DoubleCacheServiceImpl anterior, dado que se ejecutan 50 subprocesos al mismo tiempo, no hay caché al consultar Redis, por lo que todos los subprocesos consultan la base de datos y la presión de la base de datos no se puede tolerar. Ahora supongamos que a un subproceso se le permite Consulta la base de datos y otros subprocesos no pueden hacerlo, lo que reduce la presión de la base de datos. Entonces, ¿cómo garantizar que un hilo funcione y los demás esperen? Puedes usar el bloqueo. Aquí simplemente usamos bloqueos de sincronización . Después de todo, el rendimiento de los bloqueos de sincronización se ha optimizado después de JDK1.6.

El código de servicio modificado es el siguiente:

@Service
@Slf4j
public class DoubleCacheServiceImpl implements DoubleCacheService{
    
    

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Person doubleCache(String key) throws Exception {
    
    
        Person person;
        Object o = redisTemplate.opsForValue().get(key);

        if (Objects.nonNull(o)) {
    
    
            log.info(">>>>>>从 Redis 缓存中获取结果 person = {}",o);
            return (Person) o;
        }

        synchronized (DoubleCacheServiceImpl.class) {
    
    
            Object tempObj = redisTemplate.opsForValue().get(key);

            if (Objects.nonNull(tempObj)) {
    
    
                log.info(">>>>>>从 Redis 缓存中获取结果 person = {}",tempObj);
                return (Person) tempObj;
            }

            person = jdbcTemplate.queryForObject("select id, user_name,age from test_user where id=?",new Object[]{
    
    key}, new BeanPropertyRowMapper<>(Person.class));
            log.info(">>>>>>从数据库中获取结果 person = {}",person);

            redisTemplate.opsForValue().set(key,person);
            redisTemplate.expire(key, 30,TimeUnit.SECONDS);
        }

        return person;
    }
}

Consulta el caché nuevamente en el bloqueo de sincronización, que es algo similar a DCL, si hay un caché en el caché. Es solo que el rendimiento general se ve afectado. Pero reduce la presión de la base de datos.

Supongo que te gusta

Origin blog.csdn.net/qq_35971258/article/details/128664578
Recomendado
Clasificación