1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
try {
1.优先从缓存中获取数据
查询Redis获取业务数据
命中缓存则直接返回
if (redisTemplate.hasKey(dataKey)) {
log.info("命中缓存,直接返回,线程ID:{},线程名称:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
return productSku;
}
2.尝试获取 分布式锁(set k v ex nx可能获取锁失败)
构建锁key
String lockKey = "product:sku:lock:" + skuId;
采用UUID作为线程标识
String lockVal = UUID.randomUUID().toString().replaceAll("-", "");
Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 5, TimeUnit.SECONDS);
if (flag) {
3.获取锁成功执行业务,将查询业务数据放入缓存Redis
log.info("获取锁成功:{},线程名称:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
try {
productSku = this.getProductSkuFromDB(skuId);
long ttl = productSku == null ? 1 * 60 : 10 * 60;
redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.SECONDS);
return productSku;
} finally {
4.业务执行完毕释放锁
String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
"then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(scriptText);
redisScript.setResultType(Long.class);
redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
}
} else {
try {
5.获取锁失败则自旋(业务要求必须执行)
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.error("获取锁失败,自旋:{},线程名称:{}", Thread.currentThread().getId(), Thread.currentThread().getName());
return this.getProductSku(skuId);
}
} catch (Exception e) {
//兜底处理方案:Redis服务有问题,将业务数据获取自动从数据库获取
log.error("[商品服务]查询商品信息异常:{}", e);
return this.getProductSkuFromDB(skuId);
}
|