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

Redis进阶知识个人汇总

持久化

三种方式实现它的持久化:

  • RDB持久化

    • 全称Redis数据备份文件,又称Redis数据快照

    • 这种就是将Redis内存中所有数据记录到磁盘中,当实例出故障后,从磁盘中读快照文件进行恢复数据。

    • 一般使用bgsave指令实现

      • 复制主线程得到一个子进程,共享同一内存空间

      • 子进程读取Redis内存数据,并写入一个新RDB文件

      • 最后,用新的RDB文件替代旧的

    • 可以配置:save 60 100

      • 代表60s内至少执行100次触发RDB

    • 缺点

      • RDB执行间隔长,两次RDB之间写有数据丢失风险

      • fork子进程、压缩、写RDB文件都耗时

  • AOF持久化

    • 全称追加文件

    • Redis处理的每一个写命令都记录在AOF文件(类似操作日志文件)

    • 配置

      • 默认关闭,修改redis.conf开启

      • 记录频率的三种方式

        • always(立刻记录)

        • everysec(每隔一秒记录)

        • no(由操作系统决定)

  • 混合持久化

    • 结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。

    • 缺点是可读性差,兼容性差

Redis主从架构

搭建主从集群,实现读写分离

  • 全量同步:master将完整的内存数据生成RDB,发送RDB到slave后。后续命令则记录在repl_baklog,逐个发送给slave

  • 增量同步:slave提交自己的offset到master,master获取repl_backlog中从offset之后命令给slave

主从数据同步流程

  1. slave节点请求增量同步

  2. master判断replid ,如果不一致拒绝增量

  3. slave情况数据,加载master的RDB

  4. master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave

  5. slave执行接收到的命令,并与master保持同步

什么时候执行全量同步

  • slave节点第一次连接master的时候

  • slave节点断开时间太久,repl_baklog中的offset已经被覆盖的时候

Redis哨兵

哨兵是Redis提供的一种利用心跳机制监听主节点是否存活的机制。

如果主节点挂了,通过判断slave的slave-priority选举一个新的。

分片集群

集群中多个master,每个master保持不同数据。

就是将多个Redis组合成了一个大的。

Redis会把每个master节点映射到0-16383供16384个插槽上(hash slot),查看集群信息就能看到。

Redis如何判断某个key应该在哪个实例?
  • 将16384个插槽分配到不同的实例中

  • 根据key的有效部分计算hash值,对16384取余

  • 余数作为插槽,寻找插槽所在实例

多级缓存

JVM进程缓存

使用Caffeine,利用JVM的进程缓存。

Caffeine 的3中缓存淘汰策略:

  1. 基于容量

  2. 基于时间

  3. 基于对象引用。

    • 设置对象为 软引用或弱引用,利用GC来回收缓存数据。性能较差,不建议使用。

Nginx-反向代理缓存

在服务端,可以利用反向代理(如Nginx)设置本地缓存。当请求到达Nginx时,它会首先检查本地是否有请求的数据,如果有则直接返回,避免了到达应用服务器的请求。

Canal

使用Canal实现数据一致性

Redis键值设计

一般约定:

  1. 基本格式:[业务名称]:[数据名]:[id]

  2. 长度不超过44字节

  3. 不包括特殊字符

拒绝BigKey,

  • key本身数据量过大:一个string类型的key,值为5MB

  • key中成员数过多:一个zset类型的key,成员数量为:10000个

  • key中成员数据量过大:一个hash类型的key,成员数量虽只有1000个,但这个value的总大小为100MB

推荐key值:

  • 单个key的value小于10KB

  • 对于集合类型的key,建议元素数量为小于1000

bigKey危害:

  1. 执行对BigKey读的时候,少量的QPS可能导致带宽使用占满

  2. 导致数据倾斜,bigkey所在Redis实例内存远超其他实例

  3. 可能运算耗时过久,导致主线程阻塞

批处理优化

  • Mset(处理数据类型有限)

    • mset

    • hmset

  • Pipeline(可以对复制数据类型的批处理需要)

    @Test
    void testPipeline(){创建管道Pipeline pipeline = jedis.pipelined();for(int i=1;i <= 100000; i++){//放入命令到管道pipeline.set("test:key_" + i,"value_"+i);if(i % 1000 == 0){//批量执行pipeline.sync();}}
    }

集群下数据处理

使用Spring提供的stringRedisTemplate就行,底层使用的是并行slot

image-20240428134902755

Redis底层数据结构

动态字符串SDS

具备自动扩容的能力。

结构:

  1. len:字符串字节数

  2. alloc:申请的总字节数

  3. flags:不同的SDS头类型,用来控制头的大小

  4. buf:具体的字符串

申请空间策略:

  1. 当新字符串小于1M,则新空间扩展后为字符串长度的两倍+1

  2. 当新字符串大于1M,则新空间为扩展后字符串长度 + 1M +1。(称为内存预分配)

SDS是一种由C语言实现的动态字符串,具有空间预分配和惰性释放空间的特点

IntSet

  • Inset是Redis中set集合的一种实现方式

  • 基于整形数组来实现,并具备长度可变、有序等特性。(适合使用于数据量不多的情况)

结构:

  1. encoding:编码方式

  2. length:元素个数

  3. contents[]:整数数组,保存集合数据

总结:

  1. Redis确保Intset元素唯一、有序

  2. 具备类型升级机制(升级编码方式到合适大小),可省内存空间

  3. 底层采用二分查询

intset是一种元素全部都是整形的set集合,具有对新增数据判断升级、底层采用二叉查找数据、保证数据有序且唯一的特点

Dict

实现了键值的映射关系

组成部分:哈希表(dictht:数组+链表)、哈希节点(DictEntry)、字典(Dict)

dictEntry结构:

  1. key:键

  2. v:值

  3. struct dictEntry *next:下一个Entry指针

dictht(dictHashTable)结构:

  1. dictEntry **table:entry类型数组

  2. size:哈希表大小

  3. sizemask:哈希表大小的掩码,总等于:size-1

  4. used:entry个数

dict结构:

  1. type:dict类型,内置不同的hash函数

  2. privdate:私有数据,做特殊hash运算使用

  3. ht[2]:包含两个哈希表,一个是当前数据,另一个是空,rehash使用

  4. rehashidx:rehash进度,-1为未进行

  5. pauserehash:rehash是否暂停,1暂停,0继续

image-20240606163826453

扩容条件

因为dict中的hashtable是由数组+单向链表实现。当元素过多的时候,会导致哈希冲突;而且链表过长,影响查询效率,所以这个时候就需要扩容了。

LoadFactor=used(所有的entry)/size(哈希表大小-1)

下面这两种情况会扩容:

  1. LoadFactor >= 1,并服务器没有执行bgsave / bgrewriteaof等后台进行时候

  2. LoadFactor > 5

每次扩容到2^n

Dict收缩

  • LoadFactor < 0.1时,进行收缩

  • 每次收缩到2^n,但是必须大于 4

rehash

  • 因为我们知道扩容还是收缩都要创建一个新的哈希表,但是这会导致size和sizemask变化。

  • 所以必须对哈希表中每个key重新计算索引,插入新的哈希表中,这个过程就叫rehash

rehash过程:

  1. 计算哈希表大小 realeSize(应分配的哈希表大小)

    1. 扩容:realeSize为 大于等于dict.ht[0].used + 1的新的2^n

    2. 收缩:同上,但是是小于等于,但不能小于4

  2. 申请内存空间并创建一个新dictht

  3. 设置dict.rehashidx=0,标志rehash的开始

  4. 重新对旧hash表中的数据进行hash求值,并复制到新hash表中

  5. 切换hash表,并释放旧哈希表中的内存空间

渐进式rehash:(因为一次性搬迁哈希表可能出现阻塞情况,所以可以采用渐进式,将一步分为多步实现)

  • 渐进式rehash相对普通的rehash而言就是,在数据迁移的时候,rehash是一次性的操作,而渐进式rehash是通过客户端请求过程中同时进行迁移操作

  • 在渐进式rehash搬迁数据的时候,如果要查询数据,是对两个hash表中进行查询

ziplist

ziplist是一种类似双端队列的设计,可以在头尾两端进行加/减元素操作,然后他存储的方式是一种连续内存的方式,当前元素记录上一个元素的长度,可以根据元素的type类型区分是字符串还是数字,然后选择是全数字的ziplist还是全字符串的。

ziplist结构:

  1. zlbytes:记录整个压缩列表占用内存字节数

  2. zltail:记录压缩列表表尾部节点距离压缩列表的起始地址有多少字节。通过这个偏移量,可以确定表尾节点的地址。

  3. zlen:记录了压缩列表包含的节点数量。

  4. entry:压缩列表包含的各个节点,节点的长度由字节保存的内容决定。

  5. zlend:特殊值OxFF(十进制 255),用于标记压缩列表的末端

entry结构:(整个entry字节数=前节点长度 + 编码 + 当前节点内容)

  1. previous_entry_length:前一节点长度,占1或5个字节

    • 如果前一节点长度小于254字节,则采用1个字节保存这个长度值

    • 大于254字节,用5个字节

  2. encoding:编码属性,记录content的数据类型(字符串/整数)以及长度。占用1/2/5个字节

  3. contents:负责保存节点的数据,可以是字符串或整数

编码:

  1. 字符串:编码是以 “00”、“01”、“10”开头

  2. 数字:“11”开始,且encoding固定占用1个字节

问题:连锁更新 => 导致内存连续被申请喝销毁

QuickList

用来解决ziplist连续空间、无法存储大量数据、数据拆分后分散不易管理的问题。

quicklist是一个双端链表,每个节点都是一个ziplist。

  • 限制ziplist的entry数量:Redis提供了一个list-max-ziplist-size的配置项,用来限制ziplist的entry过多。

    • -1:不超过4kb

    • -2:8

    • -3:16

    • -4:32

    • -5:64

  • 限制ziplist的大小:quicklist还可以通过配置list-compress-depth进行对ziplist做压缩

    • 0:特殊值,不压缩

    • 1:首尾1个节点不压缩,中间节点压缩

    • 2:首尾2个节点不压缩,中间节点压缩

    • 。。。同上类推

QuickListNode结构:

  • QuickListNode *prev:前一节点指针

  • QuickListNode *next:后一节点指针

  • char *zl:当前节点的ziplist指针

  • sz:当前节点的ziplist的字节大小

  • count:当前节点的ziplist的entry个数

  • encoding:编码(1:ziplist,2:lzf压缩模式)

  • container:数据容器类型(预留)1:其他,2:ziplist

  • ...

quicklist结构:

  • head:头节点指针

  • tail:尾节点指针

  • count:所有ziplist的entry的数量

  • len:ziplist总数量

  • fill:ziplist的entry上限

  • compress:首尾不压缩节点数量

  • ...

image-20240429212852394

总结

我的理解:

quicklist是一种用来解决ziplist连续存储空间、无法存储超大容量数据、数据拆分不易管理而提出的一种数据类型,是一个双端链表,节点上存储的是一个ziplist。

skiplist

skiplist是一种特殊的链表,具有升序排序存储、节点包含多个不同跨度的指针的特点。

zskiplistNode结构:

  • ele:节点存储的值

  • score:节点分数。用来排序、查询

  • backword:前一个节点指针

  • level[] :多级索引数组

    • forward:下一个节点指针

    • span:索引跨度

zskiplist:

  • header,tail:头尾指针

  • length:节点数量

  • level:索引层级,默认1,最多32级

image-20240429214103125

总结:

个人理解:

skiplist是一种按score值升序排序存储的双向链表,对这个链表按照全局固定的一个span值进行划分一个索引层次,可以用这个层次来加速查询

RedisObject

通过对上述的任意数据类型的键值封装成一个RedisObject形成Redis的基本数据类型。

结构:

  • type:对象类型。string、hash、list、set、zset(占4bit)

  • encoding:底层编码方式,11种(占4bit)

  • lru:

  • refcount:对象引用计数器,计数器为0时,无人引用,可被回收

  • ptr:指向实际的数据存储空间

image-20240429214646035

上述的头部已经占用了16个字节了。

这就是为什么存储大容量数据不用string类型的原因了,一个string数据就多占用16个字节

11种编码方式:

image-20240429214820332

对应着不同数据类型采用的不同编码:

image-20240429214909437

Redis的5种基本数据类型

string

采用的编码:int、embstr、raw

  • 基本编码方式是raw,基于动态字符串(SDS)实现,存储上限512MB

  • 当SDS长度小于44字节,则采用embstr编码。

  • 当存储的字符串是整数值,并在long_max范围,采用int编码

    • 直接将数据保存在RedisObject的ptr指针位置(刚好有8个字节大小),不需要SDS了

image-20240430103804424

list

采用的编码

  • 3.2之前:linkedlist + ziplist

  • 3.2之后:quicklist

image-20240430104705677

set

采用的编码:intset、ht

  • 当存储的所有数据都是整数的时候,并且元素的数量不超过set-max-intset-entries,set会采用intset这种编码节省空间

  • 其他时候采用ht编码(Dict),key用来存储元素,value统一为null(虽然这个value为null有点浪费,但是整体来说还是值得的)

image-20240430105510393

zset

采用的编码:ziplist、ht、skiplist

因为zset的特点:键值存、键必须唯一、可排序

通过skiplist + ht(Dict)实现:

  • skiplist:可排序,可存储score喝ele值(member)

  • ht(Dict):存储键值,根据key找value

typedef struct zset{dict *dict;zskiplist *zls;
}

image-20240430112627572

当元素数量不多的时候,ht+skiplist的优势不明显,而且更耗内存。这个时候会通过ziplist来节省内存。

要满足以下两个条件:

  1. 元素数量小于zset_ziplist_entries,默认128

  2. 每个元素都小于 zset_max_ziplist_value字节,默认64

ziplist本身没有键值存储、没有排序功能,这个时候要功能代码进行实现:

  • ziplist是连续内存,所以score和ele是紧凑一起的两个entry,ele前,score后

  • score越小越近队首,score越大越接近队尾,按score升序排序

image-20240430113128446

hash

采用的编码:ziplist、ht

hash底层跟zset基本一致,只需把排序有关的skiplist去掉即可:

  • hash默认采用ziplist,节省内存,相邻的两个entry分别保存field 和 value

  • 数据较大时,hash转ht编码(Dict)。触发条件:

    1. ziplist的元素数量超过了hash-max-ziplist-entries(默认512)

    2. ziplist种任意的entry大小超过了hash-max-ziplist-value(默认64字节)

image-20240430113745962

总结

  1. string

    • intset:全整数+数据小时

    • embstr:小于44字节

    • raw默认这种,采用SDS,最大512字节

  2. list

    • 3.2前:看情况使用ziplist和linkedlist(大)实现

    • 3.2后:统一使用quicklist实现

  3. set

    • intset:全数字 + 不超多一个阙值

    • dict

  4. zset

    • ziplist:数据量小时=> 头小,尾大;前ele,后score

    • skiplist(排序) + dict(唯一)

  5. hash

    • ziplist:数据量小时=> 头小,尾大;前field,后value

    • dict(唯一)

Linux网络模型

在Linux操作系统中,用户是不是没有直接的权限去操作文件的,而是通过调用系统内核的接口实现调用的。这里分为用户空间和内核空间两个概念了。

阻塞IO

  • 要读取数据,发现没有数据,就阻塞等待。

  • 在等待数据阶段和拷贝数据阶段这两个阶段都在阻塞。

image-20240430202355182

非阻塞IO

  • 通过反复轮询请求访问内核,看看是否有数据。

  • 等待阶段是非阻塞的(如果没有数据就返回一个错误代码),拷贝数据这个阶段是阻塞的

  • 非阻塞IO的关键在于减少阻塞等待的时间,使得线程或进程在等待IO操作完成的同时可以去做其他事情

image-20240430202549611

IO多路复用

在Linux系统中,开启了许多个FD,通过使用一个单线程检测各个FD是否完成,进行返回结果。

fd(文件描述符):Linux系统中任意一个资源的打开,包括socket。

实现方式

  • select模式

    1. 初始化fd_set

      1. 定义个存储fd的集合:fd_set

      2. 情况fd_set

      3. 将fd添加到fd_set中

    2. select函数的调用

      1. select函数调用,将fd_set拷贝到内核空间中

      2. 遍历fd_set,没有就绪则休眠

      3. 等待数据就绪被唤醒或超时

    3. 处理结果

      1. 检查返回值,看看有多少的fd准备好了

      2. 集合遍历,看看指定的fd是否在就绪集合中

      3. 处理就绪的fd

  • poll模式

    1. 初始化

      1. 创建pollfd数组,并添加关注的fd信息,数组大小自定义

    2. 调用poll函数

      1. 调用poll函数,将pollfd数组拷贝到内核中,转无上限链表存储

      2. 遍历fd,判断是否就绪

      3. 数据就绪/超时,拷贝pollfd数组到用户空间,返回fd就绪个数n

    3. 处理结果

      1. 用户进行判断n是否大于0

      2. 大于0,遍历poll数组,找到就绪的fd

  • epoll模式

    1. 初始化 epoll_create()

      1. 在内核空间创建一个eventpoll的结构体

      2. 结构体包括:rb_root rbr红黑树,list_head rdlist就绪列表

    2. 添加fd epoll_ctl()

      1. 添加fd添加到epoll的rbr红黑树中,并设置ep_poll_callback

      2. 当callback触发时,把对应的FD添加到rdlist就绪列表中

    3. 检查rdlist列表是否为空,不为空则直接返回就绪的FD数量 epoll_wait()

上述小结:

select模式的问题:

  • 能监听的FD不超过1024

  • 每次select都需要把要监听的fd拷贝到内核空间

  • 每次都要遍历所有的fd来判断就绪状态

poll模式的问题

  • poll利用链表解决了select监听fd上限问题,但仍需遍历所有的fd。如果监听较多,性能下降

epoll解决上述问题:

  • 基于epoll实例中,红黑树保存监听fd,理论上无上限,增删改查性能较高,性能不随着监听的fd数量增多而下降

  • 每个fd只需执行一次epoll_ctl添加到红黑树,之后epol_wait无需传递任何参数,无需重复拷贝fd到内核空间

事件通知机制

当rdlist中有数据可读时,调用epoll_wait可以得到通知,得到事务通知的模式有以下两种:

  1. levelTriggered(LT):当FD有数据可读时,会重复通知多次,知道数据处理完成(默认)

  2. EdgeTriggered(ET):当FD有数据可读时,只会通知一次,不管是数据是否完成

  • ET模式避免了LT模式可能出现的惊群现象

  • ET模式最好结合非阻塞IO读取FD数据,相对比LT复杂点,但是效率更高

惊群现象:当多个套接字(sockets)同时处于等待事务的状态,但实际上只有一个套接字会接收到数据通知时所发生的情况

Web服务中流程

image-20240502134005624

信号驱动IO

写真正的实现了非阻塞,但读时候,仍有阻塞。

用户应用与内核进行了一个绑定,如果没有数据,用户应用可以去做其他的事,但是当有数据的时候,会发送一个通知给用户应用,让他来处理事务

image-20240502134404879

异步IO

用户应用只用调用,内核会自动去做完一切,到最后通知。

但是有一个问题:如果在高并发请求下,疯狂的调用,直接会导致崩溃。所以需要进行调用限流,但是这个限流实现还是有点小麻烦的。

image-20240502134622371

总结

只有异步IO是异步的,其他都是同步。

image-20240502135014261

Redis网络模型

Redis是单线程还是多线程?

  • Redis核心业务部分(命令处理)是单线程

    • 处理客户端请求、执行命令时,Redis 主要依赖于单个线程来完成。这种设计简化了并发控制,提高了 Redis 在处理大量连接和请求时的效率。

  • 整体上Redis 是多线程

    • 在持久化过程中,执行IO操作会利用到多线程

    • 在某些高级特性如 Redis Sentinel、Redis Cluster 中,可能会使用多个线程来处理监视、故障转移等任务。

    • 在一些 Redis 模块中,可能会引入多线程以支持特定的功能或提高性能

    • 持久化操作或者部分计算密集型操作,Redis 会使用额外的线程来执行这些任务,以避免阻塞主线程。

为什么Redis要采用单线程?

  1. 简化设计和实现:单线程模型使得 Redis 的设计和实现变得相对简单,减少了并发控制和同步的复杂性。这降低了开发和维护的成本,并且使得 Redis 的代码更加清晰和易于理解。

  2. 多线程不会提高太大的性能提升:抛开持久化不说,Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟,而不是执行速度,因此多线程并不会带来巨大的性能提升。

  3. 高效利用CPU:虽然 Redis 是单线程的,但它可以利用多个 CPU 核心,并行地处理多个客户端请求。通过使用非阻塞 I/O 和事件驱动的方式,Redis 能够高效地利用 CPU 资源,并在处理高并发时表现出色。

  4. 减少上下文切换和内存开销:单线程模型减少了线程创建和销毁的开销,以及线程上下文切换的成本,从而提高了 Redis 在高并发环境下的性能和稳定性。此外,单线程模型也减少了为每个连接分配线程所需的内存开销。

image-20240502141530213

Redis单线程网络模型流程

image-20240502145202973

网络模型引入多线程

Redis处理时候速度非常快,但是在IO网络请求的速度没有那么快,容易出现网络阻塞,所以在网络读/写的时候加入多线程提高系统效率。

image-20240502145434457

Redis内存策略

过期key处理

  • Redis本身是一个key-value内存存储的数据库——redisDb。

  • key,value都保存在Dict结构中(两个Dict)

    • 一个Dict存储key-value

    • 一个记录key-TTL

结构:

  • dict:存储key-value

  • expires:存储key的TTL

image-20240502153120288

image-20240502153449030

Redis是如何知道一个key是否过期的?

  • 利用两个Dict分别记录key-value和key-ttl

  • 有两种策略发现是否过期,并将该过期key删除

    • 惰性删除:在访问一个key对象时,检查该key的存活事件,如果已过则删除

    • 周期删除:周期性抽样部分过期key,然后执行删除

      • (了解即可)SLOW:设置一个定时任务serverCron(),按照server.hz频率执行过期key清理

      • (了解即可)FAST:每个事件循环前会调用beforeSleep(),执行过期key

内存淘汰机制

内存淘汰:当Redis内存使用达到阙值的时候,Redis主动挑选部分Key删除释放更多内存。

内存淘汰策略

补充一下lru和lfu:

  • lru:最近最少使用。用当前事件减去最后一次访问时间,值越大越先淘汰

  • lfu:最少频率使用。会统计每个key的访问频率,值越小淘汰越优先

  • noeviction:不淘汰任何key,但是内存满了不允许写入新数据(默认)

  • ttl:淘汰最小TTL值

    • volatile-ttl:对设置了TTL的key,淘汰最小的TTL值

  • random:随机

    • volatile-random:对设置了TTL的key,随机进行淘汰

    • allkeys-random:对所有的key,随机进行淘汰

  • lru:最近最少使用的值(淘汰掉最近一段时间内最少被访问的数据)

    • volatile-lru:对设置了TTL的key,采用lru进行淘汰

    • allkeys-lru:对所有的key,采用lru进行淘汰

  • lfu:最少频率使用(淘汰访问频率低的)

    • volatile-lfu:对设置了TTL的key,采用lfu进行淘汰

    • allkeys-lfu:对所有的key,采用lfu进行淘汰

补充:

Redis中队TTL、LRU、LFU策略使用的时候,使用的是抽样部分淘汰

相关文章:

Redis进阶知识个人汇总

持久化 三种方式实现它的持久化&#xff1a; RDB持久化 全称Redis数据备份文件&#xff0c;又称Redis数据快照 这种就是将Redis内存中所有数据记录到磁盘中&#xff0c;当实例出故障后&#xff0c;从磁盘中读快照文件进行恢复数据。 一般使用bgsave指令实现 复制主线程得到一…...

从中序与后序遍历序列构造二叉树-力扣

中序遍历序列存放节点的顺序是左中右&#xff0c;后序遍历存放节点的顺序是左右中后序遍历序列的最后一个节点即为二叉树的根节点由于每个值在二叉树中都是唯一的&#xff0c;那么根据根节点的值&#xff0c;就可以将中序遍历序列一分为二&#xff0c;前部分存储的是根节点左子…...

操作系统期末复习(大题)

1. 进程调度 周转时间作业完成时刻-作业到达时刻 带权周转时间周转时间/服务时间 平均周转时间各个作业周转时间之和/作业个数 操作系统&#xff1a;周转时间和其他时间_系统为作业提供的时间-CSDN博客 2. 进程调度 3. 调度算法 4. 临界区互斥访问问题 即证明是否满足互斥&a…...

解决富文本中抖音视频无法播放的问题——403

问题 富文本中的抖音视频无法播放&#xff0c;资源状态码是403禁止访问打开控制台&#xff0c;可以看到在项目中打开&#xff0c;数据请求的请求头多了一个Referer: http://localhost:3000/而复制链接在新窗口直接打开&#xff0c;请求头中并不会携带Referer 解决方案 在ind…...

2024最新华为OD机试(C卷+D卷)真题目录+使用说明+在线评测

文章目录 &#x1f4d2;声明&#x1f39a;专栏介绍&#x1f4d6;试读文章&#x1f380;关于华为OD &#x1f9f7;真题目录2024最新 C卷 & D卷 目录(实时跟新中~)2024最新 C卷 & D卷 100分题目 (实时跟新中~)2024最新 C卷 & D卷 200分题目 (实时跟新中~) &#x1f4…...

hana 中的缓存视图功能,类似ORACLE 中的 物化视图功能

为什么启用物化视图、缓存视图这里就不过多解释了。 参考官方文章&#xff1a; Static Result Cache | SAP Help Portal 在 HANA中&#xff0c;视图的缓存分 静态结果缓存 和 动态结果缓存。 静态结果缓存和动态结果缓存是缓存查询结果以获得性能优势的可配置应用程序。 缓…...

express入门02静态资源托管

目录 1 搭建静态资源结构2 代码助手3 多目录托管4 服务器热启动总结 上一篇我们讲解了使用express搭建服务器的过程&#xff0c;服务器搭建好了之后&#xff0c;除了在地址栏里输入URL发起get请求或者post请求外&#xff0c;通常我们还需要访问静态资源&#xff0c;比如html、c…...

Java常见的引用类型

1、强引用&#xff1a;普通的变量引用&#xff0c;Student sutnew Student(); 2、软引用&#xff1a;堆内对象若未被引用&#xff0c;GC不会立刻删除&#xff0c;而是在堆内存空间不足时才会进行删除。 3、弱引用&#xff1a;GC触发&#xff0c;会立刻删除。 4、虚引用&am…...

使用易备数据备份软件,简单快速地备份 Oracle 数据库

易备数据备份软件能够以简单高效的方式&#xff0c;实现对 Oracle 数据库的保护。 易备数据备份软件数据库备份功能的关键特性 自动保护网站数据库及应用程序实时备份&#xff0c;不需要任何中断或数据库锁定基于日期和时间的备份任务计划可恢复到一个已存在的数据库或创建一…...

基于SSM+Jsp的交通事故档案管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…...

深度解析:ChatGPT全面测评——功能、性能与用户体验全景剖析

从去年底至今&#xff0c;由 OpenAI 发布的大规模语言模型 ChatGPT 引发了几乎所有科技领域从业者的高度关注。据瑞银集团的报告显示&#xff0c;自 2023 年 1 月起&#xff0c;仅两个月内&#xff0c;ChatGPT 的月活用户数便超过了 1 亿。 ChatGPT 被誉为“最强 AI”&#xff…...

领夹麦克风哪个品牌好?哪个麦克风好?揭秘无线麦克风十大排名!

​无线领夹麦克风因其便携性和高音质而备受青睐。今天&#xff0c;我要为大家推荐几款备受赞誉的无线领夹麦克风&#xff0c;它们不仅在音质上表现出色&#xff0c;更在设计和性能上各有千秋。这些麦克风不仅适合专业录音师使用&#xff0c;也适合普通用户在日常生活中的各种场…...

低代码开发:智能财务系统开发应用

在当今数字化时代&#xff0c;企业对于高效的财务管理系统需求日益增长。低代码开发平台为开发智能财务系统提供了快速、灵活的解决方案&#xff0c;使企业能够快速构建、定制和部署应用程序&#xff0c;提升财务管理效率。本文将探讨低代码开发在智能财务系统开发应用中的应用…...

Windows 10 找不到Microsoft Edge 浏览器

下载链接 了解 Microsoft Edge 手动下载浏览器 问题说明 一般来说&#xff0c;windows10系统应该是自带浏览器edge的&#xff0c;但有的电脑就是没有找到edge浏览器&#xff0c;可能系统是精简过的&#xff0c;可能是被卸载了。如下&#xff0c;控制面板确实没找到程序。 ​ …...

【react】useState 使用指南

React的useState是函数组件中用于管理状态(state)的Hook。以下是关于useState的使用指南,结合参考文章中的信息,以清晰、分点的方式表示: 1. 基本概念 useState是React函数组件中用于管理状态(state)的Hook。它接受一个初始状态值,并返回一个包含当前状态和一个用于更新…...

RK3588 Debian11进行源码编译安装Pyqt5

RK3588 Debian11进行源码编译安装Pyqt5 参考链接 https://blog.csdn.net/qq_38184409/article/details/137047584?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171808774816800222841743%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&…...

二叉树的前序遍历-力扣

二叉树的前序遍历&#xff0c;指先遍历中间节点&#xff0c;然后遍历左节点&#xff0c;然后遍历右节点&#xff0c;按照这个顺序进行递归即可。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* …...

千问Qwen7B chat:本地部署及网页端使用

基于前面的安装经验&#xff0c;千问大模型的本地部署并不算难&#xff0c;主要时间用在大模型文件的下载上。同时系统运行对硬件也有较高的要求&#xff0c;本机的硬件配置为N卡3060&#xff0c;显存12G。 使用conda创建虚拟环境&#xff0c;主要版本如下&#xff1a; Pyth…...

(27)ADC接口--->(002)FPGA实现AD7606接口

(002)FPGA实现AD7606接口 1 目录 (a)FPGA简介 (b)IC简介 (c)Verilog简介 (d)FPGA实现AD7606接口 (e)结束 1 FPGA简介 (a)FPGA(Field Programmable Gate Array)是在PAL (可编程阵列逻辑)、GAL(通用阵列逻辑)等可编程器件的基础上进一步发展的产物。…...

设计模式-设计模式分类

概述 23 种设计模式&#xff0c;分为创建型模式、结构型模式和行为型模式。另外&#xff0c;近来这一清单又增加了一些类别&#xff0c;例如&#xff0c;并发型模式、线程池模式、Java EE 企业技术的多层应用程序上的模式等。 一、创建型模式 1.工厂方法模式(Factory Method…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...