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方式 (1)RDB手动 (2)RDB自动 (3)RDB的优点 (4)RDB缺点 3、AOF方式 &#…...
Butterworth filter的运行原理
想象一下,你正在录制一个舞蹈表演的视频,但在录制过程中,摄像机由于风的影响稍微晃动了一下。现在,录像中的舞者看起来不再那么流畅,动作变得有点颤抖。你希望能让舞者的动作重新看起来平滑和优雅,这时你就…...
掌握SQL的威力:批量更新与删除的艺术
标题:掌握SQL的威力:批量更新与删除的艺术 在数据库管理中,批量更新(UPDATE)和删除(DELETE)操作是常见的需求,特别是在处理大量数据时。SQL作为数据库查询和操作的标准语言…...

《新一代数据可视化分析工具应用指南》正式开放下载
2024年8月12日,由DataEase开源项目组编写的《新一代数据可视化分析工具应用指南》白皮书正式面向广大用户开放下载。 《新一代数据可视化分析工具应用指南》是DataEase开源项目组为了支持企业落地并推广BI工具、推进企业数据可视化建设而编著的指导手册。通过本白皮…...
数据结构与算法——BFS(广度优先搜索)
算法介绍: 广度优先搜索(Breadth-First Search,简称BFS)是一种遍历或搜索树和图的算法,也称为宽度优先搜索,BFS算法从图的某个节点开始,依次对其所有相邻节点进行探索和遍历,然后再…...

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

【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…...

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

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

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

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

【wiki知识库】09.欢迎页面添加(统计浏览量)Vue修改
目录 编辑 一、今日目标 二、新增the-welcome组件 2.1 template 2.2 script 2.2.1 getStatistic 2.2.2 get30DayStatistic 一、今日目标 上篇文章链接:【wiki知识库】08.添加用户登录功能--前端Vue部分修改-CSDN博客 今天就要实现最后的东西了,…...
ui自动化难点
位置坐标:可以通过滑动等方式实现 颜色显示:UIAuto.DEV (devsleep.com) --此工具可以解决很多属性上无法解决的问题 原理: 对系统控件的选择和点击实现该方法...

静态路由与默认路由和实验以及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会显示丢包? 3.1 ARP 工…...

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

AtCoder Beginner Contest 367 A~D
本期封面原图 画师えびら A - Shout Everyday 题意 b点睡觉c点起床,问你a点是不是醒着的 思路 直接判断即可,注意处理跨天的情况 代码 #include <bits/stdc.h> #define mod 998244353 using namespace std; typedef long long ll; typedef …...
oracle 保留两位小数
在Oracle数据库中,保留两位小数可以通过多种函数实现,主要包括ROUND、TRUNC和TO_CHAR函数。每种函数都有其特定的使用场景和效果。 1. ROUND函数 ROUND函数用于对数值进行四舍五入操作,保留指定的小数位数。其基本语法为: ROUN…...
Aop切面技术之存储用户信息
一、背景 在我们日常项目中,常常会将用户的认证信息存入缓存中,方便我们在程序执行中,进行获取用户信息。本篇文章主要是介绍使用自定义注解和AOP切面技术进行实现,这也也是非常容易使用的。 二、代码详解 2.1 自定义注解 定义…...

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

【C语言小项目】五子棋游戏
目录 前言 一、游戏规则 1.功能分析 2.玩法分析 3.胜负判定条件 二、游戏实现思路 三、代码实现与函数封装 1.项目文件创建 2.头文件说明 3.函数封装 1)菜单实现 2)进度条实现 3)main函数实现 4)Game函数 5࿰…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...

STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...

EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...

java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟
众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了,延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp ,边缘服务器拉流推送到云服务器 …...