本地缓存的问题:
缓存数据不一致:每个微服务都要有缓存服务、数据更新时只更新自己的缓存,造成缓存数据不一致
解决方案:分布式缓存,微服务共用 缓存中间件
分布式锁
分布式项目时,但本地锁只能锁住当前服务,需要分布式锁
redis分布式锁的原理:setnx,同一时刻只能设置成功一个
前提,锁的key是一定的,value可以变
没获取到锁阻塞或者sleep一会
public Map<String, List<Catelog2Vo>> getCatalogJsonDbWithRedisLock() {
if (lock) { // 获取到锁
} else { //没获取到锁阻塞或者sleep一会
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 睡眠0.1s后,重新调用
//自旋
return getCatalogJsonDbWithRedisLock();
}
}
设置好了锁,万一服务出现宕机,没有执行删除锁逻辑,这就造成了死锁
解决:设置过期时间
Boolean lock = ops.setIfAbsent("lock", uuid, 500, TimeUnit.SECONDS);
业务还没执行完锁就过期了,别人拿到锁,自己执行完去删了别人的锁
解决:锁续期(redisson有看门狗),。删锁的时候明确是自己的锁。如uuid
String uuid = UUID.randomUUID().toString();
Boolean lock = ops.setIfAbsent("lock", uuid, 500, TimeUnit.SECONDS);
判断uuid对了,但是将要删除的时候锁过期了,别人设置了新值,那删除了别人的锁
解决:删除锁必须保证原子性(保证判断和删锁是原子的)。使用redis+Lua脚本完成,脚本是原子的
// Lua脚本
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"), // 键key的集合
* lockValue); // 锁的值
*/
stringRedisTemplate.execute(
new DefaultRedisScript<Long>(script, Long.class), // 脚本和返回类型
Arrays.asList("lock"), // 参数
lockValue); // 参数值,锁的值
public Map<String, List<Catelog2Vo>> getCatalogJsonDbWithRedisLock() {
String uuid = UUID.randomUUID().toString();
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
// 设置好了锁,万一服务出现宕机,没有执行删除锁逻辑,这就造成了死锁
// 解决:设置过期时间
Boolean lock = ops.setIfAbsent("lock", uuid, 500, TimeUnit.SECONDS);
if (lock) {
Map<String, List<Catelog2Vo>> categoriesDb = getCategoryMap();
String lockValue = ops.get("lock");
// 业务还没执行完锁就过期了,别人拿到锁,自己执行完去删了别人的锁
// 解决:锁续期(redisson有看门狗)。删锁的时候明确是自己的锁。如uuid
// 判断uuid对了,但是将要删除的时候锁过期了,别人设置了新值,那删除了别人的锁
// 解决:删除锁必须保证原子性(保证判断和删锁是原子的)。使用redis+Lua脚本完成,脚本是原子的
// get和delete原子操作
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"), // 键key的集合
* lockValue); // 锁的值
*/
stringRedisTemplate.execute(
new DefaultRedisScript<Long>(script, Long.class), // 脚本和返回类型
Arrays.asList("lock"), // 参数
lockValue); // 参数值,锁的值
return categoriesDb;
} else {
//没获取到锁阻塞或者sleep一会
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 睡眠0.1s后,重新调用 //自旋
return getCatalogJsonDbWithRedisLock();
}
}