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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NVeloDocx一个基于NVelocity的word模版引擎

NVeloDocx是一个基于NVelocity的Word模版引擎&#xff0c;目前主要是用于E6低代码开发平台供用户轻松制作各种Word报告模版。 有以下优点&#xff1a; 1、完全的NVelocity语法&#xff1b; 2、直接在Word中写NVelocity脚本&#xff0c;使用非常非常方便&#xff1b; 3、完全兼…

值类型和引用类型在使用和存储上的区别

使用上的区别 //值类型 int a 10; //引用类型 int[] arr new int[] { 1, 2, 3, 4, 5 };//声明了一个b让其等于之前的a int b a; //声明了一个arr2让其等于之前的arr int[] arr2 arr; Console.WriteLine("a{0},b{1}", a, b); Console.WriteLine("arr[0]{0},…

【SQLAlChemy】如何连接数据库?

使用SQLAlChemy连接数据库 导入包 首先&#xff0c;导入创建数据库引擎的包。 from sqlalchemy import create_engine编写数据库配置 SQLALCHEMY_DATABASE_URL "mysql://root:123456789127.0.0.1:3306/tortoise"字段解释&#xff1a; mysql:&#xff1a;这是数…

关于python中的列表和元组

变量就是一块内存空间&#xff0c;用来表示/存储数据 如果表示的数据较少&#xff0c;直接定义几个变量就行了 但是也有的时候&#xff0c;要表示的数据就比较多&#xff0c;如果只是通过定义一个变量来表示一个数据的话&#xff0c;这样的工作效率太低&#xff0c; 所以在p…

QT学习之标签+鼠标/定时器

标签鼠标 QEvent 需求鼠标进入控件范围会进行相关操作 在QEnterEvent类中 [virtual protected] void QWidget::enterEvent(QEnterEvent *event)虚成员函数可以进行重写 实现鼠标进入或离开Lable会打印输出 新建一个父类为QWidget的名字为mylable的C类文件 先声明在.h文件这…

[HGAME 2023 week4]shellcode

看题目&#xff0c;将base64解密&#xff0c;然后dump下来&#xff0c;再拉进ida里&#xff0c;发现为tea加密 在tea加密中得到key 密文就是另外的一个文件 exp import re from ctypes import *import libnumdef decrypt(v, k):v0, v1 c_uint32(v[0]), c_uint32(v[1])delta…

HTML做成一个炫酷跳动爱心的页面

大家好&#xff0c;今天制作制作一个炫酷跳动爱心的页面&#xff01; 先看具体效果&#xff1a; 要创建一个炫酷跳动爱心的HTML页面&#xff0c;你可以使用HTML、CSS和JavaScript的组合。以下是一个简单的示例&#xff0c;它使用CSS动画和JavaScript来实现跳动效果。 首先&…

【NetTopologySuite类库】C#生成带约束(线、面)的Delaunay三角网

介绍 API地址&#xff1a;https://nettopologysuite.github.io/NetTopologySuite/api/NetTopologySuite.Triangulate.ConformingDelaunayTriangulationBuilder.html#NetTopologySuite_Triangulate_ConformingDelaunayTriangulationBuilder_Constraints 约束为线 效果图 红色…

计算机系统基础笔记(12)——控制

前言 在持续输出ing 一、条件码 1.处理器状态&#xff08;x86-64&#xff0c;部分的&#xff09; 当前程序的执行信息 ◼ 临时数据 ◼ 运行时栈的位置&#xff08;栈顶&#xff09; ◼ 当前代码控制点的位置&#xff08;即将要执行的指令地址&#xff09; ◼ 最近一次指令执…

使用缓存降低数据库并发读写方案探索

文章目录 前言缓存设计思想缓存划分缓存应用时机 客户端缓存浏览器缓存网关或代理服务器缓存CDNPCDN 服务端缓存本地缓存本地缓存实现Java堆缓存memcached/ecachecaffeineORM框架一级/二级缓存 分布式缓存分布式缓存优缺点分布式缓存实现分布式缓存实施过程可能遇到问题分布式缓…

【模拟-BM100 设计LRU缓存结构】

题目 BM100 设计LRU缓存结构 描述 设计LRU(最近最少使用)缓存结构&#xff0c;该结构在构造时确定大小&#xff0c;假设大小为 capacity &#xff0c;操作次数是 n &#xff0c;并有如下功能: Solution(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存get(key)&am…

【C#】WinForm关闭新(二级)界面使主程序关闭

参考视频&#xff1a;https://www.bilibili.com/video/BV1JY4y1G7jo?p14&vd_source1c57ab1b2e551da5b65c0dfb0f05a493 1.背景介绍 主程序界面&#xff0c;点击弹出二级界面&#xff08;同时隐藏主界面&#xff09;&#xff0c;不做任何设置&#xff0c;这时关闭二级界面…

SpringCloud-OpenFeign拓展-连接池、最佳使用方法、日志输出

目录 1 OpenFeign连接池 1.1 常见连接类型 1.2 连接池使用方法 1.2.1 引入依赖 1.2.2 开启连接池功能 1.2.3 配置完成&#xff0c;重启实例即可&#xff0c;底层将更改设置。 2 OpenFeign最佳使用方法 2.1 每个微服务都是单独的project&#xff0c;内部有三个独立模块 …

脖子以下是人机交互,脖子以上是人机融合智能

“脖子以下是人机交互&#xff0c;脖子以上是人机融合智能”这句话是当前人与人工智能配合技术发展的一种形象描述&#xff0c;一个是生理物理&#xff0c;一个是人脑电脑&#xff1a; 1、人机交互的重要性 脖子以下的人机交互在当前的人工智能系统中扮演着重要的角色。人机交互…

Leetcode1161. 最大层内元素和

Every day a Leetcode 题目来源&#xff1a;1161. 最大层内元素和 解法1&#xff1a;层序遍历 每次以「层」为单位进行拓展&#xff0c;统计该层的元素和&#xff0c;维护处理过程中的最大值层数和&#xff0c;以及层深度。 代码&#xff1a; /** lc appleetcode.cn id116…

Unity 编辑器扩展,获取目录下所有的预制件

先看演示效果 实现方案 1创建几个用于测试的cube 2&#xff0c;创建一个Editor脚本 3&#xff0c;编写脚本内容 附上源码 using UnityEditor; using UnityEngine;public class GetPrefeb : EditorWindow {private string folderPath "Assets/Resources"; // 指定预…

两款好用的IOS、Android图片处理应用

GIF 小助手 GIF工具包是一个简单实用的GIF动画编辑器&#xff0c;目前仅支持IOS平台。 使用该软件&#xff0c;可以将多个图像、视频和现场照片创建为gif。 主要功能&#xff1a; 多种输入源&#xff1a;用户可以将多个图片、视频或Livephoto转换成GIF动图。 编辑功能&#…

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

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

【C++】用红黑树封装map、set

用红黑树封装map、set 1. 红黑树1.1 模板参数的控制1.1.1 Value1.1.2 KeyOfValue 1.2 正向迭代器1.2.1 构造函数1.2.2 begin()end()1.2.3 operator()1.2.4 operator--()1.2.5 operator*()1.2.6 operator->()1.2.7 operator()1.2.8 operator!()1.2.9 总代码 1.3 反向迭代器1.…

vue2的element的table组件使用form校验

1.需求描述 vue2有时候做自增表格el-table&#xff0c;希望能够带一些校验&#xff0c;但又不想手搓校验逻辑&#xff0c;可以借用el-form的校验逻辑。 2.代码处理 1. html <template><div class"sad-cont"><el-form ref"form" :model&…