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策略使用的时候,使用的是抽样部分淘汰

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/24947.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

记一次源码部分丢失后补救过程

起因 最近植物大战僵尸杂交版玩的入迷&#xff0c;写了一个“神奇”小工具&#xff0c;来辅助游戏。用Git新建一个库&#xff0c;想把代码备份到GitHub&#xff0c;结果push错库了&#xff0c;无奈reset&#xff0c;结果把本地项目一起reset了&#xff0c;结果就是源代码丢失。…

k8s——secret配置资源管理

一、Secret 1.1 Secret定义 Secret是用来保存密码、token、密钥等敏感数据的k8s资源&#xff0c;这类数据虽然也可以存放在Pod或者镜像中&#xff0c;但是放在Secret中是为了更方便的控制如何使用数据&#xff0c;并减少暴露的风险。 1.2 Secret类型 kubernetes.io/service-ac…

SpringBoot+Vue网上超市系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 系统角色对应功能 用户管理员 系统功能截图

Cesium项目报错An error occurred while rendering. Rendering has stopped.

一般就是本地打开会报错&#xff0c;改成用本地服务器打开 全局安装一个live-server sudo cnpm i live-server -g然后新增一个package.json文件 npm init -y然后在package.json的scripts中增加一个命令 "server": "live-server ./ --port8181 --hostlocalhos…

AI图书推荐:用ChatGPT来写非虚构类书籍

这本书《用ChatGPT来写非虚构类书籍 》&#xff08;ChatGPT For KDP_ A manual from an experienced self-publisher to nonfiction authors for writing the book you were born to write with ChatGPT prompts mastering&#xff09;是一本专为非虚构类书籍作者编写的指南&am…

实习记录2

1.flowable框架参数传递大概流程 通过传递xml&#xff0c;传递到后端&#xff0c;然后后端去解析 2.vue封装组件 在 Vue.js 中创建可复用的自定义组件是一个常见的需求&#xff0c;这样可以提高代码的复用性和可维护性。下面是一个简单的步骤指南&#xff0c;帮助你创建一个…

嵌入式linux系统中利用I2C控制器应用开发详解

大家好,今天主要给大家分享一下,在linux系统上如何使用I2C进行应用开发详解。 l2C (Inter一Integrated Circuit BUS)是I2C BUS简称.中文为集成电路总线.是目前应用最广泛的总线之一。和IMX6ULL有些相关的是.刚好该总线是NXP前身的PHLIPS 设计。 第一:I2C协议概述 …

xml 取值错误 #{} boolean 一直为 false

取值时 #{param.msgStatus} 一直是false&#xff0c;java代码里面显示true。 <select id"findPageOaReading" resultType"com.focusin.data.office.func.dto.ProcessMessageInfoDTO">select i.*, t.template_name procdefNamefrom process_message_…

西瓜书总结——决策树原理+ID3决策树的模拟实现

西瓜书总结——决策树原理ID3决策树的模拟实现 前言1. 决策树结构2. 决策树的生成&#xff08;注意区分属性和类别&#xff09;3. 划分选择3.1 信息熵和信息增益3.2 增益率3.3 基尼指数&#xff08;鸡你指数&#xff09; 4. 剪枝处理4.1 预剪枝4.2 后剪枝 5. 连续值与缺失值处理…

二分+模拟,CF1461D - Divide and Summarize

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1461D - Codeforces 二、解题报告 1、思路分析 我们发现每次分裂操作结果都是固定的 我们从初始序列分裂出两个确定的子序列&#xff0c;两个确定的子序列又分裂出4个确定的子序列 那么也就是说…

【Python】解决Python报错:ZeroDivisionError: division by zero

​​​​ 文章目录 引言1. 错误详解2. 常见的出错场景2.1 直接除零2.2 变量导致的间接除零 3. 解决方案3.1 检查除数3.2 使用异常处理 4. 预防措施4.1 数据验证4.2 编写防御性代码 结语 引言 在Python中&#xff0c;尝试将一个数字除以零时&#xff0c;会抛出ZeroDivisionErr…

Duilib多标签选项卡拖拽效果:添加动画特效!

动画是小型界面库的“难题”、“通病” 几年前就有人分享了如何用direct UI制作多标签选项卡界面的方法。还有人出了一个简易的浏览器demo。但是他们的标签栏都没有Chrome浏览器那样的动画特效。 如何给界面添加布局是的动画特效呢&#xff1f; 动画使界面看起来高大上&#…

【录制,纯正人声】OBS录制软件,音频电流音,杂音解决办法,录制有噪声的解决办法

速度解决的方法 &#xff08;1&#xff09;用RNNoise去除噪声。RNNoise是一个开源的&#xff0c;效果不好的噪声去除器。使用方法就是点击滤镜&#xff0c;然后加噪声抑制RNNoise。【这方法不好用】 &#xff08;2&#xff09;用Krisp(https://krisp.ai/) 去除噪声。这个Kris…

探索C++ STL中的std::list:链式存储的艺术与实践

目录 ​编辑 引言 一、std::list详解 二、std::list的关键成员函数 三、示例代码 四、std::list与std::vector的对比 内存布局&#xff1a; 插入与删除&#xff1a; 迭代器稳定性&#xff1a; 五、应用场景 结语 引言 在C标准模板库(STL)中&#xff0c;std::list作…

skywalking学习

文章目录 前言一、skywalking单体安装部署1. 下载skywalking2. 部署oap和oap-ui服务3. 测试skywalking监控springboot应用 二、搭建swck(skywalking集群)1.安装k8s2.下载swck3.设置pod自动注入java agent 三、skywalking监控python四、skywalking监控cpp总结参考 前言 本文主要…

SSL/TLS和HTTPS

HTTPS就是用了TLS包装的Socket进行通信的HTTP 混合加密 被称为混合加密。具体过程如下&#xff1a; 使用非对称加密协商对称密钥&#xff1a; 在通信的开始阶段&#xff0c;通常由客户端和服务器使用非对称加密算法&#xff08;如RSA&#xff09;来协商一个对称密钥。通常情…

vue3中的ref与reactive的区别

目录 1、两者的区别底层实现响应式引用与响应式对象 2、用法3、vue3中声明的数组/对象3.1 通过reactive 声明的Array/Object&#xff0c;给它重新分配一个全新的对象时&#xff0c;会出错、或失去响应式效果 3.2 解决方案 4、cosnt 说明5、Proxy 与 definePropertyref 浅层响应…

人工智能与能源约束的矛盾能否化解

以下文章来源&#xff1a;澎湃新闻 人工智能技术在台前展示的是比特世界的算力、算法和数据&#xff0c;但其“轻盈的灵魂”背后则是土地、能源和水等物理世界“沉重的肉身”。根据本文三种情境的模拟测算&#xff0c;未来人工智能发展需要可持续的巨量能源支撑&#xff0c;能源…

基于Python的北京天气数据可视化分析

项目用到库 import numpy as np import pandas as pd import datetime from pyecharts.charts import Line from pyecharts.charts import Boxplot from pyecharts.charts import Pie,Grid from pyecharts import options as opts from pyecharts.charts import Calendar 1.2…

Python应用开发——30天学习Streamlit Python包进行APP的构建(5)

上几次我们已经将一些必备的内容进行了快速的梳理,让我们掌握了streanlit的凯快速上手,接下来我们将其它的一些基础函数再做简单的梳理,以顺便回顾我们未来可能用到的更丰富的函数来实现应用的制作。 st.write_stream 将生成器、迭代器或类似流的序列串流到应用程序中。 …