雪崩:因某一時刻,緩存集中失效致使 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); } }