찾다
데이터 베이스RedisSpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

자세한 내용은 다음과 같습니다.

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

악성 트래픽 침투란 무엇입니까

Redis에는 이메일을 키로 하는 사용자 등록 이메일 세트가 포함되어 있고 이는 User 테이블의 일부 필드에 해당한다고 가정합니다. DB.

일반적으로 캐시에서 데이터를 읽는 것이 더 빠르기 때문에 먼저 사용자가 Redis의 멤버인지 확인합니다. 이 멤버가 캐시에 없으면 DB에서 쿼리합니다.

이제 다양한 IP에서 수천만 건의 요청이 있다고 상상해 보세요(아무것도 없다고 생각하지 마세요. 공격 비용이 매우 낮기 때문에 2018년과 2019년에 발생했습니다.) 키를 사용하여 웹 사이트에 액세스합니다.

  • 요청이 웹 서버에 도착합니다.

  • 요청은 애플리케이션 계층->마이크로서비스 계층으로 전달됩니다.

  • 요청은 검색을 위해 Redis로 이동합니다. 이 Key는 Redis에 존재하지 않습니다.

  • 그러면 요청이 DB 계층에 도달하고 DB 연결이 설정된 후 쿼리가 수행됩니다.

수천만 개의 DB인지 연결 요청은 Redis가 감당할 수 있는지 여부는 문제가 되지 않습니다. DB도 즉시 중단되기 때문입니다. 이는 "캐시 고장"이라고도 알려진 "Redis 침투"입니다. 이는 캐시나 DB를 폭파시켜 일련의 "눈사태 효과"를 유발합니다.

방지 방법

Bloom 필터를 사용하여 사용자 테이블의 모든 주요 쿼리 필드를 Redis Bloom 필터에 넣을 수 있습니다. 어떤 사람들은 '이건 미친 게 아니야, 내 회원이 4천만 명이나 된다고?'라고 말할 것입니다. 그래서 뭐!

Redis에 4,000명의 회원을 넣는 것은 과장입니다. 일부 웹사이트에는 8천만 명 또는 1억 명의 회원이 있습니다. 그래서 Redis에 직접 넣어달라고 하지 않고 Bloom 필터에 넣어달라고 했는데요!

키와 값은 Bloom 필터에 직접 입력되지 않습니다. 저장되는 내용은 다음과 같습니다.

BloomFilter는 1970년 Burton Howard Bloom이 제안한 공간 효율적인 확률 데이터 구조입니다. 일반적으로 요소가 집합에 있는지 여부를 확인하는 데 사용됩니다. 뛰어난 공간 효율성에도 불구하고 잘못된 긍정 오류가 발생할 수 있습니다.

False positive&&False negatives
BloomFiter는 공간 효율성을 위해 특정 정확도를 희생하기 때문입니다. 그래서 False positive 문제가 발생합니다.

거짓양성
BloomFilter는 요소가 컬렉션에 있다고 판단할 때 특정 오류율을 가지게 됩니다. 이 오류율을 거짓양성이라고 합니다. 일반적으로 fpp로 축약됩니다.

False negatives
BloomFilter는 요소가 세트에 없을 때 오류율을 결정합니다. BloomFilter는 요소가 컬렉션에 없다고 판단한 경우 해당 요소가 컬렉션에 없어야 합니다. 따라서 거짓음성 확률은 0이다.

BloomFilter는 길이가 m 비트인 바이트 배열을 사용하고 k 해시 함수를 사용하고 요소를 추가합니다. k 해시를 통해 요소를 바이트 배열의 k 위치에 매핑하고 해당 위치의 바이트를 1로 설정합니다.
요소 존재 여부 쿼리: 요소를 k번 해시하여 k 위치에 해당하는 비트가 1이면 존재하는 것으로 간주되고, 그렇지 않으면 존재하지 않는 것으로 간주됩니다.

비트를 저장하기 때문에 데이터의 양은 매우 작습니다. 이 블로그를 작성할 때 Redis 블룸 필터에 100만 개의 이메일 메시지를 삽입했는데 그 크기는 3Mb 미만이었습니다.

Bloom Filter에는 몇 가지 핵심 값이 있습니다. 이 값을 기반으로 얼마나 많은 데이터를 넣을지, 잘못된 피해율이 있을 때 얼마나 많은 시스템 리소스를 차지할지 대략적으로 계산할 수 있습니다. 이 알고리즘의 URL은 https://krisives.github.io/bloom-calculator/입니다. 100만 개의 데이터를 입력하고 사고 피해율을 0.001%라고 가정합니다. 보세요. 시스템 메모리 리소스가 자동으로 결정됩니다. Redis를 신청해야 하는 사람은 몇 명입니까?

이런 재해율을 어떻게 해결할까요? 매우 간단합니다. 사고가 발생하면 기업이나 운영업체에서 사고 사고율을 보고합니다. 이때 100만 개의 데이터에 비해 작은 화이트리스트만 추가하면 됩니다. Bloom 필터의 쿼리 속도는 일반적으로 80~100밀리초 내에 쿼리 결과를 반환하여 호출 측에 키가 있는지 알려줄 수 있습니다.

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

Bloom 필터의 또 다른 사용 사례

Python 크롤러를 사용하여 4억 개의 URL을 크롤링했다고 가정해 보겠습니다. 중복된 항목을 제거해야 합니까?

보세요, 이 시나리오에서는 Bloom 필터가 사용되었습니다.

Redis BloomFilter 여정을 시작하겠습니다.

Redis용 Bloom Filter 설치

Redis는 4.0의 Bloom Filter만 지원하므로 이 예에서는 Redis5.4를 사용합니다.

Redis의 블룸 필터 다운로드 주소는 다음과 같습니다: https://github.com/RedisLabsModules/redisbloom.git

git clone https://github.com/RedisLabsModules/redisbloom.git
cd redisbloom
make # 编译

Redis가 시작될 때 블룸 필터를 로드하는 방법에는 두 가지가 있습니다:

수동 로딩:

redis-server --loadmodule ./redisbloom/rebloom.so

시작할 때마다 자동 로딩:

Redis의 redis.conf 파일을 편집하고 다음을 추가합니다:

loadmodule /soft/redisbloom/redisbloom.so

이렇게:

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

在Redis里使用Bloom Filter

基本指令:

bf.reserve {key} {error_rate} {size}

127.0.0.1:6379> bf.reserve userid 0.01 100000
OK

上面这条命令就是:创建一个空的布隆过滤器,并设置一个期望的错误率和初始大小。{error_rate}过滤器的错误率在0-1之间,如果要设置0.1%,则应该是0.001。该数值越接近0,内存消耗越大,对cpu利用率越高

bf.add {key} {item}

127.0.0.1:6379> bf.add userid '181920'
(integer) 1

上面这条命令就是:往过滤器中添加元素。如果key不存在,过滤器会自动创建。

bf.exists {key} {item}

127.0.0.1:6379> bf.exists userid '101310299'
(integer) 1

这个命令的作用是检查 Bloom 过滤器中是否包含指定 key 的值。存在:返回1,不存在:返回0。

结合SpringBoot使用

网上很多写的都是要么是直接使用jedis来操作的,或者是java里execute一个外部进程来调用Redis的bloom filter指令的。许多代码调试不通或只能达到helloworld级别,无法用于生产级别的应用。

笔者给出的代码保障读者完全可用!

笔者不是数学家,因此就借用了google的guava包来实现了核心算法,核心代码如下:

BloomFilterHelper.java

package org.sky.platform.util;
 
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;
		bitSize = optimalNumOfBits(expectedInsertions, fpp);
		numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
	}
 
	int[] murmurHashOffset(T value) {
		int[] offset = new int[numHashFunctions];
 
		long hash74 = Hashing.murmur3_128().hashObject(value, funnel).asLong();
		int hash2 = (int) hash74;
		int hash3 = (int) (hash74 >>> 32);
		for (int i = 1; i <= numHashFunctions; i++) {
			int nextHash = hash2 + i * hash3;
			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)));
	}
}

下面放出全工程解说,我已经将源码上传到了我的git上了,确保读者可用,源码地址在这:https://github.com/mkyuangithub/mkyuangithub.git

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

搭建spring boot工程

项目Redis配置

我们在redis-practice工程里建立一个application.properties文件,内容如下:

spring.redis.database=0  
spring.redis.host=192.168.56.101
spring.redis.port=6379
spring.redis.password=111111
spring.redis.pool.max-active=10  
spring.redis.pool.max-wait=-1  
spring.redis.pool.max-idle=10 
spring.redis.pool.min-idle=0  
spring.redis.timeout=1000

以上这个是demo环境的配置。

我们此处依旧使用的是在前一篇springboot+nacos+dubbo实现异常统一管理中的xxx-project->sky-common->nacos-parent的依赖结构。

在redis-practice工程的org.sky.config包中放入redis的springboot配置

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

RedisConfig.java

package org.sky.config;
 
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
 
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
	/**
	 * 选择redis作为默认缓存工具
	 * 
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public CacheManager cacheManager(RedisTemplate redisTemplate) {
		RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
		return rcm;
	}
 
	/**
	 * retemplate相关配置
	 * 
	 * @param factory
	 * @return
	 */
	@Bean
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
 
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		// 配置连接工厂
		template.setConnectionFactory(factory);
 
		// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
		Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
 
		ObjectMapper om = new ObjectMapper();
		// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jacksonSeial.setObjectMapper(om);
 
		// 值采用json序列化
		template.setValueSerializer(jacksonSeial);
		// 使用StringRedisSerializer来序列化和反序列化redis的key值
		template.setKeySerializer(new StringRedisSerializer());
 
		// 设置hash key 和value序列化模式
		template.setHashKeySerializer(new StringRedisSerializer());
		template.setHashValueSerializer(jacksonSeial);
		template.afterPropertiesSet();
 
		return template;
	}
 
	/**
	 * 对hash类型的数据操作
	 *
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForHash();
	}
 
	/**
	 * 对redis字符串类型数据操作
	 *
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForValue();
	}
 
	/**
	 * 对链表类型的数据操作
	 *
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForList();
	}
 
	/**
	 * 对无序集合类型的数据操作
	 *
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForSet();
	}
 
	/**
	 * 对有序集合类型的数据操作
	 *
	 * @param redisTemplate
	 * @return
	 */
	@Bean
	public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForZSet();
	}
}

这个配置除实现了springboot自动发现redis在application.properties中的配置外我们还添加了不少redis基本的数据结构的操作的封装。

我们为此还要再封装一套Redis Util小组件,它们位于sky-common工程中

RedisUtil.java

package org.sky.platform.util;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
 
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.base.Preconditions;
import org.springframework.data.redis.core.RedisTemplate;
 
@Component
public class RedisUtil {
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
 
	/**
	 * 默认过期时长,单位:秒
	 */
	public static final long DEFAULT_EXPIRE = 60 * 60 * 24;
 
	/**
	 * 不设置过期时长
	 */
	public static final long NOT_EXPIRE = -1;
 
	public boolean existsKey(String key) {
		return redisTemplate.hasKey(key);
	}
 
	/**
	 * 重名名key,如果newKey已经存在,则newKey的原值被覆盖
	 *
	 * @param oldKey
	 * @param newKey
	 */
	public void renameKey(String oldKey, String newKey) {
		redisTemplate.rename(oldKey, newKey);
	}
 
	/**
	 * newKey不存在时才重命名
	 *
	 * @param oldKey
	 * @param newKey
	 * @return 修改成功返回true
	 */
	public boolean renameKeyNotExist(String oldKey, String newKey) {
		return redisTemplate.renameIfAbsent(oldKey, newKey);
	}
 
	/**
	 * 删除key
	 *
	 * @param key
	 */
	public void deleteKey(String key) {
		redisTemplate.delete(key);
	}
 
	/**
	 * 删除多个key
	 *
	 * @param keys
	 */
	public void deleteKey(String... keys) {
		Set<String> kSet = Stream.of(keys).map(k -> k).collect(Collectors.toSet());
		redisTemplate.delete(kSet);
	}
 
	/**
	 * 删除Key的集合
	 *
	 * @param keys
	 */
	public void deleteKey(Collection<String> keys) {
		Set<String> kSet = keys.stream().map(k -> k).collect(Collectors.toSet());
		redisTemplate.delete(kSet);
	}
 
	/**
	 * 设置key的生命周期
	 *
	 * @param key
	 * @param time
	 * @param timeUnit
	 */
	public void expireKey(String key, long time, TimeUnit timeUnit) {
		redisTemplate.expire(key, time, timeUnit);
	}
 
	/**
	 * 指定key在指定的日期过期
	 *
	 * @param key
	 * @param date
	 */
	public void expireKeyAt(String key, Date date) {
		redisTemplate.expireAt(key, date);
	}
 
	/**
	 * 查询key的生命周期
	 *
	 * @param key
	 * @param timeUnit
	 * @return
	 */
	public long getKeyExpire(String key, TimeUnit timeUnit) {
		return redisTemplate.getExpire(key, timeUnit);
	}
 
	/**
	 * 将key设置为永久有效
	 *
	 * @param key
	 */
	public void persistKey(String key) {
		redisTemplate.persist(key);
	}
 
	/**
	 * 根据给定的布隆过滤器添加值
	 */
	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) {
			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) {
			if (!redisTemplate.opsForValue().getBit(key, i)) {
				return false;
			}
		}
 
		return true;
	}
}

RedisKeyUtil.java

package org.sky.platform.util;
 
public class RedisKeyUtil {
	/**
	 * redis的key 形式为: 表名:主键名:主键值:列名
	 *
	 * @param tableName     表名
	 * @param majorKey      主键名
	 * @param majorKeyValue 主键值
	 * @param column        列名
	 * @return
	 */
	public static String getKeyWithColumn(String tableName, String majorKey, String majorKeyValue, String column) {
		StringBuffer buffer = new StringBuffer();
		buffer.append(tableName).append(":");
		buffer.append(majorKey).append(":");
		buffer.append(majorKeyValue).append(":");
		buffer.append(column);
		return buffer.toString();
	}
 
	/**
	 * redis的key 形式为: 表名:主键名:主键值
	 *
	 * @param tableName     表名
	 * @param majorKey      主键名
	 * @param majorKeyValue 主键值
	 * @return
	 */
	public static String getKey(String tableName, String majorKey, String majorKeyValue) {
		StringBuffer buffer = new StringBuffer();
		buffer.append(tableName).append(":");
		buffer.append(majorKey).append(":");
		buffer.append(majorKeyValue).append(":");
		return buffer.toString();
	}
}

然后就是制作 redis里如何使用BloomFilter的BloomFilterHelper.java了,它也位于sky-common文件夹,源码如上已经贴了,因此此处就不再作重复。

最后我们在sky-common里放置一个UserVO用于演示

UserVO.java

package org.sky.vo;
 
import java.io.Serializable;
 
public class UserVO implements Serializable {
 
	private String name;
	private String address;
	private Integer age;
	private String email = "";
 
	public String getEmail() {
		return email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getAddress() {
		return address;
	}
 
	public void setAddress(String address) {
		this.address = address;
	}
 
	public Integer getAge() {
		return age;
	}
 
	public void setAge(Integer age) {
		this.age = age;
	}
 
}

下面给出我们所有gitrepo里依赖的nacos-parent的pom.xml文件内容,此次我们增加了对于“spring-boot-starter-data-redis”,它跟着我们的全局springboot版本走:

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

parent工程的pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.sky.demo</groupId>
	<artifactId>nacos-parent</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>pom</packaging>
	<description>Demo project for Spring Boot Dubbo Nacos</description>
	<modules>
	</modules>
 
	<properties>
		<java.version>1.8</java.version>
		<spring-boot.version>1.5.15.RELEASE</spring-boot.version>
		<dubbo.version>2.7.3</dubbo.version>
		<curator-framework.version>4.0.1</curator-framework.version>
		<curator-recipes.version>2.8.0</curator-recipes.version>
		<druid.version>1.1.20</druid.version>
		<guava.version>27.0.1-jre</guava.version>
		<fastjson.version>1.2.59</fastjson.version>
		<dubbo-registry-nacos.version>2.7.3</dubbo-registry-nacos.version>
		<nacos-client.version>1.1.4</nacos-client.version>
		<mysql-connector-java.version>5.1.46</mysql-connector-java.version>
		<disruptor.version>3.4.2</disruptor.version>
		<aspectj.version>1.8.13</aspectj.version>
		<nacos-service.version>0.0.1-SNAPSHOT</nacos-service.version>
		<spring.data.redis>1.8.14-RELEASE</spring.data.redis>
		<skycommon.version>0.0.1-SNAPSHOT</skycommon.version>
		<maven.compiler.source>${java.version}</maven.compiler.source>
		<maven.compiler.target>${java.version}</maven.compiler.target>
		<compiler.plugin.version>3.8.1</compiler.plugin.version>
		<war.plugin.version>3.2.3</war.plugin.version>
		<jar.plugin.version>3.1.2</jar.plugin.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	</properties>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-web</artifactId>
				<version>${spring-boot.version}</version>
			</dependency>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring-boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.apache.dubbo</groupId>
				<artifactId>dubbo-spring-boot-starter</artifactId>
				<version>${dubbo.version}</version>
				<exclusions>
					<exclusion>
						<groupId>org.slf4j</groupId>
						<artifactId>slf4j-log4j12</artifactId>
					</exclusion>
				</exclusions>
			</dependency>
			<dependency>
				<groupId>org.apache.dubbo</groupId>
				<artifactId>dubbo</artifactId>
				<version>${dubbo.version}</version>
			</dependency>
			<dependency>
				<groupId>org.apache.curator</groupId>
				<artifactId>curator-framework</artifactId>
				<version>${curator-framework.version}</version>
			</dependency>
 
			<dependency>
				<groupId>org.apache.curator</groupId>
				<artifactId>curator-recipes</artifactId>
				<version>${curator-recipes.version}</version>
			</dependency>
			<dependency>
				<groupId>mysql</groupId>
				<artifactId>mysql-connector-java</artifactId>
				<version>${mysql-connector-java.version}</version>
			</dependency>
			<dependency>
				<groupId>com.alibaba</groupId>
				<artifactId>druid</artifactId>
				<version>${druid.version}</version>
			</dependency>
			<dependency>
				<groupId>com.lmax</groupId>
				<artifactId>disruptor</artifactId>
				<version>${disruptor.version}</version>
			</dependency>
			<dependency>
				<groupId>com.google.guava</groupId>
				<artifactId>guava</artifactId>
				<version>${guava.version}</version>
			</dependency>
			<dependency>
				<groupId>com.alibaba</groupId>
				<artifactId>fastjson</artifactId>
				<version>${fastjson.version}</version>
			</dependency>
			<dependency>
				<groupId>org.apache.dubbo</groupId>
				<artifactId>dubbo-registry-nacos</artifactId>
				<version>${dubbo-registry-nacos.version}</version>
			</dependency>
			<dependency>
				<groupId>com.alibaba.nacos</groupId>
				<artifactId>nacos-client</artifactId>
				<version>${nacos-client.version}</version>
			</dependency>
			<dependency>
				<groupId>org.aspectj</groupId>
				<artifactId>aspectjweaver</artifactId>
				<version>${aspectj.version}</version>
			</dependency>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-data-redis</artifactId>
				<version>${spring-boot.version}</version>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>${compiler.plugin.version}</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>${war.plugin.version}</version>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>${jar.plugin.version}</version>
			</plugin>
		</plugins>
	</build>
</project>

sky-common中pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.sky.demo</groupId>
	<artifactId>skycommon</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<parent>
		<groupId>org.sky.demo</groupId>
		<artifactId>nacos-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<dependencies>
 
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-framework</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
		</dependency>
 
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.spockframework</groupId>
			<artifactId>spock-core</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.spockframework</groupId>
			<artifactId>spock-spring</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
 
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
 
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
		</dependency>
		<dependency>
			<groupId>com.lmax</groupId>
			<artifactId>disruptor</artifactId>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
	</dependencies>
</project>

到此,我们的springboot+redis基本框架、util类、bloomfilter组件搭建完毕,接下来我们重点说我们的demo工程

Demo工程:redis-practice说明

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

pom.xml文件,它依赖于nacos-parent同时还引用了sky-common

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.sky.demo</groupId>
	<artifactId>redis-practice</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>Demo Redis Advanced Features</description>
	<parent>
		<groupId>org.sky.demo</groupId>
		<artifactId>nacos-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
 
	<dependencies>
 
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.dubbo</groupId>
			<artifactId>dubbo</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-framework</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.spockframework</groupId>
			<artifactId>spock-core</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.spockframework</groupId>
			<artifactId>spock-spring</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
			<exclusion>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-tomcat</artifactId>
			</exclusion>
		</dependency>
 
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
		</dependency>
		<dependency>
			<groupId>com.lmax</groupId>
			<artifactId>disruptor</artifactId>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
		</dependency>
		<dependency>
			<groupId>org.sky.demo</groupId>
			<artifactId>skycommon</artifactId>
			<version>${skycommon.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
	</dependencies>
	<build>
		<sourceDirectory>src/main/java</sourceDirectory>
		<testSourceDirectory>src/test/java</testSourceDirectory>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
		<resources>
			<resource>
				<directory>src/main/resources</directory>
			</resource>
			<resource>
				<directory>src/main/webapp</directory>
				<targetPath>META-INF/resources</targetPath>
				<includes>
					<include>**/**</include>
				</includes>
			</resource>
			<resource>
				<directory>src/main/resources</directory>
				<filtering>true</filtering>
				<includes>
					<include>application.properties</include>
					<include>application-${profileActive}.properties</include>
				</includes>
			</resource>
		</resources>
	</build>
</project>

用于启动的Application.java

package org.sky;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;
 
@EnableTransactionManagement
@ComponentScan(basePackages = { "org.sky" })
@EnableAutoConfiguration
public class Application {
 
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

然后我们制作了一个controller名为UserController,该controller里有两个方法:

  • public ResponseEntity addUser(@RequestBody String params),该方法用于接受来自外部的api post然后把一条email地址塞入redis的bloomfilter中;

  • public ResponseEntity findEmailInBloom(@RequestBody String params),该方法用于接受来自外部的api post然后去redis的bloomfilter中验证是否外部输入的user信息中的email地址在上百万的email记录中存在;

以此来完成验证塞入redis的bloom filter中上百万条记录占用了多少内存以及使用bloom filter查询一条记录有多快。

UserController.java

package org.sky.controller;
 
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
 
import javax.annotation.Resource;
 
import org.sky.platform.util.BloomFilterHelper;
import org.sky.platform.util.RedisUtil;
import org.sky.vo.UserVO;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Charsets;
import com.google.common.hash.Funnel;
 
@RestController
@RequestMapping("user")
public class UserController extends BaseController {
 
	@Resource
	private RedisTemplate redisTemplate;
 
	@Resource
	private RedisUtil redisUtil;
 
	@PostMapping(value = "/addEmailToBloom", produces = "application/json")
	public ResponseEntity<String> addUser(@RequestBody String params) {
		ResponseEntity<String> response = null;
		String returnResultStr;
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		Map<String, Object> result = new HashMap<>();
		try {
			JSONObject requestJsonObj = JSON.parseObject(params);
			UserVO inputUser = getUserFromJson(requestJsonObj);
			BloomFilterHelper<String> myBloomFilterHelper = new BloomFilterHelper<>((Funnel<String>) (from,
					into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 1500000, 0.00001);
			redisUtil.addByBloomFilter(myBloomFilterHelper, "email_existed_bloom", inputUser.getEmail());
			result.put("code", HttpStatus.OK.value());
			result.put("message", "add into bloomFilter successfully");
			result.put("email", inputUser.getEmail());
			returnResultStr = JSON.toJSONString(result);
			logger.info("returnResultStr======>" + returnResultStr);
			response = new ResponseEntity<>(returnResultStr, headers, HttpStatus.OK);
		} catch (Exception e) {
			logger.error("add a new product with error: " + e.getMessage(), e);
			result.put("message", "add a new product with error: " + e.getMessage());
			returnResultStr = JSON.toJSONString(result);
			response = new ResponseEntity<>(returnResultStr, headers, HttpStatus.INTERNAL_SERVER_ERROR);
		}
		return response;
	}
 
	@PostMapping(value = "/checkEmailInBloom", produces = "application/json")
	public ResponseEntity<String> findEmailInBloom(@RequestBody String params) {
		ResponseEntity<String> response = null;
		String returnResultStr;
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		Map<String, Object> result = new HashMap<>();
		try {
			JSONObject requestJsonObj = JSON.parseObject(params);
			UserVO inputUser = getUserFromJson(requestJsonObj);
			BloomFilterHelper<String> myBloomFilterHelper = new BloomFilterHelper<>((Funnel<String>) (from,
					into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 1500000, 0.00001);
			boolean answer = redisUtil.includeByBloomFilter(myBloomFilterHelper, "email_existed_bloom",
					inputUser.getEmail());
			logger.info("answer=====" + answer);
			result.put("code", HttpStatus.OK.value());
			result.put("email", inputUser.getEmail());
			result.put("exist", answer);
			returnResultStr = JSON.toJSONString(result);
			logger.info("returnResultStr======>" + returnResultStr);
			response = new ResponseEntity<>(returnResultStr, headers, HttpStatus.OK);
		} catch (Exception e) {
			logger.error("add a new product with error: " + e.getMessage(), e);
			result.put("message", "add a new product with error: " + e.getMessage());
			returnResultStr = JSON.toJSONString(result);
			response = new ResponseEntity<>(returnResultStr, headers, HttpStatus.INTERNAL_SERVER_ERROR);
		}
		return response;
	}
 
	private UserVO getUserFromJson(JSONObject requestObj) {
		String userName = requestObj.getString("username");
		String userAddress = requestObj.getString("address");
		String userEmail = requestObj.getString("email");
		int userAge = requestObj.getInteger("age");
		UserVO u = new UserVO();
		u.setName(userName);
		u.setAge(userAge);
		u.setEmail(userEmail);
		u.setAddress(userAddress);
		return u;
 
	}
}

注意UserController中的BloomFilterHelper的用法,我在Redis的bloomfilter里申明了可以用于存放150万数据的空间。如果存和的数据大于了你预先申请的空间怎么办?那么它会增加“误伤率”

下面我们把这个项目运行起来看看效果吧。

运行redis-practice工程

运行起来后

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

我们可以使用postman先来做个小实验

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

我们使用"、addEmailToBloom"往redis bloom filter里插入了一个“yumi@yahoo.com”的email。

接下来我们会使用“/checkEmailInBloom”来验证这个email地址是否存在

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

我们使用redisclient连接上我们的redis查看,这个值确实也是插入进了bloom filter了。

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

使用压测工具喂120万条数据进入Redis Bloomfilter看实际效果

接下来,我们用jmeter对着“/addEmailToBloom”喂上个120万左右数据进去,然后我们再来看bloom filter在120万email按照布隆算 法喂进去后我们的系统是如何表现的。

我这边使用的是apache-jmeter5.0,为了偷懒,我用了apache-jmeter里的_RandomString函数来动态创造16位字符长度的email。这边用户名、地址信息都是恒定,就是email是每次不一样,都是一串16位的随机字符+“@163.com”。

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

jmeter中BeanShell产生16位字符随机组成email的函数

useremail="${__RandomString(16,abcdefghijklmnop,myemail)}"+"@163.com";
vars.put("random_email",useremail);

jmeter测试计划设置成了75个线程,连续运行30分钟(实践上笔者运行了3个30分钟,因为是demo环境,30分钟每次插大概40万条数据进去吧)

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

jmeter post请求

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

然后我们使用jmeter命令行来运行这个测试计划:

jmeter -n -t add_randomemail_to_bloom.jmx -l add_email_to_bloom\report\03-result.csv -j add_email_to_bloom\logs\03-log.log -e -o add_email_to_bloom\html_report_3

它代表:

  • -t 指定jmeter执行计划文件所在路径;

  • -l 生成report的目录,这个目录如果不存在则创建 ,必须是一个空目录;

  • -j 生成log的目录,这个目录如果不存在则创建 ,必须是一个空目录;

  • -e 生成html报告,它配合着-o参数一起使用;

  • -o 生成html报告所在的路径,这个目录如果不存在则创建 ,必须是一个空目录;

回车后它就开始运行了

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

一直执行到这个过程全部结束,跳出command命令符为止。

我们查看我们用-e -o生成的jmeter html报告,前面说过了,我一共运行了3次,第一次是10分钟70059条数据 ,第二次是30分钟40多万条数据 ,第三次是45他钟70多万条数据。我共计插入了1,200,790条email。

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

而这120万数据总计在redis中占用内存不超过8mb,见下面demo环境的zabbix录制的记录

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

120万条数据插进去后,我们接着从我们的log4j的输出中随便找一条logger.info住的email如:egpoghnfjekjajdo@163.com来看一下,redis bloomfilter找到这条记录的表现如何,76ms,我运行了多次,平均在80ms左右:

SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.

通过上面这么一个实例,大家可以看到把email以hash后并以bit的形式存入bloomfilter后,它占用的内存是多么的小,而查询效率又是多么的高。

往往在生产上,我们经常会把上千万或者是上亿的记录"load"进bloomfilter,然后拿它去做“防击穿”或者是去重的动作。

bloomfilter에 존재하지 않는 키가 클라이언트에 직접 false를 반환하는 한, nginx, cdn, waf 및 인터페이스 레이어 캐싱의 동적 확장을 통해 실제로 전체 웹사이트에서 6자리 또는 심지어 7자리 동시성도 가능합니다.

위 내용은 SpringBoot+Redis Bloom 필터는 악성 트래픽이 캐시에 침투하는 것을 방지합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 亿速云에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
Redis vs. SQL 데이터베이스 : 주요 차이점Redis vs. SQL 데이터베이스 : 주요 차이점Apr 25, 2025 am 12:02 AM

Redis와 SQL 데이터베이스의 주요 차이점은 Redis가 고성능 및 유연성 요구 사항에 적합한 메모리 데이터베이스라는 것입니다. SQL 데이터베이스는 관계형 데이터베이스로 복잡한 쿼리 및 데이터 일관성 요구 사항에 적합합니다. 구체적으로, 1) Redis는 고속 데이터 액세스 및 캐싱 서비스를 제공하고 캐싱 및 실시간 데이터 처리에 적합한 여러 데이터 유형을 지원합니다. 2) SQL 데이터베이스는 테이블 구조를 통한 데이터를 관리하고 복잡한 쿼리 및 트랜잭션 처리를 지원하며 데이터 일관성이 필요한 전자 상거래 및 금융 시스템과 같은 시나리오에 적합합니다.

Redis : 데이터 저장소 및 서비스 역할을하는 방법Redis : 데이터 저장소 및 서비스 역할을하는 방법Apr 24, 2025 am 12:08 AM

redisactsasbothadatastoreandaservice.1) asadatastore, itusesin-memorystorageforfastoperations, 지원을 지원합니다

Redis 대 기타 데이터베이스 : 비교 분석Redis 대 기타 데이터베이스 : 비교 분석Apr 23, 2025 am 12:16 AM

redis 与其他数据库相比 与其他数据库相比, 与其他数据库相比 : 1) 速度极快 速度极快 速度极快, 读写操作通常在微秒级别; 2) 支持丰富的数据结构和操作; 3) 灵活的使用场景 3) 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 灵活的使用场景 3) redis 또는 기타 데이터베이스를 선택할 때 특정 요구 사항과 시나리오에 따라 다릅니다. Redis는 고성능 및 저도가 낮은 응용 프로그램에서 잘 수행됩니다.

Redis의 역할 : 데이터 저장 및 관리 기능 탐색Redis의 역할 : 데이터 저장 및 관리 기능 탐색Apr 22, 2025 am 12:10 AM

Redis는 데이터 저장 및 관리에서 핵심적인 역할을하며 여러 데이터 구조 및 지속 메커니즘을 통해 현대 애플리케이션의 핵심이되었습니다. 1) Redis는 문자열, 목록, 컬렉션, 주문 컬렉션 및 해시 테이블과 같은 데이터 구조를 지원하며 캐시 및 복잡한 비즈니스 로직에 적합합니다. 2) RDB와 AOF의 두 가지 지속 방법을 통해 Redis는 신뢰할 수있는 스토리지 및 데이터의 빠른 복구를 보장합니다.

REDIS : NOSQL 개념 이해REDIS : NOSQL 개념 이해Apr 21, 2025 am 12:04 AM

Redis는 대규모 데이터의 효율적인 저장 및 액세스에 적합한 NOSQL 데이터베이스입니다. 1.Redis는 여러 데이터 구조를 지원하는 오픈 소스 메모리 데이터 구조 스토리지 시스템입니다. 2. 캐싱, 세션 관리 등에 적합한 매우 빠른 읽기 및 쓰기 속도를 제공합니다. 3. REDIS는 RDB 및 AOF를 통해 지속성을 지원하고 데이터 보안을 보장합니다. 4. 사용 예제에는 기본 키 값 쌍 작업 및 고급 수집 중복 제거 기능이 포함됩니다. 5. 일반적인 오류에는 연결 문제, 데이터 유형 불일치 및 메모리 오버플로가 포함되므로 디버깅에주의를 기울여야합니다. 6. 성능 최적화 제안에는 적절한 데이터 구조 선택 및 메모리 제거 전략 설정이 포함됩니다.

REDIS : 실제 사용 사례 및 예제REDIS : 실제 사용 사례 및 예제Apr 20, 2025 am 12:06 AM

실제 세계에서 Redis의 애플리케이션에는 다음이 포함됩니다. 1. 캐시 시스템으로서 데이터베이스 쿼리를 가속화, 2. 웹 응용 프로그램의 세션 데이터를 저장하려면 3. 실시간 순위를 구현하려면 메시지 전달을 메시지 큐로 단순화합니다. Redis의 다목적 성과 고성능은 이러한 시나리오에서 빛을 발합니다.

Redis : 기능과 기능을 탐색합니다Redis : 기능과 기능을 탐색합니다Apr 19, 2025 am 12:04 AM

Redis는 고속, 다양성 및 풍부한 데이터 구조로 인해 두드러집니다. 1) Redis는 문자열, 목록, 컬렉션, 해시 및 주문 컬렉션과 같은 데이터 구조를 지원합니다. 2) 메모리를 통해 데이터를 저장하고 RDB 및 AOF 지속성을 지원합니다. 3) Redis 6.0에서 시작하여 멀티 스레드 I/O 작업이 도입되어 동시 동시성 시나리오에서 성능이 향상되었습니다.

Redis는 SQL 또는 NOSQL 데이터베이스입니까? 대답이 설명되었습니다Redis는 SQL 또는 NOSQL 데이터베이스입니까? 대답이 설명되었습니다Apr 18, 2025 am 12:11 AM

redisisclassifiedasanoSqldatabaseBecauseItuseSakey-valuedatamodelinsteadofThraditionalRelationalDatabasemodel.Itoffersspeedandflexibility, makingIdealforreal-timeApplicationsandcaching, butitmaynotbesuitableforscenariosrequiringstrictaintetaintetaintetaintetaintetaintetaintegry

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.