日常bug -- 锁内查询缓存还是锁外查询缓存

示例说明:获取第三方请求token,token的有效时长为24小时,且获取接口存在qps=3的限制

1、先查询缓存,在加锁请求第三方

java 复制代码
    private void getAccessToken(Config config){
         String key = "redisKey";
         Object redisValue = redisUtils.get(key);
         if (ObjectUtils.isNotEmpty(redisValue)){
             config.setAccessToken(redisValue.toString());
         }
         
         boolean locked = redisUtils.lockOn("redisLockOnKey", 3000, 3000, ()->{
             String token = HttpClientUtil.doPostJson();
             config.setAccessToken(token);
             redisUtils.set(key, token, 23 * 3600);
         });
 ​
         if (!locked){
             // 异常提示
         }
     }

2、先加锁在查询缓存,缓存不存在再去请求第三方

java 复制代码
    private void getAccessToken(Config config){
         boolean locked = redisUtils.lockOn("redisLockOnKey", 3000, 3000, ()->{
             String key = "redisKey";
             Object redisValue = redisUtils.get(key);
             if (ObjectUtils.isNotEmpty(redisValue)){
                 config.setAccessToken(redisValue.toString());
             }else {
                 String token = HttpClientUtil.doPostJson();
                 config.setAccessToken(token);
                 redisUtils.set(key, token, 23 * 3600);
             }
         });
 ​
         if (!locked){
             // 异常提示
         }
     }

我的结论和想说明的问题

锁外查询缓存:当缓存不存在时会多请求进入锁等待,当请求进入时还是会存在多次请求的问题

锁内查询缓存:当缓存存在时直接返回,当缓存不存在时仅会请求一次,并将结果放入缓存,等待获取锁的线程便会从缓存中获取,避免多次请求。

AI提供的最优解流程:

java 复制代码
查缓存
     ↓
 命中,直接返回(无锁)
     ↓
 未命中
     ↓
 竞争分布式锁
     ↓
 再次查缓存(Double Check)
     ↓
 存在,直接返回
     ↓
 不存在,请求第三方
     ↓
 写入缓存
java 复制代码
 private void getAccessToken(Config config){
     String key = "redisKey";
     Object redisValue = redisUtils.get(key);
     if (ObjectUtils.isNotEmpty(redisValue)){
         config.setAccessToken(redisValue.toString());
     }
     
     boolean locked = redisUtils.lockOn("redisLockOnKey", 3000, 3000, ()->{
         redisValue = redisUtils.get(key);
         if (ObjectUtils.isNotEmpty(redisValue)){
             config.setAccessToken(redisValue.toString());
         }else {
             String token = HttpClientUtil.doPostJson();
             config.setAccessToken(token);
             redisUtils.set(key, token, 23 * 3600);
         }
     });
 ​
     if (!locked){
         // 异常提示
     }
 }