Avalancha de caché de Redis, avería, explicación y solución de penetración, precalentamiento de caché, filtro Bloom, bloqueo mutex

Avalancha de caché de Redis, avería, explicación y solución de penetración, precalentamiento de caché, filtro Bloom, bloqueo mutex

avalancha de caché

**Explicación:** Cuando una gran cantidad de datos almacenados en caché caduca al mismo tiempo o falla el redis, una gran cantidad de solicitudes de usuario llegan directamente a la base de datos, lo que provoca un tiempo de inactividad de la base de datos.

Solución:

  1. Agregue un número aleatorio al tiempo de vencimiento

  2. bloqueo mutex,

    Cuando se descubre que los datos no están en Redis, agregue un bloqueo mutex para garantizar que solo una solicitud construya el caché al mismo tiempo. Si otras solicitudes no adquieren el bloqueo, espere o devuelva un valor nulo. Tenga en cuenta que el tiempo de espera debe configurarse para esperar; de lo contrario, si ocurre la primera solicitud Después de adquirir el bloqueo, se bloqueará, lo que provocará que otras solicitudes nunca adquieran el bloqueo.

    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.company.dubbodemo.entity.User;
    import com.company.dubbodemo.mapper.UserMapper;
    import com.company.dubbodemo.service.UserService;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User>
            implements UserService {
          
          
        
    
        @Resource
        private RedissonClient redissonClient;
        
        @Resource
        private UserMapper userMapper;
        
        @Override
        // 一定要设置sync = true开启异步,否则会导致多个线程同时获取到锁
        @Cacheable(cacheNames = "user", key = "#id", sync = true)
        public User findById(Long id) {
          
          
            /**
             *
             * 加了@Cacheable之后方法体执行说明缓存中不存在所查询的数据
             * 获取一把锁,只要锁的名字一样,就是同一把锁
             */
        
            /**
             * 注意: 设置了lock.lock(10,TimeUnit.SECONDS) 锁过期不会自动续期
             *      1、如果我们传递了锁的过期时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
             *      2、如果没有指定锁的超时时间,就使用30000L(LockWatchdogTimeout 看门狗的默认时间)
             *      可通过RedissonConfig-->getRedissonClient()-->config.setLockWatchdogTimeout()设置看门狗时间
             *         只要占锁成功就会启动一个定时任务【就会重新给锁设置过期时间,新的时间就是看门狗的默认时间】,每隔10s都会自动续期,续期成30s
             * 看门狗机制
             * 1、锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s。不用担心因为业务时间长,锁自动过期被删除
             * 2、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除
             *
             */
            RLock lock = redissonClient.getLock("redissonClient-lock");
        
            // 对第一个线程执行方法体的线程加锁,加了@Cacheable,方法执行之后会将方法的返回值存入缓存,下一个线程直接读取缓存
            lock.lock(10,TimeUnit.SECONDS);
            User user = new User();
            try {
          
          
                user = userMapper.selectById(id);
            } catch (Exception e) {
          
          
                e.printStackTrace();
            } finally {
          
          
                lock.unlock();
            }
            return user;
        }
    
    }
    
  3. Estrategia de doble clave

    Los datos de caché establecen dos claves, una es la clave maestra (establece el tiempo de vencimiento) y la otra es la clave esclava (el tiempo se establece más largo que la clave maestra, por ejemplo, 5 días más). Aunque las claves son diferentes, las Los valores son los mismos.

    Cuando el hilo empresarial no puede acceder a la clave maestra, devuelve directamente los datos de la clave esclava y actualiza la clave maestra y la clave esclava al mismo tiempo.

  4. Configure el caché para que sea permanentemente válido y actualícelo periódicamente en segundo plano.

    No establezca el tiempo de validez del caché, hágalo permanentemente válido y deje el trabajo de actualizar el caché en segundo plano para actualizaciones periódicas.

    Aunque los datos almacenados en caché no tienen un período de validez, el caché no siempre existirá en redis porque cuando la memoria es escasa, redis eliminará automáticamente ciertas claves.

    Actualizar caché periódicamente

    método uno

    Cree una tarea programada para monitorear periódicamente si ciertas claves no son válidas y recargarlas si no lo son.

    Método 2

    Después de que el hilo empresarial descubre que el caché no es válido, envía un mensaje mq para recargar el caché.

    ps: calentamiento de caché

    Después de iniciar el sistema, puede verificar si existen ciertas claves en Redis. Si no existen, vuelva a cargarlas. El precalentamiento de la caché es una técnica de optimización para el uso de la caché.

    Desglose de caché

    Explicación: Cierto caché de punto de acceso falla en un momento determinado y luego sucede que hay una gran cantidad de solicitudes simultáneas. En este momento, estas solicitudes causarán una gran presión en la base de datos. Esta situación se llama falla de caché.

    Ps: el desglose del caché y la penetración del caché a menudo se recuerdan mal, aquí hay un pequeño truco

    Simplemente recuerde el modismo que dice que un hit es seguro; las claves del punto de acceso generalmente son menos de una; de esta manera, puede asociar y recordar que la falla de la caché es el problema de la falla de la clave del punto de acceso.

    Solución

    1. Cola de bloqueo

      El método de bloqueo de la avalancha de caché es el mismo: se bloquea cuando se vuelve a verificar la base de datos y almacena en el búfer una gran cantidad de solicitudes.

    2. La configuración de la clave de hotspot nunca caduca

    Para algunos cachés de puntos de acceso, podemos configurarlos para que nunca caduquen , para garantizar la estabilidad del caché. Sin embargo, debemos prestar atención a actualizar el caché del punto de acceso a tiempo después de que cambien los datos, de lo contrario, se producirán errores en los resultados de la consulta. .

    penetración de caché

    **Explicación:** Los datos a los que accede el usuario no están en el caché ni en la base de datos; cuando ocurre una gran cantidad de solicitudes de este tipo, la presión sobre la base de datos aumenta drásticamente y puede ser penetrada directamente; hay dos situaciones principales en que se produce esta operación.

    1. Debido a un mal funcionamiento comercial, los datos en el caché y los datos en la base de datos se eliminaron accidentalmente, lo que resultó en que no hubiera datos en el caché ni en la base de datos;

    2. Ataques maliciosos de piratas informáticos

    Solución:

    1. Agregue listas negras y listas blancas para restringir el acceso de ciertas IP maliciosas

    2. Caché de valores vacíos o valores predeterminados: cuando no se consulta ni el caché ni la base de datos, el valor vacío se devolverá directamente.

    3. Agregar filtro de floración

      import com.google.common.hash.BloomFilter;
      import com.google.common.hash.Funnels;
      
      /**
       * 布隆过滤器
       */
      public class BloomFilterTest {
              
              
      
          /**
           * @param expectedInsertions 预期插入值
           *                           这个值的设置相当重要,如果设置的过小很容易导致饱和而导致误报率急剧上升,如果设置的过大,也会对内存造成浪费,所以要根据实际情况来定
           * @param fpp                误差率,例如:0.001,表示误差率为0.1%
           * @return 返回true,表示可能存在,返回false一定不存在
           */
          public static boolean isExist(int expectedInsertions, double fpp) {
              
              
              // 创建布隆过滤器对象
              BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 500, 0.01);
      
              // 判断指定元素是否存在
              System.out.println(filter.mightContain(10));
      
              // 将元素添加进布隆过滤器
              filter.put(10);
      
              // 再判断指定元素是否存在
              System.out.println(filter.mightContain(10));
              return filter.mightContain(10);
          }
      
          //主类中进行测试
          public static void main(String[] args) {
              
              
      
              boolean exist = isExist(100000000, 0.001);
          }
      }
      

Supongo que te gusta

Origin blog.csdn.net/itScholar001/article/details/131071967
Recomendado
Clasificación