如何在springboot專案中redis使用布隆過濾器防止快取穿透
阿新 • • 發佈:2020-11-27
上一篇部落格講到了布隆過濾器在java中的應用,這一篇說
如何在springboot專案中redis使用布隆過濾器防止快取穿透。
先引入依賴
<!--使用Redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--藉助guava的布隆過濾器--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>19.0</version> </dependency>
yml redis配置
spring:
redis:
database: 3
host: 127.0.0.1
port: 6379
password: 12345
jedis.pool.max-idle: 100
jedis.pool.max-wait: -1ms
jedis.pool.min-idle: 2
timeout: 2000ms
兩個工具類
BloomFilterHelper
package com.whrfjd.rescenter.utis; import com.google.common.base.Preconditions; import com.google.common.hash.Funnel; import com.google.common.hash.Hashing; public class BloomFilterHelper<T> { private int numHashFunctions; private int bitSize; private Funnel<T> funnel; public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) { Preconditions.checkArgument(funnel != null, "funnel不能為空"); this.funnel = funnel; // 計算bit陣列長度 bitSize = optimalNumOfBits(expectedInsertions, fpp); // 計算hash方法執行次數 numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize); } public int[] murmurHashOffset(T value) { int[] offset = new int[numHashFunctions]; long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong(); int hash1 = (int) hash64; int hash2 = (int) (hash64 >>> 32); for (int i = 1; i <= numHashFunctions; i++) { int nextHash = hash1 + i * hash2; if (nextHash < 0) { nextHash = ~nextHash; } offset[i - 1] = nextHash % bitSize; } return offset; } /** * 計算bit陣列長度 */ private int optimalNumOfBits(long n, double p) { if (p == 0) { // 設定最小期望長度 p = Double.MIN_VALUE; } int sizeOfBitArray = (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2))); return sizeOfBitArray; } /** * 計算hash方法執行次數 */ private int optimalNumOfHashFunctions(long n, long m) { int countOfHash = Math.max(1, (int) Math.round((double) m / n * Math.log(2))); return countOfHash; } }
RedisBloomFilter
package com.whrfjd.rescenter.utis; import com.google.common.base.Preconditions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; /** * @Author : JCccc * @CreateTime : 2020/4/23 * @Description : **/ @Service public class RedisBloomFilter { @Autowired private RedisTemplate redisTemplate; /** * 根據給定的布隆過濾器新增值 */ public <T> void addByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) { Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能為空"); int[] offset = bloomFilterHelper.murmurHashOffset(value); for (int i : offset) { System.out.println("key : " + key + " " + "value : " + i); redisTemplate.opsForValue().setBit(key, i, true); } } /** * 根據給定的布隆過濾器判斷值是否存在 */ public <T> boolean includeByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) { Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能為空"); int[] offset = bloomFilterHelper.murmurHashOffset(value); for (int i : offset) { System.out.println("key : " + key + " " + "value : " + i); if (!redisTemplate.opsForValue().getBit(key, i)) { return false; } } return true; } }
配置完成現在可以測試了。
redis布隆過濾器資料新增
@GetMapping("/redis/bloomFilter")
@ApiOperation("redis布隆過濾器資料新增")
public ResponseResult redisBloomFilter(){
List<String> allResourceId = resCenterDao.getAllResourceId();
for (String id : allResourceId) {
//將所有的資源id放入到布隆過濾器中
redisBloomFilter.addByBloomFilter(bloomFilterHelper,"bloom",id);
}
return new ResponseResult(ResponseEnum.SUCCESS);
}
redis布隆過濾器資源測試
@GetMapping("/redis/bloomFilter/resourceId")
@ApiOperation("redis布隆過濾器資源測試")
public ResponseResult redisBloomFilterResourceId(@RequestParam("resourceId")String resourceId){
boolean mightContain = redisBloomFilter.includeByBloomFilter(bloomFilterHelper,"bloom",resourceId);
if (!mightContain){
return new QueryResult<>(ResCenterEnum.RESOURCE_EXSIT,"");
}
return new ResponseResult(ResponseEnum.SUCCESS);
}
完成!