基于互斥锁方式解决缓存击穿问题
大体思路:

参考代码:
1 2 3 4 5 6 7 8 9 10 11 12
| @Override public Result queryById(Long id) {
Shop shop = queryWithMutex(id);
if(shop==null){ return Result.fail("店铺信息不存在"); }
return Result.ok(shop); }
|
关键代码:
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| public Shop queryWithMutex(Long id){
String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);
if(StrUtil.isNotBlank(shopJson)){ return JSONUtil.toBean(shopJson, Shop.class); }
if(shopJson!=null){ return null; }
Shop shop = null;
try {
if (!tryLock(RedisConstants.LOCK_SHOP_KEY+id)) { Thread.sleep(50); return queryWithMutex(id); }
shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);
if(StrUtil.isNotBlank(shopJson)){ return JSONUtil.toBean(shopJson, Shop.class); }
if(shopJson!=null){ return null; }
shop = getById(id);
if(shop==null){ stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES); return null; }
stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY+ id,JSONUtil.toJsonStr(shop),RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES); } catch (InterruptedException e) { throw new RuntimeException(e); }finally {
unLock(RedisConstants.LOCK_SHOP_KEY+id); }
return shop; }
|
获取锁的代码:
1 2 3 4
| private boolean tryLock(String key){ Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS); return BooleanUtil.isTrue(flag); }
|
这段代码,原理是基于Redis中的setnx操作,先设置一个key值,后面再次设置同样的key时,会设置失败,达成一个锁的效果,其中一个线程抢先取设这个值,后面的线程就无法再设了,场景是多个线程请求一个热点数据,所以他们要设的key是相同的,所以是互斥的。
释放锁的代码:
1 2 3
| private void unLock(String key){ stringRedisTemplate.delete(key); }
|
它的逻辑就是,删除之前设置的key value,让后面的线程,可以去设置这个key