緩存雪崩、緩存擊穿

雪崩:因某一時刻,緩存集中失效致使  java

解決辦法:加鎖  spring

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);
    }


}
相關文章
相關標籤/搜索