Caché distribuido (problemas y soluciones comunes)

1) La caché local enfrenta problemas

Cuando hay múltiples servicios, la caché de cada servicio solo se puede usar para el servicio, de modo que cada servicio tiene que consultar la base de datos una vez, y cuando se actualizan los datos, solo se actualizarán los datos de la caché de un solo servicio, lo que causará inconsistencia en los datos Problema
Inserte la descripción de la imagen aquí
Todos los servicios van al mismo redis para obtener datos, puede evitar este problema
Inserte la descripción de la imagen aquí

2) Cerradura distribuida

Cuando el proyecto distribuido también necesita bloquearse en alta concurrencia, pero el bloqueo local solo puede bloquear el servicio actual, en este momento, se requiere un bloqueo distribuido
Inserte la descripción de la imagen aquí

3) La evolución de las cerraduras distribuidas

Fundamental

Podemos ir a un lugar al mismo tiempo para "ocupar el hoyo", y si lo hacemos, ejecutaremos la lógica. De lo contrario, debe esperar hasta que se libere el bloqueo. "Zhankeng" puede ir a redis, puede ir a la base de datos, puede ir a cualquier lugar al que todos puedan acceder. Esperando una forma que pueda girar.

La etapa uno

Inserte la descripción de la imagen aquí

	public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    
    
        //阶段一
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");
        //获取到锁,执行业务
        if (lock) {
    
    
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            //删除锁,如果在此之前报错或宕机会造成死锁
            stringRedisTemplate.delete("lock");
            return categoriesDb;
        }else {
    
    
            //没获取到锁,等待100ms重试
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

public Map<String, List<Catalog2Vo>> getCategoryMap() {
    
    
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String catalogJson = ops.get("catalogJson");
        if (StringUtils.isEmpty(catalogJson)) {
    
    
            System.out.println("缓存不命中,准备查询数据库。。。");
            Map<String, List<Catalog2Vo>> categoriesDb= getCategoriesDb();
            String toJSONString = JSON.toJSONString(categoriesDb);
            ops.set("catalogJson", toJSONString);
            return categoriesDb;
        }
        System.out.println("缓存命中。。。。");
        Map<String, List<Catalog2Vo>> listMap = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {
    
    });
        return listMap;
    }

problema:

1. Setnx está ocupado, el código comercial es anormal o el programa se bloquea durante el proceso de la página. No se ejecuta ninguna lógica de bloqueo de eliminación, lo que provoca un interbloqueo

Solución: establezca la expiración automática del bloqueo, incluso si no se elimina, se eliminará automáticamente

Etapa dos

Inserte la descripción de la imagen aquí

  public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    
    
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");
        if (lock) {
    
    
            //设置过期时间
            stringRedisTemplate.expire("lock", 30, TimeUnit.SECONDS);
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            stringRedisTemplate.delete("lock");
            return categoriesDb;
        }else {
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

problema:

1. Setnx está configurado, está a punto de configurar el tiempo de vencimiento y está inactivo. Está estancado de nuevo.

resolver:

La configuración de la hora de vencimiento y el marcador de posición debe ser atómica. redis admite el uso del comando setnx ex

Etapa tres

Inserte la descripción de la imagen aquí

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    
    
    //加锁的同时设置过期时间,二者是原子性操作
    Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "1111",5, TimeUnit.SECONDS);
    if (lock) {
    
    
        Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
        //模拟超长的业务执行时间
        try {
    
    
            Thread.sleep(6000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        stringRedisTemplate.delete("lock");
        return categoriesDb;
    }else {
    
    
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return getCatalogJsonDbWithRedisLock();
    }
}

problema:

1. ¿Eliminar el candado directamente? ? ?
Si el bloqueo en sí expira debido a un período comercial prolongado, lo eliminamos directamente, lo que puede eliminar el bloqueo retenido por otros.
Solución: cuando
el candado está ocupado, el valor se especifica como uuid y cada persona hace coincidir su propio candado antes de eliminarlo.

Etapa cuatro

Inserte la descripción de la imagen aquí

 public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    
    
        String uuid = UUID.randomUUID().toString();
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
     	//为当前锁设置唯一的uuid,只有当uuid相同时才会进行删除锁的操作
        Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
        if (lock) {
    
    
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            String lockValue = ops.get("lock");
            if (lockValue.equals(uuid)) {
    
    
                try {
    
    
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                stringRedisTemplate.delete("lock");
            }
            return categoriesDb;
        }else {
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

problema:

1. Si se considera que es el valor actual, cuando el bloqueo está a punto de eliminarse, el bloqueo ha expirado y otra persona ha establecido el nuevo valor. Luego borramos el candado de otra persona

resolver:

Eliminar bloqueos debe garantizar la atomicidad. Utilice el script redis + Lua para completar

Forma final de la quinta etapa

Inserte la descripción de la imagen aquí

 public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    
    
        String uuid = UUID.randomUUID().toString();
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
        if (lock) {
    
    
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            String lockValue = ops.get("lock");
            String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                    "    return redis.call(\"del\",KEYS[1])\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end";
            stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), lockValue);
            return categoriesDb;
        }else {
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

Asegurar la atomicidad del bloqueo [ocupación + tiempo de caducidad] y la eliminación del bloqueo [juicio + eliminación]. Lo más difícil, la renovación automática de la cerradura.

Supongo que te gusta

Origin blog.csdn.net/songyinyi/article/details/113756432
Recomendado
Clasificación