商品详情页面优化

缓存穿透: 是指查询一个不存在的数据。

缓存雪崩: 是指很多key集体失效。

缓存击穿: 是指一个热点key失效。

 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);
}
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计