Redis专题详细教程(四)

缓存穿透

用户想要查询一个数据,发现redis内存数据库中没有,也就是缓存没有命中,于是向持
久层数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命
中(秒杀),于是都请求了持久层的数据,这会给持久层造成很大的压力,这时候就相当
于缓存穿透!

解决方案:
布隆过滤器,是一种数据结构,对所有可能查询的参数以hash存储,在控制层进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力

但是这种方案会存在两个问题:
1.如果空值能够被缓存起来,这就意味着,需要更多的空间存储更多的键,因为这当中
可能会有很多空值的键
2.即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的
不一致,这对于需要保持一致性的业务会有影响

缓存击穿

指一个key非常热点,在不停的扛着大并发,大并发集中对这一点进行访问,当这个key
在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了
一个洞!

解决方案:

设置热点数据永不过期
从缓存层面来看,没有设置时间,所以不会出现热点key过期后产生的问题

加互斥锁
分布式锁:保证每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大

分布式锁案例:
setnx命令(setIfAbsent #java中写法)此命令适用于实现redis分布式锁
如果为空就set值,并返回1
如果存在(不为空)不进行操作,并返回0

/**
 *
 * @desc 加锁
 * @param key
 * @param value
 * @param timeout 超时时间
 * @param autoReleaseTime 自动释放锁时间
 */
public boolean lock(String key, String value, long timeout, long autoReleaseTime) {
    boolean flag = true;

    long time = System.currentTimeMillis();
    long maxTime = time + timeout;

    // 自旋等待-如果在指定时间内还没获取到锁就退出自旋,并且设置过期时间避免死锁。
    while (!stringRedisTemplate.opsForValue().setIfAbsent(key, value) && time <= maxTime) {
        try {
            TimeUnit.MICROSECONDS.sleep(10);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            flag = false;
            break;
        }

        time = System.currentTimeMillis();
    }

    // 设置过期时间
    if (flag) {
        stringRedisTemplate.expire(key, autoReleaseTime, TimeUnit.MILLISECONDS);
    }

    return flag;
}

 /**
 *
 * @desc 解锁
 * @param key
 * @param value
 */
public void unLock(String key, String value) {
    try {
        if(StringUtils.isNotBlank(stringRedisTemplate.opsForValue().get(key))
                && stringRedisTemplate.opsForValue().get(key).equals(value)) {
            stringRedisTemplate.delete(key);
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
}


/**
 * 扣减库存
 */
public String decreaseStock(String key, String value){
    try{
        lock(key,value,6000,6000 * 2);
    }catch (Exception e){
        logger.error(e.getMessage(),e);
    }finally {
        unLock(key,value);
    }
    return "";
}

 /**
 *  测试可模拟多个线程扣减库存
 * @param skuId 商品ID
 */
public String test(String skuId) {

    decreaseStock("KEY_SKU_"+skuId, skuId);

    //线程1
    new Thread(()->{
        decreaseStock("KEY_SKU_"+skuId, skuId);
    });

    //线程2
    new Thread(()->{
        decreaseStock("KEY_SKU_"+skuId, skuId);
    });

    return "";
}

缓存雪崩

Redis专题详细教程(四)_第1张图片
解决方案:
Redis专题详细教程(四)_第2张图片

你可能感兴趣的:(redis,分布式,数据库,redis,java,缓存)