源码级别讲解 redis 底层数据结构

news2024/11/24 8:04:42

redis 底层数据结构

Redis作为Key-Value存储系统,数据结构如下:

image-20230508160801880

Redis没有表的概念,Redis实例所对应的db以编号区分,db本身就是key的命名空间。

比如:user:1000作为key值,表示在user这个命名空间下id为1000的元素,类似于user表的id=1000的 行。

1. RedisDB结构

Redis中存在“数据库”的概念,该结构由redis.h中的redisDb定义。 当redis 服务器初始化时,会预先分配 16 个数据库 所有数据库保存到结构 redisServer 的一个成员 redisServer.db 数组中 redisClient中存在一个名叫db的指针指向当前使用的数据库。

RedisDB结构体源码:

typedef struct redisDb {
    int id; //id是数据库序号,为0-15(默认Redis有16个数据库)
    long avg_ttl; //存储的数据库对象的平均ttl(time to live),用于统计
    dict *dict; //存储数据库所有的key-value
    dict *expires; //存储key的过期时间
    dict *blocking_keys;//blpop 存储阻塞key和客户端对象
    dict *ready_keys;//阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象
    dict *watched_keys;//存储watch监控的的key和客户端对象
} redisDb;

  • id
    id是数据库序号,为0-15(默认Redis有16个数据库)

  • dict
    存储数据库所有的key-value,后面要详细讲解

  • expires

    存储key的过期时间,后面要详细讲解

2. RedisObject结构

Value是一个对象。RedisObject 有五种对象:字符串对象(String)、列表对象(List)、哈希对象(Hash)、集合对象(Set)和有序集合对象(ZSet)。

2.1 结构信息概览

typedef struct redisObject {
    unsigned type:4;//类型 五种对象类型
    unsigned encoding:4;//编码
    void *ptr;//指向底层实现数据结构的指针
    //...
    int refcount;//引用计数
    //...
    unsigned lru:LRU_BITS; //LRU_BITS为24bit 记录最后一次被命令程序访问的时间
    //...
}robj;

2.1.1 4位type

type 字段表示对象的类型,占 4 位;REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有 序集合)。

当我们执行 type 命令时,便是通过读取 RedisObject 的 type 字段获得对象的类型

127.0.0.1:6379> type a1
string

2.1.2 4位encoding

encoding 表示对象的内部编码,占 4 位。每个对象有不同的实现编码。Redis 可以根据不同的使用场景来为对象设置不同的编码,大大提高了 Redis 的灵活性和效率。 通过 object encoding 命令,可以查看对象采用的编码方式。

127.0.0.1:6379> object encoding a1
"int"

2.1.3 24位LRU

lru 记录的是对象最后一次被命令程序访问的时间,( 4.0 版本占 24 位,2.6 版本占 22 位)。

高16位存储一个分钟数级别的时间戳,低8位存储访问计数(lfu : 最近访问次数)

lru----> 高16位: 最后被访问的时间

lfu----->低8位:最近访问次数

2.1.4 refcount

refcount 记录的是该对象被引用的次数,类型为整型。 refcount 的作用,主要在于对象的引用计数和内存回收。 当对象的refcount>1时,称为共享对象 Redis 为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对 象。

2.1.5 ptr

ptr 指针指向具体的数据,比如:set hello world,ptr 指向包含字符串 world 的 SDS。

2.2 7种type

2.2.1 字符串对象

C语言: 字符数组 “\0” Redis 使用了 SDS(Simple Dynamic String)。用于存储字符串和整型数据。

image-20230508161933789

struct sdshdr{
    //记录buf数组中已使用字节的数量
    int len;
    //记录 buf 数组中未使用字节的数量
    int free;
    //字符数组,用于保存字符串
    char buf[];
}

buf[] 的长度=len+free+1

SDS的优势:

1、SDS 在 C 字符串的基础上加入了 free 和 len 字段,获取字符串长度:SDS 是 O(1),C 字符串是 O(n)。 buf数组的长度=free+len+1

2、 SDS 由于记录了长度,在可能造成缓冲区溢出时会自动重新分配内存,杜绝了缓冲区溢出。

3、可以存取二进制数据,以字符串长度len来作为结束标识

使用场景:

SDS的主要应用在:存储字符串和整型数据、存储key、AOF缓冲区和用户输入缓冲。

2.2.2 跳跃表(重点)

跳跃表是有序集合(sorted-set)的底层实现,效率高,实现简单。跳跃表的基本思想: 将有序链表中的部分节点分层,每一层都是一个有序链表。

2.2.2.1 查找

在查找时优先从最高层开始向后查找,当到达某个节点时,如果next节点值大于要查找的值或next指针 指向null,则从当前节点下降一层继续向后查找。

举例:

image-20230508162357883

查找元素9,按道理我们需要从头结点开始遍历,一共遍历8个结点才能找到元素9。

第一次分层: 遍历5次找到元素9(红色的线为查找路径)

image-20230508162419956

第二次分层: 遍历4次找到元素9

image-20230508162435967

第三层分层: 遍历4次找到元素9

image-20230508162540628

这种数据结构,就是跳跃表,它具有二分查找的功能。

2.2.2.2 插入与删除

上面例子中,9个结点,一共4层,是理想的跳跃表。

通过抛硬币(概率1/2)的方式来决定新插入结点跨越的层数:

正面:插入上层

背面:不插入

达到1/2概率(计算次数)

2.2.2.3 删除

找到指定元素并删除每层的该元素即可

2.2.2.4 跳跃表特点:

  • 每层都是一个有序链表
  • 查找次数近似于层数(1/2)
  • 底层包含所有元素
  • 空间复杂度 O(n) 扩充了一倍
//跳跃表节点
typedef struct zskiplistNode {
    sds ele;
    /* 存储字符串类型数据 redis3.0版本中使用robj类型表示,
        但是在redis4.0.1中直接使用sds类型表示 */
    double score; //存储排序的分值
    struct zskiplistNode * backward; //后退指针,指向当前节点最底层的前一个节点
    /*
    层,柔性数组,随机生成1-64的值
    */
    struct zskiplistLevel {
        struct zskiplistNode * forward; //指向本层下一个节点
        unsigned int span; //本层下个节点到本节点的元素个数
    }
    level[];
}
zskiplistNode;
//链表
typedef struct zskiplist {
    //表头节点和表尾节点
    structz skiplistNode * header, * tail;
    //表中节点的数量
    unsigned long length;
    //表中层数最大的节点的层数
    int level;
}
zskiplist;

完整的跳跃表结构体:

image-20230508162943926

跳跃表的优势:

1、可以快速查找到需要的节点
2、可以在O(1)的时间复杂度下,快速获得跳跃表的头节点、尾结点、长度和高度。

应用场景:有序集合的实现

2.2.3 字典(重点+难点)

字典dict又称散列表(hash),是用来存储键值对的一种数据结构。
Redis整个数据库是用字典来存储的。(K-V结构)
对Redis进行CURD操作其实就是对字典中的数据进行CURD操作。

2.2.3.1 数组

数组:用来存储数据的容器,采用头指针+偏移量的方式能够以O(1)的时间复杂度定位到数据所在的内存地址。
Redis 海量存储 快

2.2.3.2 Hash函数

Hash(散列),作用是把任意长度的输入通过散列算法转换成固定类型、固定长度的散列值。
hash函数可以把Redis里的key:包括字符串、整数、浮点数统一转换成整数。
key=100.1 String “100.1” 5位长度的字符串
Redis-cli :times 33
Redis-Server : siphash
数组下标=hash(key)%数组容量(hash值%数组容量得到的余数)

2.2.3.3 Hash冲突

不同的key经过计算后出现数组下标一致,称为Hash冲突。
采用单链表在相同的下标位置处存储原始key和value
当根据key找Value时,找到数组下标,遍历单链表可以找出key相同的value

image-20230508164011042

2.2.3.4 Redis字典的实现

Redis字典实现包括:字典(dict)、Hash表(dictht)、Hash表节点(dictEntry)。

image-20230508164121250

Hash表
typedef struct dictht {
    dictEntry **table; // 哈希表数组
    unsigned long size; // 哈希表数组的大小
    unsigned long sizemask; // 用于映射位置的掩码,值永远等于(size-1)
    unsigned long used; // 哈希表已有节点的数量,包含next单链表数据
} dictht;

1、hash表的数组初始容量为4,随着k-v存储量的增加需要对hash表数组进行扩容,新扩容量为当前量
的一倍,即4,8,16,32
2、索引值=Hash值&掩码值(Hash值与Hash表容量取余)

Hash表节点
typedef struct dictEntry {
    void * key; // 键
    union { // 值v的类型可以是以下4种类型
        void * val;
        uint64_t u64;
        int64_t s64;
        double d;
    }
    v;
    struct dictEntry * next; // 指向下一个哈希表节点,形成单向链表 解决hash冲突
}dictEntry;

key字段存储的是键值对中的键
v字段是个联合体,存储的是键值对中的值。
next指向下一个哈希表节点,用于解决hash冲突

image-20230508164246078

dict字典
typedef struct dict {
    dictType * type; // 该字典对应的特定操作函数
    void * privdata; // 上述类型函数对应的可选参数
    dictht ht[2];
    /* 两张哈希表,存储键值对数据,ht[0]为原生
    哈希表,
    ht[1]为 rehash 哈希表 */
    long rehashidx;
    /*rehash标识 当等于-1时表示没有在
    rehash,
    否则表示正在进行rehash操作,存储的值表示
    hash表 ht[0]的rehash进行到哪个索引值
    (数组下标)*/
    int iterators; // 当前运行的迭代器数量
}dict;

type字段,指向dictType结构体,里边包括了对该字典操作的函数指针

Redis字典除了主数据库的K-V数据存储以外,还可以用于:散列表对象、哨兵模式中的主从节点管理等
在不同的应用中,字典的形态都可能不同,dictType是为了实现各种形态的字典而抽象出来的操作函数
(多态)。
完整的Redis字典数据结构:

image-20230508164401179

2.2.3.5 字典扩容

字典达到存储上限,需要rehash(扩容)

扩容流程:

image-20230508164435460

说明:

  1. 初次申请默认容量为4个dictEntry,非初次申请为当前hash表容量的一倍。
  2. rehashidx=0表示要进行rehash操作。
  3. 新增加的数据在新的hash表h[1]
  4. 修改、删除、查询在老hash表h[0]、新hash表h[1]中(rehash中)
  5. 将老的hash表h[0]的数据重新计算索引值后全部迁移到新的hash表h[1]中,这个过程称为
    rehash。
2.2.3.6 渐进式rehash

当数据量巨大时rehash的过程是非常缓慢的,所以需要进行优化。服务器忙,则只对一个节点进行rehash 服务器闲,可批量rehash(100节点)
应用场景:
1、主数据库的K-V数据存储
2、散列表对象(hash)
3、哨兵模式中的主从节点管理

2.2.4 压缩列表

压缩列表(ziplist)是由一系列特殊编码的连续内存块组成的顺序型数据结构,节省内存。是一个字节数组,可以包含多个节点(entry)。每个节点可以保存一个字节数组或一个整数。压缩列表的数据结构如下:

image-20230508164634547

zlbytes: 压缩列表的字节长度
zltail: 压缩列表尾元素相对于压缩列表起始地址的偏移量
zllen: 压缩列表的元素个数
entry1…entryX : 压缩列表的各个节点
zlend: 压缩列表的结尾,占一个字节,恒为0xFF(255)
entryX 元素的编码结构:

image-20230508164649973

previous_entry_length: 前一个元素的字节长度
encoding: 表示当前元素的编码
content: 数据内容
ziplist结构体如下:

typedef struct zlentry {
    unsigned int prevrawlensize; //previous_entry_length字段的长度
    unsigned int prevrawlen; //previous_entry_length字段存储的内容
    unsigned int lensize; //encoding字段的长度
    unsigned int len; //数据内容长度
    unsigned int headersize; //当前元素的首部长度,即previous_entry_length字段长度与 encoding字段长度之和。
    unsigned char encoding; //数据类型
    unsigned char * p; //当前元素首地址
}
zlentry;

应用场景:

sorted-set和hash元素个数少且是小整数或短字符串(直接使用)
list用快速链表(quicklist)数据结构存储,而快速链表是双向列表与压缩列表的组合。(间接使用)

2.2.5 整数集合

整数集合(intset)是一个有序的(整数升序)、存储整数的连续存储结构。

当Redis集合类型的元素都是整数并且都处在64位有符号整数范围内(2^64),使用该结构体存储。

127.0.0.1:6379> sadd set:001 1 3 5 6 2
(integer) 5
127.0.0.1:6379> object encoding set:001
"intset"
127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"

intset的结构图如下:

image-20230508164945228

typedef struct intset{
    //编码方式
    uint32_t encoding;
    //集合包含的元素数量
    uint32_t length;
    //保存元素的数组
    int8_t contents[];
}intset;

应用场景:

可以保存类型为int16_t、int32_t 或者int64_t 的整数值,并且保证集合中不会出现重复元素。

2.2.6 快速列表(重要)

快速列表(quicklist)是Redis底层重要的数据结构。是列表的底层实现。(在Redis3.2之前,Redis采用双向链表(adlist)和压缩列表(ziplist)实现。)在Redis3.2以后结合adlist和ziplist的优势Redis设计出了quicklist。

127.0.0.1:6379> lpush list:001 1 2 5 4 3
(integer) 5
127.0.0.1:6379> object encoding list:001
"quicklist"

双向列表(adlist)

image-20230508170255159

双向链表优势:

  1. 双向:链表具有前置节点和后置节点的引用,获取这两个节点时间复杂度都为O(1)。
  2. 普通链表(单链表):节点类保留下一节点的引用。链表类只保留头节点的引用,只能从头节点插入删除
  3. 无环:表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL,对链表的访问都是以 NULL 结束。环状:头的前一个节点指向尾节点
  4. 带链表长度计数器:通过 len 属性获取链表长度的时间复杂度为 O(1)。
  5. 多态:链表节点使用 void* 指针来保存节点值,可以保存各种不同类型的值。

快速列表

quicklist是一个双向链表,链表中的每个节点时一个ziplist结构。quicklist中的每个节点ziplist都能够存储多个数据元素。

image-20230508170435527

quicklist的结构定义如下:

typedef struct quicklist {
    quicklistNode * head; // 指向quicklist的头部
    quicklistNode * tail; // 指向quicklist的尾部
    unsigned long count; // 列表中所有数据项的个数总和
    unsigned int len; // quicklist节点的个数,即ziplist的个数
    int fill: 16; // ziplist大小限定,由list-max-ziplist-size给定(Redis设定)
    unsigned int compress: 16; // 节点压缩深度设置,由list-compress-depth给定(Redis设定)
} quicklist;

quicklistNode的结构定义如下:

typedef struct quicklistNode {
    struct quicklistNode * prev; // 指向上一个ziplist节点
    struct quicklistNode * next; // 指向下一个ziplist节点
    unsigned char * zl; // 数据指针,如果没有被压缩,就指向ziplist结构,反之指向 quicklistLZF结构
    unsigned int sz; // 表示指向ziplist结构的总长度(内存占用长度)
    unsigned int count: 16; // 表示ziplist中的数据项个数
    unsigned int encoding: 2; // 编码方式,1--ziplist,2--quicklistLZF
    unsigned int container: 2; // 预留字段,存放数据的方式,1--NONE,2--ziplist
    unsigned int recompress: 1; // 解压标记,当查看一个被压缩的数据时,需要暂时解压,标记此参数为 1, 之后再重新进行压缩
    unsigned int attempted_compress: 1; // 测试相关
    unsigned int extra: 10; // 扩展字段,暂时没用
}quicklistNode;

数据压缩

quicklist每个节点的实际数据存储结构为ziplist,这种结构的优势在于节省存储空间。为了进一步降低ziplist的存储空间,还可以对ziplist进行压缩。Redis采用的压缩算法是LZF。其基本思想是:数据与前面重复的记录重复位置及长度,不重复的记录原始数据。
压缩过后的数据可以分成多个片段,每个片段有两个部分:解释字段和数据字段。quicklistLZF的结构体如下:

typedef struct quicklistLZF {
    unsigned int sz; // LZF压缩后占用的字节数
    char compressed[]; // 柔性数组,指向数据部分
} quicklistLZF;

应用场景

列表(List)的底层实现、发布与订阅、慢查询、监视器等功能。

3 10种encoding 编码

encoding 表示对象的内部编码,占 4 位。Redis通过 encoding 属性为对象设置不同的编码对于少的和小的数据,Redis采用小的和压缩的存储方式,体现Redis的灵活性。大大提高了 Redis 的存储量和执行效率。

比如Set对象:

intset : 元素是64位以内的整数
hashtable:元素是64位以外的整数

如下所示:

127.0.0.1:6379> sadd set:001 1 3 5 6 2
(integer) 5
127.0.0.1:6379> object encoding set:001
"intset"
127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"

3.1 String

int、raw、embstr

int

REDIS_ENCODING_INT(int类型的整数)

127.0.0.1:6379> set n1 123
OK
127.0.0.1:6379> object encoding n1
"int"

embstr

REDIS_ENCODING_EMBSTR(编码的简单动态字符串)

小字符串 长度小于44个字节

127.0.0.1:6379> set name:001 zhangfei
OK
127.0.0.1:6379> object encoding name:001
"embstr"

raw

REDIS_ENCODING_RAW (简单动态字符串)

大字符串 长度大于44个字节

127.0.0.1:6379> set address:001
asdasdasdasdasdasdsadasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdas
dasdasdas
OK
127.0.0.1:6379> object encoding address:001
"raw"

3.2 list

列表的编码是quicklist。
REDIS_ENCODING_QUICKLIST(快速列表)

127.0.0.1:6379> lpush list:001 1 2 5 4 3
(integer) 5
127.0.0.1:6379> object encoding list:001
"quicklist"

3.3 hash

散列的编码是字典压缩列表

dict

REDIS_ENCODING_HT(字典)

当散列表元素的个数比较多或元素不是小整数或短字符串时。

127.0.0.1:6379> hmset user:003
username111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111 zhangfei password 111 num
2300000000000000000000000000000000000000000000000000
OK
127.0.0.1:6379> object encoding user:003
"hashtable"

ziplist

REDIS_ENCODING_ZIPLIST(压缩列表)
当散列表元素的个数比较少,且元素都是小整数或短字符串时。

127.0.0.1:6379> hmset user:001 username zhangfei password 111 age 23 sex M
OK
127.0.0.1:6379> object encoding user:001
"ziplist"

3.4 set

集合的编码是整形集合字典

intset

REDIS_ENCODING_INTSET(整数集合)
当Redis集合类型的元素都是整数并且都处在64位有符号整数范围内(<18446744073709551616)

127.0.0.1:6379> sadd set:001 1 3 5 6 2
(integer) 5
127.0.0.1:6379> object encoding set:001
"intset"

dict

REDIS_ENCODING_HT(字典)

当Redis集合类型的元素都是整数并且都处在64位有符号整数范围外(>18446744073709551616)

127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"

3.5 zset

有序集合的编码是压缩列表和跳跃表+字典

ziplist

REDIS_ENCODING_ZIPLIST(压缩列表)
当元素的个数比较少,且元素都是小整数或短字符串时。

127.0.0.1:6379> zadd hit:1 100 item1 20 item2 45 item3
(integer) 3
127.0.0.1:6379> object encoding hit:1
"ziplist"

skiplist + dict

REDIS_ENCODING_SKIPLIST(跳跃表+字典)
当元素的个数比较多或元素不是小整数或短字符串时。

127.0.0.1:6379> zadd hit:2 100
item1111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111 20 item2 45 item3
(integer) 3
127.0.0.1:6379> object encoding hit:2
"skiplist"

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

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

相关文章

二叉树的层级遍历以及[NOIP2015 普及组] 扫雷游戏、有效时间的数目

一、二叉树的层级遍历 二叉树的层级遍历看着比其他遍历简单&#xff0c;但是我感觉实施起来却比其他遍历难&#xff0c;它主要是通过队列实现的 比如在这样的一颗二叉树中 我没先将a入队 队列&#xff1a;a 当a出队的时候就将它的左儿子和右儿子入队 队列&…

一图看懂 toml 模块:用于解析和创建TOML(Tom‘s Obvious, Minimal Language)的Python库, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 [TOC](一图看懂 toml 模块&#xff1a;用于解析和创建TOML(Tom’s Obvious, Minimal Language)的Python库, 资料整理笔记&#xff08;大全&#xff09;) ☘️摘要 全文介绍系统内置…

leap模型重点关注技术,如:能源结构清洁转型、重点领域如工业、交通节能减排降耗、新能源发电系统及发电成本最优化、区域碳达峰碳中和实现路径设计及政策评估

模型简介&#xff1a; 中文名&#xff1a;LEAP模型 外文名&#xff1a;Long Range Energy Alternatives Planning System/ Low emission analysis platform 采用部门分析法建立的LEAP模型&#xff08;长期能源可替代规划模型&#xff09;是一种自下而上的能源-环境核算工具&a…

Spark大数据处理讲课笔记3.8 Spark RDD典型案例

文章目录 零、本节学习目标一、利用RDD计算总分与平均分&#xff08;一&#xff09;提出任务&#xff08;二&#xff09;准备工作1、启动HDFS服务2、启动Spark服务3、在本地创建成绩文件4、将成绩文件上传到HDFS &#xff08;三&#xff09;实现步骤1、打开RDD项目2、创建计算总…

FS2455高效率的同步降压DC-DC转换器5A输出电流

概述 FS2455是一种高效率的同步降压DC-DC转换器&#xff0c;具有5A输出电流。 FS2455在4.5V到30V的宽输入电压范围内工作&#xff0c; 集 成主开关和同步开关&#xff0c;具有非常低的RDS&#xff08;ON&#xff09;以最小化传导损失。 FS2455具有轻载时的应用和高效率。此外…

[GFCTF 2021]文件查看器(GZ、过滤器、phar) day4

打开界面直接一个登录界面&#xff0c;直接admin/admin登录进去 。 进来之后发现是一个文件查看器的功能 随便输入了点东西发现了报错&#xff0c;然后读取文件的功能&#xff0c;输入Files.classs.php发现读取不成功 换了个index.php <?phpfunction __autoload($classN…

无效的目标发行版: 11

背景&#xff1a;最近在研究es&#xff0c;想着弄一个连接es集群的springboot的工程&#xff0c;然后就在网上找到一个&#xff0c;结果弄到本地运行时&#xff0c;报错了“ 无效的目标发行版: 11 ” 看着报错就知道肯定是你导入的项目和你本地的JDK版本不匹配了&#xff0c;然…

手把手教你如何将安卓手机数据导入iPhone!【详解】

案例&#xff1a;安卓数据导入苹果手机 【大神们&#xff0c;刚换了新的苹果手机&#xff0c;原本的安卓手机数据怎么导入新手机&#xff1f;】 想要换用iPhone&#xff0c;但是又不想丢失安卓手机里的重要数据怎么办&#xff1f;如何将安卓手机数据导入iphone&#xff1f;本文…

如何学习5G网络优化才能拿高薪?我已摆烂,各位努力!

“内卷”和“躺平” 有的人卷成了麻花 有的人选择了躺下 毕竟只要躺得够平&#xff0c;就卷不到我 但是更多的人选择在“内卷”与“躺平”的徘徊抉择中 “躺”的核心是休息&#xff0c;“卷”的本质是提升 但是在优橙教育学习5G网络优化 大家学会将两者融合 学习的时候…

玩转ChatGPT:快速制作PPT

一、写在前面 首先还是让小Chat推销下自己&#xff1a; 你是否曾经为制作 PPT 而烦恼&#xff1f;现在有了 ChatGPT&#xff0c;再也不必担心灵感枯竭啦&#xff01;使用 ChatGPT 撰写 PPT 可以让你轻松地组织思路、快速得到内容&#xff0c;无需任何营销口号&#xff0c;Cha…

【算法与数据结构】队列

队列 队列&#xff1a;结构定义 队列是有一篇连续的存储区&#xff0c;其实连续性不重要&#xff0c;而是队列需要保持一个特性&#xff1a; 从队首出元素&#xff0c;从队尾入元素。这一点与顺序表不一样&#xff0c;元素加入的位置不一样 队列&#xff1a;只允许从尾部加入…

P1003 [NOIP2011 提高组] 铺地毯

题目提供者 CCF_NOI 难度 普及- 此篇必须看到底&#xff01; 题目描述 为了准备一个独特的颁奖典礼&#xff0c;组织者在会场的一片矩形区域&#xff08;可看做是平面直角坐标系的第一象限&#xff09;铺上一些矩形地毯。一共有 n 张地毯&#xff0c;编号从 1 到 n。现在…

电力物联网是什么?在智能配电系统中有什么作用?

摘要&#xff1a;在社会经济和科学技术不断发展中&#xff0c;配电网实现了角色转变&#xff0c;传统的单向供电服务形式已经被双向能流服务形式取代&#xff0c;社会多样化的用电需求也得以有效满足。随着物联网技术的发展&#xff0c;泛在电力物联网开始应用于当今的电力系统…

便携式挂钩型儿童椅 标准ASTMF1235测试项目周期多久?

便携式挂钩型儿童椅 适用于6 个 月至 3 岁之间的儿童&#xff0c;体重不超过 37 磅&#xff0c;并具 备自主协调坐姿的能力。 那么该类产品上亚马逊需要做下面的检测&#xff1a; 便携式儿童外出餐椅 ASTM F1235-18 和 CPSIA&#xff08;铅、邻苯二甲酸盐&#xff09; 亚马逊…

新手开始学【网络安全】要怎么入门?

前言&#xff1a;网络安全如何从零开始学习&#xff0c;少走弯路&#xff1f; 目录&#xff1a; 一&#xff0c;怎么入门&#xff1f; 1、Web 安全相关概念&#xff08;2 周&#xff09;2、熟悉渗透相关工具&#xff08;3 周&#xff09;3、渗透实战操作&#xff08;5 周&…

M304A-ZN-当贝纯净桌面-卡刷固件包-内有教程

M304A-ZN-当贝纯净桌面-卡刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&#xff0c;运行速度提升…

用ChatGPT三分钟免费做出数字人视频- 提升自媒体魅力

用ChatGPT三分钟免费做出数字人视频- 提升自媒体魅力 一、ChatGPT产生文案二、腾讯智影网站三、选择一个2D数字人四、粘贴文本五、编辑自定义&#xff0c;合成六、资源七、其他数字人平台推荐八、生成视频预览 本教程收集于&#xff1a;AIGC从入门到精通教程汇总 操作指引 Ch…

KingbaseES V8R3 集群运维系列 -- sync_flag参数配置

​案例说明&#xff1a; 在KingbaseES V8R3集群一主二备的架构中&#xff0c;配置了流复制为同步(sync)模式&#xff0c;但是集群启动后&#xff0c;流复制状态中显示备库是async模式(备库和主库数据已经同步)&#xff0c;从备库的recovery.log日志也可以看到&#xff0c;备库启…

记录--前端实现点击选词功能

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 今天有一个需求&#xff0c;点击选中某个英文单词&#xff0c;然后对这个单词做一些处理&#xff0c;例如高亮背景、查看一些详细信息等等&#xff0c;今天简单实现了一下&#xff0c;效果如下&#x…

ChatGLM-6B本地cpu部署

ChatGLM-6B是清华团队研发的机器人对话系统&#xff0c;类似ChatGPT&#xff0c;但是实际相差很多&#xff0c;可以当作一个简单的ChatGPT。 ChatGLM部署默认是支持GPU加速&#xff0c;内存需要32G以上。普通的机器无法运行。但是可以部署本地cpu版本。 本地部署&#xff0c;需…