雪崩:因某一时刻,缓存集中失效导致  

解决办法:加锁  

package com.enjoy.service;

import com.enjoy.entity.Provinces;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 缓存雪崩
 */
@Service("provincesService")
public class ProvincesServiceImpl3 extends ProvincesServiceImpl implements ProvincesService{
    private static final Logger logger = LoggerFactory.getLogger(ProvincesServiceImpl3.class);
    @Resource
    private CacheManager cm;
    private ConcurrentHashMap<String, Lock> locks = new ConcurrentHashMap<>();//线程安全的

    private static final String CACHE_NAME = "province";

    //
    public Provinces detail(String provinceid) {
        // 1.从缓存中取数据
        Cache.ValueWrapper valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);
        if (valueWrapper != null) {
            logger.info("缓存中得到数据");
            return (Provinces) (valueWrapper.get());
        }

        //2.加锁排队,阻塞式锁
        doLock(provinceid);//32个省,最多只有32把锁,1000个线程
        try{//第二个线程进来了
            // 一次只有一个线程
             //双重校验,不加也没关系,无非是多刷几次库
            valueWrapper = cm.getCache(CACHE_NAME).get(provinceid);//第二个线程,能从缓存里拿到值?
            if (valueWrapper != null) {
                logger.info("缓存中得到数据");
                return (Provinces) (valueWrapper.get());//第二个线程,这里返回
            }

            Provinces provinces = super.detail(provinceid);
            // 3.从数据库查询的结果不为空,则把数据放入缓存中,方便下次查询
            if (null != provinces){
                cm.getCache(CACHE_NAME).put(provinceid, provinces);
            }
            return provinces;
        }catch(Exception e){
            return null;
        }finally{
            //4.解锁
            releaseLock(provinceid);
        }
    }

    private void releaseLock(String userCode) {
        ReentrantLock oldLock = (ReentrantLock) locks.get(userCode);
        if(oldLock !=null && oldLock.isHeldByCurrentThread()){
            oldLock.unlock();
        }
    }



    private void doLock(String lockcode) {

        //provinceid有不同的值,参数多样化
        //provinceid相同的,加一个锁,---- 不是同一个key,不能用同一个锁

        ReentrantLock newLock = new ReentrantLock();//创建一个锁

        Lock oldLock = locks.putIfAbsent(lockcode, newLock);//若已存在,则newLock直接丢弃
        if(oldLock == null){
            newLock.lock();
        }else{
            oldLock.lock();
        }
    }
}

击穿:恶意攻击  大量查询不存在的记录

解决方法:布隆过滤器

package com.enjoy.service;

import com.enjoy.entity.Provinces;
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * 缓存穿透
 */
//@Service("provincesService")
public class ProvincesServiceImpl4 extends ProvincesServiceImpl implements ProvincesService{
    private BloomFilter<String> bf =null; //等效成一个set集合

    @PostConstruct //对象创建后,自动调用本方法
    public void init(){//在bean初始化完成后,实例化bloomFilter,并加载数据
        List<Provinces> provinces = this.list();

        //当成一个SET----- 占内存,比hashset占得小很多
        bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), provinces.size());// 32个
        for (Provinces p : provinces) {
            bf.put(p.getProvinceid());
        }
    }

    @Cacheable(value = "province")
    public Provinces detail(String provinceid) {
        //先判断布隆过滤器中是否存在该值,值存在才允许访问缓存和数据库
        if(!bf.mightContain(provinceid)){
            System.out.println("非法访问--------"+System.currentTimeMillis());
            return null;
        }
        System.out.println("数据库中得到数据--------"+System.currentTimeMillis());
        Provinces provinces = super.detail(provinceid);

        return provinces;
    }

    @CachePut(value = "province",key = "#entity.provinceid")
    public Provinces update(Provinces entity) {
        super.update(entity);
        return entity;
    }

    @CacheEvict(value = "province",key = "#entity.provinceid")
    public Provinces add(Provinces entity) {
        super.add(entity);
        return entity;
    }

    @Override
    @CacheEvict("province")
    public void delete(String provinceid) {
        super.delete(provinceid);
    }


}
04-27 23:35