本文共 3797 字,大约阅读时间需要 12 分钟。
什么是缓存穿透?缓存里面不存在数据,数据库里面也不存在的数据。新的请求(例如黑客恶意攻击:https://item.jd.com/6729892714444444.html查询一个不存在商品)进来会不断查询数据库,严重可能会导致数据库服务停止。
如果数据库查询不到数据,缓存Null值的对象返回。
显然返回Null值方案存在问题,如果查询编码不存在数据,之后又新增了此编号的数据,将导致此数据永远查不到。
布隆过滤器(性能问题不用担心,可以自行查阅资料),例如将商品所有Id加入布隆过滤器,后续访问必须先经过bloom布隆过滤器判断是否存在,如果不存在就直接返回,否则放行。以下是bloom过滤器的redis实现。bloom.filter.expectedInsertions=10000000bloom.filter.fpp=0.001F// 基于Java 配置@ConfigurationProperties("bloom.filter")@Componentpublic class RedisBloomFilter { private static final String BLOOM_NAME = "bf.name"; //预计插入量 @Getter @Setter private long expectedInsertions; //可接收错误率 @Getter @Setter private double fpp; //bit数组长度 @Getter @Setter private long numBits; //hash函数数量 @Getter @Setter private int numHashFunctions; @Autowired private RedisTemplate redisTemplate; @PostConstruct public void init(){ this.numBits = optimalNumOfBits(expectedInsertions, fpp); this.numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits); } /** * 计算bit数组长度 * @return */ private long optimalNumOfBits(long n, double p){ if (p == 0){ p = Double.MIN_VALUE; } return (long)(-n * Math.log(p) / (Math.log(2) * Math.log(2))); } /** * 计算hash函数个数 * @return */ private int optimalNumOfHashFunctions(long n, long m){ return Math.max(1, (int)Math.round((double)m / n * Math.log(2))); } /** * 判断keys是否存在于集合中 * @param key * @return */ public boolean isExist(String key){ long[] indexs = getIndexs(key); List list = redisTemplate.executePipelined(new RedisCallback
/* * 系统初始化,将所有商品ID加入布隆过滤器,后续增加商品ID也加入布隆过利器 * 注意:如果是在分布式情况下,使用分布式锁限定一次创建即可 */ @PostConstruct public void init(){ Listproducts = productService.findAll(); products.forEach(p->{ redisBloomFilter.put(String.valueOf(p.getProductId())); }); }
//布隆过滤器判断商品是否存在,不存在直接返回 public Product getProductById(Long productId){ log.debug("查询商品信息id:{}", productId); //先走布隆过滤器【缓存穿透】 if (!redisBloomFilter.isExist(String.valueOf(productId))){ return null; } ....... }
转载地址:http://iccpi.baihongyu.com/