缓存击穿【热点key问题】

定义:

缓存击穿就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

解决方案:
1. 互斥锁:(mutex key)

业界比较常用的做法是使用Redis的互斥锁(mutex key)。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去查询数据库加载数据, 而是先去set一个mutex key,当操作返回成功时(意味着获得了互斥锁),再进行查库操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX 是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

public Object getIPhone() {
    int cacheTime = 300;
    String cacheKey = "iPhone";
    String cacheValue = CacheHelper.get(cacheKey);
    
    if (cacheValue != null)  {
        return cacheValue;
     } else {
        //查询数据库,先获取锁,需要分情况考虑
        String lockKey = "lock";
        
        //需要设置过期时间,防止此线程挂掉之后,其他线程也无法加锁
        Boolean lockResult = CacheHelper.setnx(lockKey,"mylocker",30)
        
        if (lockResult) {//情况1:加锁成功
            cacheValue = DBHelper.getIPhoneFromDB();
            
            if (cacheValue == null) {
                    //如果发现为空,设置个默认值,也缓存起来
                    cacheValue = "";
            }
            
            CacheHelper.add(cacheKey, cacheValue, cacheTime);
            CacheHelper.delete(lockKey);
            
            return cacheValue;
        } else {//情况2:加锁失败
            Thread.sleep(30);
            cacheValue = CacheHelper.get(cacheKey); 
            return cacheValue;
        }
     }  
}

上述代码看似完美,但是存在问题:虽然加锁成功了,但如果查库的时间过长,导致锁失效了,最后delete锁的时候,删掉的是其他线程加的锁。这个时候应该是“各加各锁,各删各锁”,如下所示:

//查询数据库,先获取锁,需要分情况考虑
String lockKey = "lock";
String lockValue = UUID.randomUUID().toString();
//需要设置过期时间,防止此线程挂掉之后,其他线程也无法加锁
Boolean lockResult = CacheHelper.setnx(lockKey,lockValue,30)

if (lockResult){//情况1:加锁成功
    cacheValue = DBHelper.getIPhoneFromDB();
    
    if (cacheValue == null) {
        //如果发现为空,设置个默认值,也缓存起来
        cacheValue = "";
    }
    
    CacheHelper.add(cacheKey, cacheValue, cacheTime);
    
    if (lockValue.equals(CacheHelper.get(lockKey))) {
        CacheHelper.delete(lockKey);
    }
    
    return cacheValue;
}

缺点:最大问题是,线程相互等待,性能较差

2. 逻辑过期【不设置TTL】:

也就是说不设置Redis的过期时间,但是在数据内添加一个过期的日期,如果用户获取到该数据时,在进行判断是否是过期数据,是则进行重新构建缓存信息。

a. 总结优缺点:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值