雪崩:因某一时刻,缓存集中失效导致
解决办法:加锁
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); } }