Caché distribuida
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
Todos los servicios van al mismo redis para obtener datos, puede evitar este problema
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
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
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
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
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
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
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.