赞
踩
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class<T> clazz; static { ParserConfig.getGlobalInstance().setAutoTypeSupport(true); } public FastJsonRedisSerializer(Class<T> clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException { if (t == null) { return new byte[0]; } return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); } @Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length <= 0) { return null; } String str = new String(bytes, DEFAULT_CHARSET); return JSON.parseObject(str, clazz); } protected JavaType getJavaType(Class<?> clazz) { return TypeFactory.defaultInstance().constructType(clazz); } }
@Configuration public class RedisSerializeConfig { @Bean @SuppressWarnings(value = { "unchecked", "rawtypes" }) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>(); template.setConnectionFactory(connectionFactory); FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }
/** * 算法过程: * 1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数 * 2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0 * 3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1 * 4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。 **/ @Component public class BloomFilterHelper<T> { private int numHashFunctions; private int bitSize; private Funnel<T> funnel; private static final int NUM_BITS = (int) 1e4; private static final double RATE = 0.03;//不存在误判为存在的概率 private static void funnel(@Nullable Object o, PrimitiveSink primitiveSink) { primitiveSink.putBytes(o.toString().getBytes()); } public BloomFilterHelper() { this((Funnel) BloomFilterHelper::funnel, NUM_BITS, RATE); } public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) { this.funnel = funnel; // 计算bit数组长度 bitSize = optimalNumOfBits(expectedInsertions, fpp); // 计算hash方法执行次数 numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize); } public int[] getHashOffset() { return new int[numHashFunctions]; } 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; } return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2))); } /** * 计算hash方法执行次数 */ private int optimalNumOfHashFunctions(long n, long m) { return Math.max(1, (int) Math.round((double) m / n * Math.log(2))); } }
@Component @RequiredArgsConstructor public class RedisBloomFilter { private final RedisTemplate redisTemplate; private final BloomFilterHelper bloomFilterHelper; public void init(String bloomFilterName) { int[] offset = bloomFilterHelper.getHashOffset(); for (int i : offset) { redisTemplate.opsForValue().setBit(bloomFilterName, i, true); } } /** * 根据给定的布隆过滤器添加值 */ public <T> void add(String bloomFilterName, T value) { int[] offset = bloomFilterHelper.murmurHashOffset(value); for (int i : offset) { redisTemplate.opsForValue().setBit(bloomFilterName, i, true); } } /** * 根据给定的布隆过滤器判断值是否存在 */ public <T> boolean contains(String bloomFilterName, T value) { int[] offset = bloomFilterHelper.murmurHashOffset(value); for (int i : offset) { if (!redisTemplate.opsForValue().getBit(bloomFilterName, i)) { return false; } } return true; } }
@Component @RequiredArgsConstructor @Slf4j @SuppressWarnings(value = { "unchecked", "rawtypes" }) public class RedisCache { private final RedisTemplate redisTemplate; private final RedisBloomFilter redisBloomFilter; /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ public Boolean expire(final String key, final long timeout, final TimeUnit timeUnit) { log.info("为 Redis 的键值设置超时时间\t[{}]-[{} {}]", key, timeout, timeUnit.name()); return redisTemplate.expire(key, timeout, timeUnit); } /** * 原子设置过期时间 * @param key * @param value * @param timeout */ public <T> void execute(final String key, final T value, final long timeout, final TimeUnit timeUnit) { log.info("尝试存入 Redis\t[{}]-[{}],超时时间:[{} {}]", key, value, timeout, timeUnit.name()); redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { redisOperations.multi(); redisOperations.opsForValue().set(key, value); redisOperations.expire(key, timeout, timeUnit); return redisOperations.exec(); } }); } /** * 获得对象的剩余存活时间 * @param key 键 * @return 剩余存活时间 */ public long getKeyTTL(final String key, final TimeUnit timeUnit) { int ttl = Math.toIntExact(redisTemplate.opsForValue().getOperations().getExpire(key)); String message = null; switch (ttl) { case -1: message = "没有设置过期时间"; break; case -2: message = "key不存在"; break; default: message = ttl + " " + TimeUnit.SECONDS.name(); break; } log.info("查询 Redis key[{}] 剩余存活时间:{}", key, message); return TimeUnit.SECONDS.convert(ttl, timeUnit); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */ public <T> void setCacheObject(final String key, final T value) { log.info("存入 Redis\t[{}]-[{}]", key, value); redisTemplate.opsForValue().set(key, value); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timout 超时时间 */ public <T> void setCacheObject(final String key, final T value, final long timout, final TimeUnit timeUnit) { log.info("存入 Redis\t[{}]-[{}],超时时间:[{} {}]", key, value, timout, timeUnit.name()); redisTemplate.opsForValue().set(key, value, timout, timeUnit); } /** * 获取键值 * @param key 键 * @return 键对应的值,并封装成 Optional 对象 * @param <T> */ public <T> Optional<T> getCacheObject(final String key) { T value = (T) redisTemplate.opsForValue().get(key); log.info("查询 Redis\t[{}]-[{}]", key, value); return Optional.ofNullable(value); } /** * 让指定 Redis 键值进行自减 * @param key 键 * @return 自减后的值 */ public long decrementCacheNumber(final String key) { long number = redisTemplate.opsForValue().decrement(key); log.info("Redis key[{}] 自减后:{}", key, number); return number; } /** * 让指定 Redis 键值进行自增 * @param key 键 * @return 自增后的值 */ public long incrementCacheNumber(final String key) { long number = redisTemplate.opsForValue().increment(key); log.info("Redis key[{}] 自增后:{}", key, number); return number; } /** * 初始化布隆过滤器 * @param bloomFilterName */ public void initBloomFilter(final String bloomFilterName) { log.info("初始化布隆过滤器[{}]", bloomFilterName); redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { redisOperations.multi(); redisBloomFilter.init(bloomFilterName); return redisOperations.exec(); } }); } /** * 初始化布隆过滤器 * @param bloomFilterName * @param timeout * @param timeUnit */ public void initBloomFilter(final String bloomFilterName, final long timeout, final TimeUnit timeUnit) { redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { redisOperations.multi(); redisBloomFilter.init(bloomFilterName); expire(bloomFilterName, timeout, timeUnit); return redisOperations.exec(); } }); } /** * 加入布隆过滤器 * @param bloomFilterName 隆过滤器的名字 * @param key key 键 */ public <T> void addToBloomFilter(final String bloomFilterName, final T key) { log.info("加入布隆过滤器[{}]\tkey[{}]", bloomFilterName, key); redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { redisOperations.multi(); redisBloomFilter.add(bloomFilterName, key); return redisOperations.exec(); } }); } /** * 布隆过滤器是否存在该键值 * @param bloomFilterName 布隆过滤器的名字 * @param key 键 * @return 键是否存在 */ public <T> boolean containsInBloomFilter(final String bloomFilterName, final T key) { boolean flag = redisBloomFilter.contains(bloomFilterName, key); log.info("key[{}]\t是否存在于布隆过滤器[{}]:\t{}", key, bloomFilterName, flag); return flag; } /** * 缓存Map * * @param key * @param data */ public <K, T> void setCacheMap(final String key, final Map<K, T> data) { if (Objects.nonNull(data)) { log.info("Map 存入 Redis\t[{}]-[{}]", key, data); redisTemplate.opsForHash().putAll(key, data); } } /** * 缓存Map * * @param key * @param data */ public <K, T> void setCacheMap(final String key, final Map<K, T> data, long timeout, final TimeUnit timeUnit) { if (Objects.nonNull(data)) { Map<String, T> map = new HashMap<>(); data.entrySet().stream().parallel().forEach(entry -> { map.put(entry.getKey().toString(), entry.getValue()); }); log.info("尝试存入 Redis\t[{}]-[{}] 超时时间:[{} {}]", key, map, timeout, timeUnit.name()); redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { redisOperations.multi(); redisTemplate.opsForHash().putAll(key, map); expire(key, timeout, timeUnit); return redisOperations.exec(); } }); } } /** * 获得缓存的Map * * @param key * @return */ public <K, T> Optional<Map<K, T>> getCacheMap(final String key) { Map<K, T> data = redisTemplate.opsForHash().entries(key); data = data.size() == 0 ? null: data; log.info("获取 Redis 中的 Map 缓存\t[{}]-[{}]", key, data); return Optional.ofNullable(data); } /** * 往Hash中存入数据 * * @param key Redis键 * @param hashKey Hash键 * @param value 值 */ public <K, T> void setCacheMapValue(final String key, final K hashKey, final T value) { log.info("存入 Redis 的某个 Map\t[{}.{}]-[{}]", key, hashKey, value); redisTemplate.opsForHash().put(key, hashKey.toString(), value); } /** * 获取Hash中的数据 * * @param key Redis键 * @param hashKey Hash键 * @return Hash中的对象 */ public <K, T> Optional<T> getCacheMapValue(final String key, final K hashKey) { T value = (T) redisTemplate.opsForHash().get(key, hashKey.toString()); log.info("获取 Redis 中的 Map 的键值\t[{}.{}]-[{}]", key, hashKey, value); return Optional.ofNullable(value); } /** * 删除Hash中的数据 * * @param key * @param hashKey */ public <K> void delCacheMapValue(final String key, final K hashKey) { log.info("删除 Redis 中的 Map 的键值\tkey[{}.{}]", key, hashKey); redisTemplate.opsForHash().delete(key, hashKey.toString()); } /** * 让指定 HashMap 的键值进行自减 * @param key HashMap的名字 * @param hashKey HashMap的一个键 * @return 自减后的值 */ public <K> long decrementCacheMapNumber(final String key, final K hashKey) { long number = redisTemplate.opsForHash().increment(key, hashKey.toString(), -1); log.info("Redis key[{}.{}] 自减后:{}", key, hashKey, number); return number; } /** * 让指定 HashMap 的键值进行自增 * @param key HashMap的名字 * @param hashKey HashMap的一个键 * @return 自增后的值 */ public <K> long incrementCacheMapNumber(final String key, final K hashKey) { long number = redisTemplate.opsForHash().increment(key, hashKey.toString(), +1); log.info("Redis key[{}.{}] 自增后:{}", key, hashKey, number); return number; } /** * 删除单个对象 * @param key */ public boolean deleteObject(final String key) { log.info("删除 Redis 的键值\tkey[{}]", key); return redisTemplate.delete(key); } }
伪代码:
redisCache.getCacheObject(redisKey)
.orElseGet(() -> {
/* balabala ....*/
// 可能查询有误,所以这里也可能没法获取到data(为null),也就可能没必要设置缓存
redisCache.setCacheObject(redisKey, data, ttl, unit);
/* balabala ....*/
return data;
}));
现实示例:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。