当前位置: 首页 > news >正文

SpringBoot和Redis的交互数据操作以及Redis的持久化/删除策略和缓存问题

目录

一、SpringBoot和Redis/MySQL的数据交互

二、Redis的持久化

1、持久化过程保存什么

2、RDB方式 

(1)RDB手动

(2)RDB自动

(3)RDB的优点

(4)RDB缺点

3、AOF方式

(1)AOF执行过程

(2)AOF写数据三种策略(appendfsync)

(3) AOF相关配置

(4)AOF重写

三、Redis 删除策略

1、过期数据

2、数据删除策略

(1)定时删除 

(2)惰性删除

(3)定期删除

4、逐出算法

四、企业级解决方案

1、缓存雪崩

2、缓存击穿

3、缓存穿透


一、SpringBoot和Redis/MySQL的数据交互

        SpringBoot/Spring和Redis之间的交互简称为Spring-data-redis,有两种连接方式提供选择:

  • jedis : 采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用 jedis pool 连接池。
  • lettuce : 采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了。

        首先加入Redis相关依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

        要想要和Redis进行交互,必须要创建能和Redis进行交互的对象,并且要容器加载时,就要创建好该对象并注入,所以写在工厂类RedisConfig中:

@Configuration
public class RedisConfig{@Beanpublic RedisTemplate<Object, Object> jsonRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setStringSerializer(new StringRedisSerializer());//配置json类型的序列化工具template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class));template.setConnectionFactory(redisConnectionFactory);return template;}}

        RedisTemplate 是 Spring 提供的一个工具类,简化了与 Redis 交互的操作。方法参数 RedisConnectionFactory redisConnectionFactory 是 Redis 连接工厂,用于创建连接到 Redis 服务器的连接。

        setStringSerializer(new StringRedisSerializer())是设置键的序列化方式为字符串序列化。这样 Redis 中存储的键值对的键会被序列化为字符串。

        template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class));:设置 RedisTemplate 的默认序列化器为 Jackson2JsonRedisSerializer,该序列化器可以将 Java 对象序列化为 JSON 格式存储在 Redis 中,或者将 JSON 反序列化为 Java 对象。

        setConnectionFactory是设置 Redis 连接工厂,用于创建实际的 Redis 连接。这样一个RedisTemplate对象就被实例化好了,可以用来和Redis进行交互。

       

        controller层:

@RestController
public class StudentController {@Autowired(required = false)StudentService userService;@GetMapping("/findById/{id}")public Student findById(@PathVariable("id") Integer id) {Student stu = userService.findById(id);return stu;}@GetMapping("/delete/{id}")public Integer delete(@PathVariable("id") Integer id) {userService.deleteStudentById(id);return id;}}

        这里是通过示例化一个业务层的对象来调用业务层方法,其中@RestController是一个组合注解,相当于 @Controller 和 @ResponseBody。不会跳转到视图上而是直接作为 HTTP 响应的主体内容返回给客户端。

        @GetMapping是将方法映射到 HTTP GET 请求上获取数据返回给客户端,根据id返回对应的Student对象,@PathVariable("id") 将 URL 中的 {id} 部分绑定到方法参数 id 上。

       

        实体类层代码:

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("student")
public class Student{@TableId(value = "stuid" ,type = IdType.AUTO)private Integer stuid;@TableField(value = "stuname")private String stuname;@TableField(value = "stuhobby")private String stuhobby;}

        @TableName是用于指定该实体类对应的数据库表名。在这里,Student 类映射到数据库中的 student 表。剩下的TableId和TableField是对应的属性和字段的名称对应映射。

        service业务层代码:

@Service
public class StudentService {@Autowired(required = false)StudentMapper mapper;@Autowiredpublic RedisUtil redisUtil;/*** 获取用户策略:先从缓存中获取用户,没有则取数据表中数据,再将数据写入缓存*/public Student findById(Integer id){String key = "student:id:" + id;//1.1判断key在redis中是否存在boolean hasKey = redisUtil.hasKey(key);if (hasKey) {//1.2存在缓存则直接获取Object stu = redisUtil.get(key);ObjectMapper change = new ObjectMapper();Student student =   change.convertValue(stu,Student.class);System.out.println("==========从缓存中获得数据=========");System.out.println(student.getStuname());System.out.println("==============================");return student;} else {//1.3不存在缓存,先从数据库中获取,在保存至redis,最后返回用户Student student = mapper.selectById(id);System.out.println("==========从数据表中获得数据=========");System.out.println(student.getStuname());System.out.println("==============================");if (student != null){redisUtil.set(key, student);//写入缓存}return student;}}/*** 删除用户策略:删除数据表中数据,然后删除缓存**/public void deleteStudentById(Integer id){//1.删除数据库int result = mapper.deleteById(id);//2.判断数据库中是否删除成功String key = "student:id:" + id;if (result != 0) {//3.判断redis中是否存在boolean hasKey = redisUtil.hasKey(key);//4.存在删除,不存在直接跳转if (hasKey) {redisUtil.del(key);System.out.println("删除了缓存中的key:" + key);}}}
}

        findById方法执行解析:

        首先是创建mapper实现类对象,因为mapper层实现了BaseMapper接口,这里是MyBatis-plus提供的接口,里面封装了许多方法来和数据库交互,来减少我们的代码量。

        创建RedisUtil工具类类对象,用来和Redis进行交互的一些封装好的方法的工具类,比如对 Redis 的常用操作,比如获取、设置、删除缓存数据

        首先创建存储学生信息的键名,格式为 student:id:1 这样。然后通过hasKey()方法检查刚才创建好的键名是否存在,如果存在则直接从缓存中获取,如果不存在则直接从数据库获取然后存储在Redis中。

        存在的话通过创建好的工具类对象中的get方法来提取学生信息并打印。

        不存在的话通过mapper层调用BaseMapper提供的方法从数据库中拿到数据在放写入redis这中去。

        delete方法执行解析:

        首先是先删除数据库中的信息,看是否删除删除成功,如果成功再去判断Redis中是否存在这条数据,如果存在则删除,不存在则跳转。

        

        测试类代码:

@SpringBootApplication
@MapperScan("com.apesource.springboot_redis02.mapper")
public class SpringbootRedis02Application {public static void main(String[] args) {SpringApplication.run(SpringbootRedis02Application.class, args);}}

        @MapperScan是让 MyBatis 找到指定包中的 Mapper 接口,并将它们注册为 Spring 容器中的 Bean,这样你可以在服务类中通过依赖注入使用这些 Mapper 接口。

        application.properties配置层代码:

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springwork?serverTimezone=GMT%2B8#\u5F00\u542F\u65E5\u5FD7\u7BA1\u7406\uFF0C\u53EF\u4EE5\u67E5\u770Bsql\u8BED\u53E5
#logging.level.com.apesource.springboot_redis02.mapper=debug
#debug=true#\u914D\u7F6E\u8981\u8FDE\u63A5redis\u7684\u5730\u5740
spring.redis.host=localhost
spring.redis.port=6379

        前几步骤就是连接数据库数据源配置,com.apesource.springboot_redis02.mapper是日志级别为 debug,这样可以看到详细的日志输出,包括执行的 SQL 语句等。

        后面是Redis的配置,host配置主机地址,port配置的是服务器的端口号。

        演示示例:

        数据库表如下:

        启动主入口方法,输入地址请求:

        返回客户端json字符串形式:

        再去RedisDesktopManager中查看Redis是否插入:

        显示已经插入到Redis中表示成功。 

二、Redis的持久化

        Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能。

1、持久化过程保存什么
  • 将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据 (RDB)
  • 将数据的操作过程进行保存,日志形式,存储操作过程,关注点在数据的操作过程(AOF)

2、RDB方式 

        概念:在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,快照计数据

(1)RDB手动

        save指令

  • 命令:save
  • 作用:手动执行一次保存操作
  • save指令相关配置:dbfilename dump.rdb
  • 说明:设置本地数据库文件名,默认值为 dump.rdb
  • 经验:通常设置为 dump-端口号.rdb

        dir

  • 说明:设置存储.rdb文件的路径
  • 经验:通常设置成存储空间较大的目录中,目录名称data

        rdbcompression yes

  • 说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF算法 压缩
  • 经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)

        rdbchecksum yes

  • 说明:设置是否进行CRC64算法RDB文件格式校验, 该校验过程在写文件和读文件过程均进行
  • 经验:通常默认为开启状态,如果设置为no,可以节约读写性过程约10%时间消耗,但是存储一定的数据损坏风险

         bgsave指令

  • 命令 :bgsave
  • 作用 :手动启动后台保存操作,但不是立即执行
  • bgsave指令工作原理:

  • 注意:bgsave命令是针对save阻塞问题做的优化。Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用。

        Fork:Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。

(2)RDB自动

        配置:save second changes

        作用:满足限定时间范围内key的变化数量达到指定数量即进行持久化

        参数:

  • second:监控时间范围
  • changes:监控key的变化量

        位置:在conf文件中进行配置

        注意:

  • save配置要根据实际业务情况进行设置,频度过高或过低都会出现性能问题,结果可能是灾难性的
  • save配置中对于second与changes设置通常具有互补对应关系,尽量不要设置成包含性关系
  • save配置启动后执行的是bgsave操作

(3)RDB的优点
  • RDB是一个紧凑压缩的二进制文件,存储效率较高
  • RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景
  • RDB恢复数据的速度要比AOF快很多
  • RDB节省磁盘空间

 

(4)RDB缺点
  • Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
  • 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能
  • RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
  • Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象

3、AOF方式

        概念:AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的;与RDB相比可以简单描述为改记录数据为记录数据产生的过程AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。

(1)AOF执行过程
  • 客户端的请求写命令会被append追加到AOF缓冲区内;
  • AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
  • AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
  • Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;

(2)AOF写数据三种策略(appendfsync)
  • always(每次):每次写入操作均同步到AOF文件中,数据零误差,性能较低
  • everysec(每秒):每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高在系统突然宕机的情况下丢失1秒内的数据
  • no(系统控制):由操作系统控制每次同步到AOF文件的周期,整体过程不可控

(3) AOF相关配置
  • 配置:appendonly yes|no
  • 作用:是否开启AOF持久化功能,默认为不开启状态
  • 配置 :appendfsync always|everysec|no
  • 作用:AOF写数据策略
  • 配置:appendfilename filename
  • 作用:AOF持久化文件名,默认文件名未appendonly.aof,建议配置为appendonly-端口号.aof
  • 配置:dir
  • 作用:AOF持久化文件保存路径,与RDB持久化文件保持一致即可

(4)AOF重写

        在AOF中可能会遇到的问题:

        这里我们就可以使用AOF重写了。

        AOF重写:随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。AOF文件重写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。简单说就是将对同一个数据的若干个条命令执行结果转化成最终结果数据对应的指令进行记录,日志记指令

        AOF重写作用

  • 降低磁盘占用量,提高磁盘利用率
  • 提高持久化效率,降低持久化写时间,提高IO性能
  • 降低数据恢复用时,提高数据恢复效率

        AOF重写规则

  • 进程内已超时的数据不再写入文件。
  • 忽略无效指令,重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令 如del key1、 hdel key2、srem key3、set key4 111、set key4 222等。
  • 对同一数据的多条写命令合并为一条命令。
  • 如lpush list1 a、lpush list1 b、 lpush list1 c 可以转化为:lpush list1 a b c。
  • 为防止数据量过大造成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入64个元素。
  • AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)。

        AOF重写方式

  • 手动重写 bgrewriteaof
  • 自动重写
  • 触发机制,何时重写

        Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发;重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写: 

  • auto-aof-rewrite-min-size 设置重写的基准值,最小文件64MB。达到这个值开始重写。
  • auto-aof-rewrite-percentage 设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)

        总结:

  • 官方推荐两个都启用,如果对数据不敏感,可以选单独用RDB,不建议单独用 AOF,因为可能会出现 Bug。
  • 如果只是做纯内存缓存,可以都不用。

三、Redis 删除策略

1、过期数据

        Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态

  • XX:具有时效性的数据
  • -1:永久有效的数据
  • -2:已经过期的数据或被删除的数据或未定义的数据

        过期数据不是真的被删除了。

2、数据删除策略

        数据删除策略的目标:在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕 机或内存泄露。

  • 定时删除
  • 惰性删除
  • 定期删除
(1)定时删除 
  • 创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作。
  • 优点:节约内存,到时就删除,快速释放掉不必要的内存占用。
  • 缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指 令吞吐量。
  • 总结:用处理器性能换取存储空间(拿时间换空间)。

(2)惰性删除

        数据到达过期时间,不做处理。等下次访问该数据时:

  • 如果未过期,返回数据
  • 发现已过期,删除,返回不存在

        优点:节约CPU性能,发现必须删除的时候才删除

        缺点:内存压力很大,出现长期占用内存的数据

        总结:用存储空间换取处理器性能(拿空间换时间)

(3)定期删除
  • 上面两种都走了极端,有没有折中方案?
  • Redis启动服务器初始化时,读取配置server.hz的值,默认为10
  • 每秒钟执行server.hz次serverCron()中的方法---databasesCron()---activeExpireCycle()
  • activeExpireCycle()对每个expires[*]逐一进行检测,每次执行250ms/server.hz
  • 对某个expires[*]检测时,随机挑选W个key检测:
  • 如果key超时,删除key
  • 如果一轮中删除的key的数量>W * 25%,循环该过程
  • *如果一轮中删除的key的数量≤W * 25%,检查下一个expires[*],0-15循环
  • W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
  • 参数current_db用于记录activeExpireCycle() 进入哪个expires[*] 执行
  • 如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行

        定期删除:周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度。

  • 优点1:CPU性能占用设置有峰值,检测频度可自定义设置
  • 优点2:内存压力不是很大,长期占用内存的冷数据会被持续清理
  • 总结:周期性抽查存储空间 (随机抽查,重点抽查)

        删除策略比对

  • 定时删除 节约内存,无占用 不分时段占用CPU资源,频度高 拿时间换空间。
  • 惰性删除 内存占用严重 延时执行,CPU利用率高 拿空间换时间。
  • 定期删除 内存定期随机清理 每秒花费固定的CPU资源维护内存 随机抽查,重点抽查。

4、逐出算法

        当新数据进入redis时,如果内存不足怎么办?

        Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充 足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储 空间。清理数据的策略称为逐出算法

        注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。 当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。

        抛出异常:(error) OOM command not allowed when used memory >'maxmemory'

        影响数据逐出的相关配置

        maxmemory最大可使用内存:占用物理内存的比例,默认值为0,表示不限制,生产环境中根据需求设定,通常设置在50%以上。

        maxmemory-samples每次选取待删除数据的个数:选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式 作为待检测删除数据。

        maxmemory-policy删除策略

        检测易失数据(可能会过期的数据集server.db[i].expires ):

  • ① volatile-lru:挑选最近最少使用的数据淘汰
  • ② volatile-lfu:挑选最近使用次数最少的数据淘汰
  • ③ volatile-ttl:挑选将要过期的数据淘汰
  • ④ volatile-random:任意选择数据淘汰

        检测全库数据(所有数据集server.db[i].dict ):

  • ⑤ allkeys-lru:挑选最近最少使用的数据淘汰
  • ⑥ allkeys-lfu:挑选最近使用次数最少的数据淘汰
  • ⑦ allkeys-random:任意选择数据淘汰

        放弃数据驱逐:

        ⑧ no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发错误OOM(Out Of Memory)达到最大内存后的,对被挑选出来的数据进行删除的策略

四、企业级解决方案

1、缓存雪崩

        缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

        解决方案

  • 给不同的Key的TTL添加随机值
  • 利用Redis集群提高服务的可用性
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

2、缓存击穿

        缓存击穿问题也叫热点Key问题,就是一个高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

        常见的解决方案有两种:互斥锁、逻辑过期。

        逻辑分析:假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的, 此时只要线程1走完这个逻辑,其他线程就都能从缓存中加载这些数据了,但是假设在线程1没有走完的 时候,后续的线程2,线程3,线程4同时过来访问当前这个方法, 那么这些线程都不能从缓存中查询到 数据,那么他们就会同一时刻来访问查询缓存,都没查到,接着同一时间去访问数据库,同时的去执行 数据库代码,对数据库访问压力过大。

        解决方案一、使用锁来解决

        因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压 力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用 tryLock方法 + double check来解决这样的问题。

        假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人 去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休 眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。

        解决方案二、逻辑过期方案

        方案分析:我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我 们不设置过期时间,其实就不会有缓存击穿的问题,但是不设置过期时间,这样数据不就一直占用我们 内存了吗,我们可以采用逻辑过期方案。

        我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续 通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1 去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个 线程去进行 以前的重构数据 的逻辑,直到新开的线程完成这个逻辑后,才释放锁, 而线程1直接进行返回,假设现在线程3过来访 问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把 重建数据构建完后,其他线程才能走返回正确的数据。

        这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据

        进行对比:

        互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其 他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性 能肯定受到影响。

        逻辑过期方案: 线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是 在重构数据完成前,其他的线程只能返回之前的数据,且实现起来麻烦。

3、缓存穿透

        缓存穿透:缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这 些请求都会打到数据库。

        常见的解决方案有两种:

        缓存空对象:

  • 优点:实现简单,维护方便。
  • 缺点:额外的内存消耗、可能造成短期的不一致。

        缓存空对象思路分析:当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据, 此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据 库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会 访问到数据库,简单的解决方案就是哪怕这个数据在数据库中也不存在,我们也把这个数据存入到redis 中去,这样,下次用户过来访问这个不存在的数据,那么在redis中也能找到这个数据就不会进入到缓存了。

        布隆过滤:布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问 redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数据后,再将其放入到redis中,假设布隆过滤器判断这个数据不存在,则直接返回。

        这种方式优点在于节约内存空间,存在误判,误判原因在于:布隆过滤器走的是哈希思想,只要哈希思想,就可能存在哈希冲突

相关文章:

SpringBoot和Redis的交互数据操作以及Redis的持久化/删除策略和缓存问题

目录 一、SpringBoot和Redis/MySQL的数据交互 二、Redis的持久化 1、持久化过程保存什么 2、RDB方式 &#xff08;1&#xff09;RDB手动 &#xff08;2&#xff09;RDB自动 &#xff08;3&#xff09;RDB的优点 &#xff08;4&#xff09;RDB缺点 3、AOF方式 &#…...

Butterworth filter的运行原理

想象一下&#xff0c;你正在录制一个舞蹈表演的视频&#xff0c;但在录制过程中&#xff0c;摄像机由于风的影响稍微晃动了一下。现在&#xff0c;录像中的舞者看起来不再那么流畅&#xff0c;动作变得有点颤抖。你希望能让舞者的动作重新看起来平滑和优雅&#xff0c;这时你就…...

掌握SQL的威力:批量更新与删除的艺术

标题&#xff1a;掌握SQL的威力&#xff1a;批量更新与删除的艺术 在数据库管理中&#xff0c;批量更新&#xff08;UPDATE&#xff09;和删除&#xff08;DELETE&#xff09;操作是常见的需求&#xff0c;特别是在处理大量数据时。SQL作为数据库查询和操作的标准语言&#xf…...

《新一代数据可视化分析工具应用指南》正式开放下载

2024年8月12日&#xff0c;由DataEase开源项目组编写的《新一代数据可视化分析工具应用指南》白皮书正式面向广大用户开放下载。 《新一代数据可视化分析工具应用指南》是DataEase开源项目组为了支持企业落地并推广BI工具、推进企业数据可视化建设而编著的指导手册。通过本白皮…...

数据结构与算法——BFS(广度优先搜索)

算法介绍&#xff1a; 广度优先搜索&#xff08;Breadth-First Search&#xff0c;简称BFS&#xff09;是一种遍历或搜索树和图的算法&#xff0c;也称为宽度优先搜索&#xff0c;BFS算法从图的某个节点开始&#xff0c;依次对其所有相邻节点进行探索和遍历&#xff0c;然后再…...

登录 k8s-Dashboard 显示 Your connection is not private

文章目录 一、背景二、解决方案 一、背景 部署好 kubernetes-Dashboard 后使用 master节点的 ipport 登录 Dashboard 显示 Your connection is not private 无论是 Edge 还是 Google Chrome 都是这样的情况 二、解决方案 点击网页空白处&#xff0c;英文输入法输入&#xf…...

【Bifrost】ubuntu24.04 远程构建及clion设置编码风格google

Bifrost 构建通过clion 远程到ubuntu24.04 构建感觉是不认识这种写法,这种至少是c++11 fix : 修改absl 的构建cmakelist,明确c++17 好像还是不行error: ‘uint8_t’ was not declared in this scope加入:#include <stdint.h>可以解决一部分。那么,这种呢?/home/zha…...

批量查询全国快递单号:高效追踪物流信息

在日常生活和工作中&#xff0c;我们经常会遇到需要查询多个快递单号物流信息的情况。如果手动逐一查询&#xff0c;不仅效率低下&#xff0c;而且容易出错。为了解决这个问题&#xff0c;我们可以借助固乔科技推出的【固乔快递查询助手】软件&#xff0c;轻松实现全国快递的批…...

DVWA | CSRF(LowMedium)攻击的渗透实践

目录 概述 Low Medium 概述 CSRF&#xff08;Cross-Site Request Forgery&#xff0c;跨站请求伪造&#xff09; 是一种网络攻击方式。 通过伪造当前用户的行为&#xff0c;让目标服务器误以为请求由当前用户发起&#xff0c;并利用当前用户权限实现业务请求伪造。 例如&a…...

Tmagic-editor低代码底层拖拽库Moveable示例学习

在前面咱们的自研低代码海报制作平台学习分享计划中分享了自己开发的基本拖拽组件&#xff0c;也只是做了最简单的基本实现。真要写产品&#xff0c;更多还是依赖相关的开源优秀库。 文章目录 参考基本拖拽基本缩放基本Scalable基本旋转基于原点的拖拽和旋转关于练习源码 参考 …...

公开测评:文件防泄密系统哪家好|4款文件防泄密软件推荐

在文件防泄密系统领域&#xff0c;有多款软件以其高效、安全和全面的功能脱颖而出&#xff0c;为企业数据保护提供了有力支持。以下是四款值得推荐的文件防泄密软件&#xff0c;它们各具特色&#xff0c;能够满足不同企业的数据安全需求。 1. 安企神软件 7天试用版https://wor…...

【wiki知识库】09.欢迎页面添加(统计浏览量)Vue修改

目录 ​编辑 一、今日目标 二、新增the-welcome组件 2.1 template 2.2 script 2.2.1 getStatistic 2.2.2 get30DayStatistic 一、今日目标 上篇文章链接&#xff1a;【wiki知识库】08.添加用户登录功能--前端Vue部分修改-CSDN博客 今天就要实现最后的东西了&#xff0c…...

ui自动化难点

位置坐标&#xff1a;可以通过滑动等方式实现 颜色显示&#xff1a;UIAuto.DEV (devsleep.com) --此工具可以解决很多属性上无法解决的问题 原理&#xff1a; 对系统控件的选择和点击实现该方法...

静态路由与默认路由和实验以及ARP工作原理

目录 1.静态路由和默认路由 1.1 静态路由 1.2 默认路由 1.3 主要区别总结 2.实验 2.1 实验 2.1.1 实验top 2.1.2 实验要求 2.2 实验配置 2.2.1 ip信息配置 2.2.2 配置静态 2.2.3配置默认 2.3 实验结果查看 3.为什么第一个ping会显示丢包&#xff1f; 3.1 ARP 工…...

美国洛杉矶大带宽服务器的运维与监控

美国洛杉矶的大带宽服务器因其优越的地理位置、高速的网络连接以及充足的带宽资源&#xff0c;在全球范围内享有很高的声誉。为了确保这些服务器的稳定运行和高效服务&#xff0c;运维与监控工作显得尤为重要。以下是一些关于美国洛杉矶大带宽服务器运维与监控方面的科普内容。…...

AtCoder Beginner Contest 367 A~D

本期封面原图 画师えびら A - Shout Everyday 题意 b点睡觉c点起床&#xff0c;问你a点是不是醒着的 思路 直接判断即可&#xff0c;注意处理跨天的情况 代码 #include <bits/stdc.h> #define mod 998244353 using namespace std; typedef long long ll; typedef …...

oracle 保留两位小数

在Oracle数据库中&#xff0c;保留两位小数可以通过多种函数实现&#xff0c;主要包括ROUND、TRUNC和TO_CHAR函数。每种函数都有其特定的使用场景和效果。 1. ROUND函数 ROUND函数用于对数值进行四舍五入操作&#xff0c;保留指定的小数位数。其基本语法为&#xff1a; ROUN…...

Aop切面技术之存储用户信息

一、背景 在我们日常项目中&#xff0c;常常会将用户的认证信息存入缓存中&#xff0c;方便我们在程序执行中&#xff0c;进行获取用户信息。本篇文章主要是介绍使用自定义注解和AOP切面技术进行实现&#xff0c;这也也是非常容易使用的。 二、代码详解 2.1 自定义注解 定义…...

FreeBSD 针对OpenSSH 高危漏洞发布紧急补丁

近日&#xff0c;FreeBSD 项目的维护者针对OpenSSH 高危漏洞发布了紧急补丁。该漏洞被追踪为 CVE-2024-7589&#xff0c;CVSS 得分为 7.4&#xff08;最高分为 10.0&#xff09;。通过利用该漏洞&#xff0c;黑客能够在权限提升的情况下远程执行任意代码。 根据上周发布的一份…...

【C语言小项目】五子棋游戏

目录 前言 一、游戏规则 1.功能分析 2.玩法分析 3.胜负判定条件 二、游戏实现思路 三、代码实现与函数封装 1.项目文件创建 2.头文件说明 3.函数封装 1&#xff09;菜单实现 2&#xff09;进度条实现 3&#xff09;main函数实现 4&#xff09;Game函数 5&#xff0…...

基于Java语言的能源管理系统-水电气热油数据采集系统

基于Java语言的能源管理系统-水电气热油数据采集系统 介绍 适用于高能耗企业、建筑、工厂、园区的水、电、气、热、油、空压机等能源数据采集、分析、报表&#xff1b; 基于SpringCloud的能源管理系统-能源管理平台源码-能源在线监测平台-双碳平台源码-SpringCloud全家桶-能管…...

人工智能在肿瘤亚型分类领域的研究进展|顶刊速递·24-08-13

小罗碎碎念 文献日推主题&#xff1a;人工智能在肿瘤亚型分类领域的研究进展 昨天晚上在研究鼻咽癌的病理学诊断指南&#xff0c;看到了下面这段话的时候&#xff0c;我问了自己一个问题——通过AI识别出肿瘤亚型的根本目的是什么&#xff1f;可以衔接哪些具体的下游任务&#…...

Taro+Vue 创建微信小程序

TaroVue 创建微信小程序 一、全局安装 tarojs/cli二、项目初始化三、现在去启动项目吧 一、全局安装 tarojs/cli npm install -g tarojs/cli //安装 npm info tarojs/cli //查看安装信息 如果正常显示版本说明成功了&#xff0c;就直接跳到第二步吧官网说&#xff1a;…...

智能安全守护,寺庙安全用电解决方案

在四川省蓬溪县城北&#xff0c;高峰山以其千年的历史沉淀和独特的文化风貌&#xff0c;默默诉说着道教与佛教交融的传奇。然而&#xff0c;2017年5月31日凌晨的一声巨响&#xff0c;打破了这里的宁静&#xff0c;一场突如其来的大火&#xff0c;让这座承载着无数信徒信仰与梦想…...

加热系统加入达温即停和保温功能

加热系统加入达温即停和保温功能 引言 ​ 我们前一节, 是通过控制io口, 直接控制加热的开关, 但是这太简单粗暴了, 如果只是那样, 和普通的家居电器,是没区别的, 我们想要更智能一点, 就是像咱们自己家的热水壶那样, 水烧滚了(水温达到100摄氏度), 就自动停止了. 加热功能&a…...

C++_2_ inline内联函数 宏函数(2/3)

C推出了inline关键字&#xff0c;其目的是为了替代C语言中的宏函数。 我们先来回顾宏函数&#xff1a; 宏函数 现有个需求&#xff1a;要求你写一个Add(x,y)的宏函数。 正确的写法有一种&#xff0c;错误的写法倒是五花八门&#xff0c;我们先来“见不贤而自省也。” // …...

ROS执行多个节点报错(遥控运动及SLAM建图)

今天在实体机器人中同时执行多个ROS节点: roslaunch rei_robot_base oryxbot_base.launchroslaunch robot_joy robot_joy.launchroslaunch oryxbot_slam oryxbot_slam_local.launch结果全部报错退出了 现在换一种执行方式: roscoreroslaunch rei_robot_base oryxbot_base.la…...

Spring Boot项目中实现文件的上传、下载和预览功能

在Spring Boot项目中实现文件的上传、下载和预览功能&#xff0c;可以通过使用Spring MVC的MultipartFile接口来处理文件上传&#xff0c;并使用HttpServletResponse或Resource来实现文件下载和预览。下面是如何实现这些功能的完整示例。 1. 引入依赖 确保在pom.xml中引入了S…...

【JAVA入门】Day21 - 时间类

【JAVA入门】Day21 - 时间类 文章目录 【JAVA入门】Day21 - 时间类一、JDK7前的时间相关类1.1 Date1.2 SimpleDateFormat1.3 Calendar 二、JDK8新增的时间相关类2.1 Date 相关类2.1.1 ZoneId 时区2.1.2 Instant 时间戳2.1.3 ZoneDateTime 带时区的时间 2.2 DateTimeFormat 相关…...

SQL server数据库备份和还原

新手小白都懂的sql server数据库备份和还原 一、备份 1.打开sql server数据库找到 2.展开找到对应的数据库文件 鼠标右击—任务–备份 3.复制名称 4.复制完点击添加 5.点击添加完之后再次点击查找路径 6.分别两个路径 原路径和新路径 &#xff08;新路径是找到原路径新建了一…...

B站搜索建库架构优化实践

前言 搜索是B站的重要基础功能&#xff0c;需要对包括视频、评论、图文等海量的站内优质资源建立索引&#xff0c;处理来自用户每日数亿的检索请求。离线索引数据的正确、高效产出是搜索业务的基础。我们在这里分享搜索离线架构整体的改造实践&#xff1a;从周期长&#xff0c;…...

XSS反射实战

目录 1.XSS向量编码 2.xss靶场训练&#xff08;easy&#xff09; 2.1第一关 2.2第二关 方法一 方法二 2.3第三关 2.4第四关 2.5第五关 2.6第六关 2.7第七关 第一种方法&#xff1a; 第二种方法&#xff1a; 第三个方法&#xff1a; 2.8第八关 1.XSS向量编码 &…...

远程消息传递的艺术:NSDistantObject在Objective-C中的妙用

标题&#xff1a;远程消息传递的艺术&#xff1a;NSDistantObject在Objective-C中的妙用 引言 在Objective-C的丰富生态中&#xff0c;NSDistantObject扮演着至关重要的角色&#xff0c;特别是在处理分布式系统中的远程消息传递。它允许对象之间跨越不同地址空间进行通信&…...

指向派生类的基类指针、强转为 void* 再转为基类指针、此时调用虚函数会发生什么?

指向派生类的基类指针、强转为 void* 再转为基类指针、此时调用虚函数会发生什么&#xff1f; 1、无论指针类型怎么转&#xff0c;类对象内存没有发生任何变化&#xff0c;还是vfptr指向虚函数表&#xff0c;下面是成员变量&#xff0c;这在编译阶段就已经确定好了&#xff1b…...

操作系统(Linux实战)-进程创建、同步与锁、通信、调度算法-学习笔记

1. 进程的基础概念 1.1 进程是什么&#xff1f; 定义&#xff1a; 进程是操作系统管理的一个程序实例。它包含程序代码及其当前活动的状态。每个进程有自己的内存地址空间&#xff0c;拥有独立的栈、堆、全局变量等。操作系统通过进程来分配资源&#xff08;如 CPU 时间、内…...

react的setState中为什么不能用++?

背景&#xff1a; 在使用react的过程中产生了一些困惑&#xff0c;handleClick函数的功能是记录点击次数&#xff0c;handleClick函数被绑定到按钮中&#xff0c;每点击一次将通过this.state.counter将累计的点击次数显示在页面上 困惑&#xff1a; 为什么不能直接写prevStat…...

2.2算法的时间复杂度与空间复杂度——经典OJ

本博客的OJ标题均已插入超链接&#xff0c;点击可直接跳转~ 一、消失的数字 1、题目描述 数组nums包含从0到n的所有整数&#xff0c;但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗&#xff1f; 2、题目分析 &#xff08;1&#xff09;numsS…...

【CentOS 】DHCP 更改为静态 IP 地址并且遇到无法联网

文章目录 引言解决方式标题1. **编辑网络配置文件**&#xff1a;标题2. **确保配置文件包含以下内容**&#xff1a;特别注意 标题3. **重启网络服务**&#xff1a;标题4. **检查配置是否生效**&#xff1a;标题5. **测试网络连接**&#xff1a;标题6. **检查路由表**&#xff1…...

Linux 操作系统 --- 信号

序言 在本篇内容中&#xff0c;将为大家介绍在操作系统中的一个重要的机制 — 信号。大家可能感到疑惑&#xff0c;好像我在使用 Linux 的过程中并没有接触过信号&#xff0c;这是啥呀&#xff1f;其实我们经常遇到过&#xff0c;当我们运行的进程当进程尝试访问非法内存地址时…...

黑马前端——days09_css

案例 1 页面框架文件 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compati…...

【Python爬虫】技术深度探索与实践

目录 引言 第一部分&#xff1a;Python爬虫基础 1.1 网络基础 1.2 Python爬虫基本流程 第二部分&#xff1a;进阶技术 2.1 动态网页抓取 2.2 异步编程与并发 2.3 反爬虫机制与应对 第三部分&#xff1a;实践案例 第四部分&#xff1a;法律与道德考量 第五部分&#x…...

智启万象|挖掘广告变现潜力,保障支付安全便捷

谷歌致力于为开发者提供 先进的广告变现与支付解决方案 一起回顾 2024 Google 开发者大会 了解如何利用谷歌最新工具和功能 提高变现收入&#xff0c;优化用户体验&#xff0c;保障交易安全 让变现更上一层楼 广告检查器是谷歌 AdMob 平台最新推出的高级测试工具&#xff0c;开…...

函数递归,匿名、内置行数,模块和包,开发规范

一、递归与二分法 一&#xff09;递归 1、递归调用的定义 递归调用&#xff1a;在调用一个函数的过程中&#xff0c;直接或间接地调用了函数本身 2、递归分为两类&#xff1a;直接与间接 #直接 def func():print(from func)func()func() # 间接 def foo():print(from foo)bar…...

Springboot3 整合swagger

一、pom.xml <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-api</artifactId><version>2.1.0</version></dependency> 二、application.yml # SpringDoc配置 # springdoc:swa…...

查看同一网段内所有设备的ip

使用命令提示符&#xff08;CMD&#xff09;进行扫描 查看本机IP地址 首先通过 ipconfig /all 命令查看本机的IP地址&#xff0c;确定你的网段&#xff0c;例如 192.168.1.。 Ping网段内每个IP地址 接着使用循环命令&#xff1a; for /L %i IN (1,1,254) DO ping -w 1 -n …...

Spark MLlib 特征工程(上)

文章目录 Spark MLlib 特征工程(上)特征工程预处理 Encoding:StringIndexer特征构建:VectorAssembler特征选择:ChiSqSelector归一化:MinMaxScaler模型训练总结Spark MLlib 特征工程(上) 前面我们一起构建了一个简单的线性回归模型,来预测美国爱荷华州的房价。从模型效果来…...

《SPSS零基础入门教程》学习笔记——03.变量的统计描述

文章目录 3.1 连续变量&#xff08;1&#xff09;集中趋势&#xff08;2&#xff09;离散趋势&#xff08;3&#xff09;分布特征 3.2 分类变量&#xff08;1&#xff09;单个分类变量&#xff08;2&#xff09;多个分类变量 3.1 连续变量 &#xff08;1&#xff09;集中趋势 …...

2024年杭州市网络与信息安全管理员(网络安全管理员)职业技能竞赛的通知

2024年杭州市网络与信息安全管理员&#xff08;网络安全管理员&#xff09;职业技能竞赛的通知 一、组织机构 本次竞赛由杭州市总工会牵头&#xff0c;杭州市人力资源和社会保障局联合主办&#xff0c;杭州市萧山区总工会承办&#xff0c;浙江省北大信息技术高等研究院协办。…...

SpringBoot参数校验详解

前言 在web开发时&#xff0c;对于请求参数&#xff0c;一般上都需要进行参数合法性校验的&#xff0c;原先的写法时一个个字段一个个去判断&#xff0c;这种方式太不通用了&#xff0c;Hibernate Validator 是 Bean Validation 规范的参考实现&#xff0c;用于在 Java 应用中…...

安全基础学习-SHA-1(Secure Hash Algorithm 1)算法

SHA-1(Secure Hash Algorithm 1)是一种密码学哈希函数,用于将任意长度的输入数据(消息)转换成一个固定长度的输出(哈希值或摘要),长度为160位(20字节)。SHA-1的主要用途包括数据完整性验证、数字签名、密码存储等。 1、SHA-1 的特性 定长输出:无论输入数据长度是多…...