【2024最新版,redis7】redis底层的10种数据结构

news2024/11/16 2:14:00

前言:本文redis版本:7.2.4
本文语雀原文地址(首发更新):https://www.yuque.com/wzzz/redis/xg2cp37kx1s4726y
本文CSDN转载地址: https://blog.csdn.net/u013625306/article/details/136842107

1. 常见的数据结构

Redis常见的数据结构有5种: String,List, Set, ZSet, Hash,这只是对外的数据结构
Redis对内的底层数据结构具体实现方法还有如下几种:int, raw, embstr, linkedlist, ziplist, hashtable, intset, skiplist,quicklist,listpack
他们之间的映射关系如下
在这里插入图片描述

2. Redis对象基础知识

Redis对象,就是不管你是什么类型的redis变量,都必须要有这个对象,可以想象是一种对象头,就比如你java中所有的类,不管是系统的,新建的,他们都有一个Object父类
Redis对象的定义如下:

typedef struct redisobject{
    // 类型 4bit 即用于表示[String, List, Set, ZSet, Hash, Geo, HyperLogLog, Stream, Bitmaps]中的一种
    unsigned type:4;
    // 编码方式 4bit 即用于表示[int, embstr, raw, ziplist...]中的一种
    unsigned encoding:4;
    // LRU时间 24bit (相对于server.lrulock)
    unsigned lru:24;
    // 引用计数 32bit redis里面的数据可以通过引用计数进行共享
    int refcount:32;
    // 用于指向具体对象的指针 64bit,比如指向string, list等等
    void *ptr;
}

一个redisobject占用的存储空间:4+4+24+32+64=128bit/8=16字节

3. String底层数据结构

3.1 int类型

首先 object encoding key这个命令可以返回key在redis中内部的编码方式
当保存的字符串是数字类型时,长度≤19位,此时内部就会用int表示
image.pngimage.png
当保存的是个20位数字时
在这里插入图片描述

image.png
当保存的字符串首位数字是0时,不会用int表示,因为int前面的0存可以存,但读不出来呀
image.pngimage.png

3.2 embstr类型

embstr类型是redis专门用来保存短字符串的一种优化编码,其结构如下图所示
embstr底层编码方式
该结构中,redisobject是必须有的,每个redis对象都有。embstr使用时,只分配一次内存空间,因为redisobject和sdshdr是连续的,所以在一起分配,当embstr字符串扩大时,也就照成了要重新给embstr类型的字符串重新分配内存空间,所以redis中为了杜绝这种情况,embstr字符串是只读的,不能修改,如果修改了,就会变为raw编码方式,无论其长度是否达到了44字节

3.3 raw类型

raw类型是redis用来保存,可变长的,可修改的字符串,其结构如下图所示:
raw编码的字符串对象
如图所示,raw方式保存字符串时,会进行两次申请内存空间,第一次是给redisobject申请,第二次是给sdshdr申请。
这样的方式,虽然比embstr多申请一次内存空间,但是这种方式当字符串长度发生扩展时,只用重新申请sdshdr的内存就行了,然后修改ptr指针,可以有效防止内存碎片。

3.4 SDS 简单动态字符串

3.4.1 3.2版本之前

SDS的结构定义在sds.h文件中,以前版本的SDS比较简单,有三个属性,分别是len, free, buf数组

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

3.4.2 3.2版本及之后

在3.2版本之后,根据字符串长度的不同,分别初始化不同的sds

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

3.2版本之后,会根据字符串的长度来选择对应的数据结构

static inline char sdsReqType(size_t string_size) {
    if (string_size < 1<<5)  // 32
        return SDS_TYPE_5;
    if (string_size < 1<<8)  // 256
        return SDS_TYPE_8;
    if (string_size < 1<<16)   // 65536 64k
        return SDS_TYPE_16;
    if (string_size < 1<<32)  // 4294967296 4G
        return SDS_TYPE_32;
    return SDS_TYPE_64;
}

以下是raw编码方式下sdshdr8的一个字符串示例:

3.5 SDS动态字符串与C语言字符串的区别

C语言使用长度N+1的字符数组来表示长度为N的字符串,字符数组的最后一位是’\0’, 用以表示字符串的结尾,但是这种方式不能满足redis对字符串的安全性,效率和功能上的要求,所以redis在字符串上面使用了sds动态字符串来保存字符串,相比与c语言字符串,sds动态字符串有以下优点

C语言字符串SDS动态字符串SDS的优点
C语言中没有保存字符串长度,当想获取字符串长度时,需要遍历字符串,统计长度,时间复杂度O(n)SDS结构中保存了字符串的长度len, 当程序想获取字符串的长度时,直接访问len属性即可,时间复杂度为O(1),确保了获取字符串长度不会成为redis的性能瓶颈常数复杂度获取字符串长度
  • C语言中如果对字符串进行操作,如果是增长字符串,会导致内存的重新分配。
  • 如果是截取字符串,未截取的地方需要被释放掉,如果没有释放,就会导致内存溢出
    | SDS结构中,对字符串数组采用预申请,len属性记录已经使用的长度,alloc属性记录所有的长度(在3.0版本使用free记录未使用空间,3.2版本则改为alloc记录总长度),sds采用这种空间预分配惰性空间释放两种策略,解决了字符串拼接和截取两种场景下的空间问题。
  • 空间预分配:当对一个SDS字符串进行增长操作且内存不够用时,SDS会进行内存的申请,申请时,如果len≤1MB,那么就申请len+len+1的内存,也就是额外申请已使用内存len的两倍+1。如果申请时len>1MB, 那么就申请len+1MB+1的内存,每次扩增1MB。
  • 惰性空间释放:用于优化SDS字符串缩短操作,当SDS的字符串进行缩短时,程序不会释放其内存,而是使用free字段记录下还有多少未使用的空间,等待以后使用
    | 杜绝缓冲区溢出
    减少修改字符串带来的重新分配内存的次数 |
    | 可以使用所有的<string.h>中的函数 | 可以使用部分<string.h>中的函数 | |
    | 只能保存文本数据 | 可以保存文本或二进制数据 | |

4. List底层数据结构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.1 ziplist类型(压缩列表)

在说Redis的ziplist之前,先看一下什么是压缩列表。压缩列表是一种紧凑型的数据结构,目的就是为了节省内存空间,他借鉴了数组的存储思想,占用一块连续的内存空间,但又与数组有些许区别。因为数组要求每个元素占用相同的存储空间,且每个存储空间的大小依据最大的那个元素来计算。

上面图中所表示,前4个元素都浪费了存储空间,所以为了保证占用内存空间的连续性和节省更多的内存空间,可以对数组进行压缩,使每个元素的大小为实际存储的大小。但这样的话,不知道每个元素具体的大小了,代码不能判断下一个元素的起点,所以还要为每个元素增加一个字段用于记录每个元素占据的位置大小,用于标识当前元素的大小,这样代码在遍历的时候,就能判断下一个元素的起点,这就构成了最简单的压缩链表
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
redis的ziplist就是基于上面的压缩列表做了自己的封装,接下来我们看redis中ziplist具体的实际结构如何:

ziplist最大的好处就是节省了内存空间,但他的缺点也很多。

  • 不能存储太多的元素,否则遍历效率极低
  • 当新增或修改某个元素时,会重新分配内存,甚至会引起连锁更新的问题。

**连锁更新:**连锁更新就是我只更新了一个元素,但由于某个原因,导致所有元素都进行了内存重分配,导致所有元素都进行了更新,就会降低效率。
那么ziplist如何出现连锁更新问题的呢?这就要看entry节点中prevlen属性占用空间大小了,prevlen属性记录是上一个节点的大小,当上一个节点大小小于256字节时,prevlen属性需要1字节的空间来保存这个长度大小,当上一个节点大小超过256字节时,prevlen属性就需要5字节的空间来保存这个长度大小。如果当上一个节点增大超过256字节时,就会引发下一个节点的prevlen属性内存空间增大,需要为下一个节点重新分配额外的4字节空间。循环往复,有可能每一个下个节点都被导致重新分配内存空间,直至最后一个节点,就会导致连锁更新。

因此:ziplist只适合保存结点数量不多的场景。

4.2 linkedlist类型(双向链表)

redis中的linkedlist就是基于双向链表实现的。以下是redis中linkedlist具体结构图:

可以看到linkedlist中包含了3个属性:

  • head:用于指向链表中的第一个结点
  • tail:用于指向链表中的最后一个结点
  • len:用于记录链表中所有结点的数量

除此之外,还包含了3个函数:

  • dup:结点复制函数,用于复制节点
  • free:结点释放函数,用于释放节点的内存空间
  • match:结点比较函数,用于比较节点之间的值

linkedlist的优缺点:

linkedlist的优点linkedlist的缺点
  • 可以直接获取到头结点和尾结点,时间复杂度O(1)
  • 每个结点中都有next和prev属性,可以方便的获取下一个结点和上一个结点,时间复杂度也是O(1)
  • linkedlist中维护了链表的长度len,可以直接获取链表长度,时间复杂度也是O(1)
    |
  • 双向链表中的每个结点内存空间都是不连续的,无法很好的利用cpu缓存
  • 双向链表遍历的时间复杂度为O(n)
    |

4.3 quicklist类型

由于ziplist和linkedlist都存在着不可避免的缺陷,所以在redis3.2版本之后,引入了一种新的数据结构:QuickList(快速列表)。List对象的底层数据结构,也由ziplist和linkedlist变为QuickList。
QuickList结合了ziplist和linkedlist的优点,它是一个压缩列表ziplist为结点的双向链表,以下是它的数据结构图:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
可以看到quicklist的数据结构和linkedlist总体上比较相似,都是包含了一个双向链表,并且都维护了双向链表的head结点和tail结点,以及结点的数量,最大的区别就是在双向链表中存储的value值发生了变化,linkedlist中存储的值是实际的值,而quicklist中存储的值是一个指向ziplist的指针。该ziplist的最大大小由quicklist的fill属性进行限制,当某个节点的ziplist大小超出了该限制,就会在链表中新建一个ziplist保存到新的quicklist结点中。

4.4 listpack类型(紧凑列表)

5. Set底层数据结构

5.1 intset

intset适用于保存都是整数的集合,根据编码的不同可以保存不同大小范围的整数,如果三种编码都无法保存这个整数,或者有元素不是整数,就会升级为hashtable的编码方式进行编码。

5.2 hashtable

HashTable(哈希表)是一个保存key-value键值对的结构体,它与Java中的HashMap相似,每个key都是唯一的,可以通过key去查询和修改其value值。
在redis中,hashtable底层实现是基于数组的,数组的每个元素相当于一个bucket桶,当通过哈希散列函数计算出key对应的hash值(也就是数组下标)时,就会将该元素放入在数组中对应的下标位置,当发生hash冲突时,就会采用链地址法,在对应的bucket桶下形成一个链表来保存这些数据。

常见的解决hash冲突的方式还有以下几种:

  1. 链地址法:形成一个Entry链表来保存数据
  2. 二次hash:用另一种函数重新计算hash值,尝试映射到其他的bucket中
  3. 公共溢出区:将产生Hash冲突的元素统一存放到一个公共的区域
  4. 开放定址法:冲突的Entry按照一定的规则(线性探测、平方探测)在hash表中找到下一个空闲的bucket存放

下面是hashtable的结构图:

从结构中可以看到,映射到相同数组下标位置的元素,会形成链表,当redis再次查询时,会先计算数组下标位置,然后再沿着链表进行遍历查找,时间复杂度为O(1)+O(n)=O(n).
哈希表使用了链式哈希法虽然很好的解决了哈希冲突的问题,但随着结点越来越多,链表上的结点元素也越来越多,就会照成查询时的成本变高,会大大降低查询效率。
redis为了解决hash冲突,在数据量达到一定阈值的时候,就会对hash表进行rehash(重新计算hash)操作,会将数组进行扩容,对每个元素重新计算hash,然后重新映射到新位置。
触发rehash的条件有两个:

  1. 负载因子≥1,并且服务器没有执行bgsave命令或者bgrewriteaof命令,就会执行rehash操作,但并一定100%执行
  2. 负载因子≥5,说明此时的hash表冲突已经很严重了,将强制执行rehash操作

负载因子 = hash表中entry结点的总数量 / 数组大小

redis为了解决hash冲突,在进行rehash的时候,也设计了一种dict结构,dict中包含了两个dictht哈希表ht[0]和ht[1],默认情况下,这两个哈希表只使用一个。以下是dict的结构:

当我们第一次新增数据的时候,元素结点会存放在ht[0]中,当ht[0]中的结点负载因子超过阈值时,就会重新计算结点的hash值,将其放入到ht[1]中。
rehash步骤:

  1. 给哈希表ht[1]分配空间,分配空间的大小为 2n中第一个>ht[0].used * 2的n值。比如上图中ht[0].used * 2=22, 而25>22>24,所以n=5,即分配的空间大小为32.
  2. 将ht[0]中的entry结点经过rehash重新运算后,迁移到ht[1]中
  3. 迁移完成后,释放ht[0]的内存空间,并把ht[1]设置为ht[0]
  4. 创建一个新的ht[1],为下次扩容做准备

通过扩容操作,并进行rehash操作,可以有效地解决hash冲突的问题,但当hash表中的元素很大时,就会导致rehash动作耗时很长,而redis是单线程的,一旦耗时过长影响到后续的操作,那就是得不偿失的。因此在redis中没有采用直接全部rehash,而是采用渐进式rehash操作来进行。
**渐进式rehash:**不一次性把所有的数据rehash到新的哈希表中,而是在每次对哈希表的元素进行增删查改的时候,顺带把某个数组下标的链表上所有的结点进行rehash操作,直至把所有的元素rehash。在新增数据时,对于新增的数据将其放入ht[1]中,对于查询数据时,先去查询ht[0],再去查询ht[1]。在渐进式rehash操作时,两个dict中都会存储数据,但ht[0]中的数据会越来越少,直至全部rehash进入到ht[1]中。
以下是rehash的结构图:

6. ZSet底层数据结构

6.1 ziplist类型

ziplist的具体结构在4.1节已经介绍过了。但ZSet中具体的做法是将元素和其对应的分值分别保存到两个相邻的entry的content中。
当ziplist作为zset的底层数据结构时,每个集合元素使用两个紧挨在一起的压缩列表结点来保存。第一个结点保存元素成员,第二个结点保存结点的分值。
image.png

redis选择ziplist作为zset的底层数据结构也是有限制的:

  1. 有序集合中保存的元素数量<128个
  2. 有序集合保存的所有元素的长度小于64字节

否则就会使用skiplist作为zset的底层实现。

6.2 skiplist类型

ziplist俗称跳表,跳表是一个基于有序链表实现的可以快速查找元素的数据结构。下面是一个普通的有序链表的结构图:

该链表中如果想找到靠后的结点,就需要挨个遍历结点,时间复杂度为O(n)。那么有没有办法提高查询效率呢,当然是有的。既然我们的链表是有序的,我们可不可以跳过前面的结点呢?这时候我们的初级链表就出来了(一个含有两层链表的跳表, 相当于带了一层索引):

这样我们查询12的话:

  1. 只有一层链表:1->3->6->8->9->12,需要遍历6次,时间复杂度O(n)
  2. 有两层链表:1->6->9->12,需要遍历4次,时间复杂度O( n 2 \frac{n}{2} 2n)
  3. 同理,对于一个n层链表来说,时间复杂度为O( l o g n logn logn)

在上面的结构中,跳跃表遵守上一层结点是下一层结点的一半,但是在发生大量插入元素的时候,就会发生频繁调整跳跃表这个操作。
所以在redis中设计了一种升层算法,对于每一个新增的结点,初始化其层数为1,然后循环以下算法步骤:算法算出一个0~1之间随机数,如果随机数<0.25,则层数增加一层,然后循环这个步骤,直到生成的随机数> 1 4 \frac{1}{4} 41,如果循环到第三次退出循环时,那么这个新增节点的层数就是2。这个算法对于每个结点来说,新增一层的概率是0.25,层数越高,概率越小。对于新增的结点,只需要修改其前向指正和后向指针即可,其它结点不受影响。
综上所述,通过将有序集合的部分节点层,从最上层结点开始依次查找,如果本层的next结点大于我们想要找的结点,那么就自降一层再依次查找,如果找到了就返回其值,找不到就返回null。如果跳跃表的层数很多的话,那么就会跳过很多节点,从而提升查询的效率,这就是跳跃表的思想。
跳跃表有以下性质:

  1. 跳跃表有很多层结构组成,最底层的结点个数为跳跃表的长度
  2. 跳跃表有一个头结点,头结点中默认有一个32层的结构(redis默认层数最高32层),每层的结构都包含一个指向本层下一个元素的next指针
  3. 除了头结点外,有最高层的结点的层数称为跳跃表的高度
  4. 每层都是一个有序链表,随着数据score依次递增
  5. 除了头结点外,最底层的链表包含所有的元素

在跳跃表中,对于一个新增的元素来说,其新增结构如下:

7. Hash底层数据结构

7.1 ziplist类型

当元素较少时,使用ziplist保存hash结构的元素,当元素个数超过一定的限制后,使用hashtable来保存。

7.2 hashtable类型

hashtable在上述5.2章节中已经介绍。

7.3 listpack类型

本文参考文献:

  • https://blog.csdn.net/YTREE_BJ/article/details/119415521
  • https://zhuanlan.zhihu.com/p/619828101
  • https://www.jianshu.com/p/674c9635b2b9
  • https://baijiahao.baidu.com/s?id=1731524214636496899&wfr=spider&for=pc
  • https://www.jianshu.com/p/674c9635b2b9
  • https://blog.51cto.com/u_39029/6501913
  • https://blog.csdn.net/u013277209/article/details/125998869
  • https://blog.csdn.net/qq_32099833/article/details/133889188

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

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

相关文章

烯冷新能源邀您参观2024长三角快递物流展

参加企业介绍 宁波戈雷贝拓科技有限公司&#xff08;宁波烯冷新能源科技有限公司&#xff09;宁波烯冷新能源科技有限公司于2022年初成立&#xff0c;依托中国科学院宁波材料技术与工程研究所和国家石墨烯创新中心&#xff0c;公司开发产品包括&#xff1a;新能源制冷系统和集…

Mysql学习--深入探究索引和事务的重点要点与考点

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

一键入门Ubuntu22!

目录 一、安装 二、常用目录 三、常用指令 四、用户指令 五、ssh与scp 六、服务相关 七、Python与Pycharm 八、Vim编辑器 九、Ubuntu22下使用Mysql 十、Ubuntu22下使用mongodb 十一、Ubuntu22下使用redis Ubuntu是一个基于Debian的开源操作系统&#xff0c;由Canoni…

LeetCode每日一题——x 的平方根

x 的平方根OJ链接&#xff1a;69. x 的平方根 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 乍一看题目只需要算一个数的平方根&#xff0c;根据我们之前学的C语言我们能很快的想到使用sqrt&#xff0c;pow这类的<math.h>库函数&#xf…

【计算机网络篇】数据链路层(2)封装成帧和透明传输

文章目录 &#x1f95a;封装成帧和透明传输&#x1f388;封装成帧&#x1f388;透明传输&#x1f5d2;️面向字节的物理链路使用字节填充的方法实现透明传输。&#x1f5d2;️面向比特的物理链路使用比特填充的方法实现透明传输。 &#x1f6f8;练习 &#x1f95a;封装成帧和透…

【目标检测基础篇】目标检测评价指标:mAP计算的超详细举例分析以及coco数据集标准详解(AP/AP50/APsmall.....))

学习视频&#xff1a; 霹雳吧啦Wz-目标检测mAP计算以及coco评价标准 【目标检测】指标介绍&#xff1a;mAP 1 TP/FP/FN TP(True Positive) : IoU>0.5的检测框数量(同一Ground truth只计算一次)FP(False Positive) : IoU<0.5的检测框(或者是检测到同一个GT的多余检测框的…

(css)vue 自定义背景 can‘t resolve

(css)vue 自定义背景 can’t resolve 旧写法&#xff1a; background-image: url(/assets/images/step-bg.jpg);background-size: 100% 100%; 新写法&#xff1a; background-image: url(~/assets/images/step-bg.jpg);background-size: 100% 100%; 解决参考&#xff1a;https…

印刷企业实施MES管理系统如何做好需求分析

在数字化、信息化的大潮中&#xff0c;印刷企业面临着转型升级的迫切需求。MES管理系统作为连接企业资源计划ERP和现场自动化系统的桥梁&#xff0c;对于提升印刷企业的生产效率、优化资源配置、提高产品质量具有重要意义。因此&#xff0c;做好MES管理系统的需求分析&#xff…

分布式搜索引擎elasticsearch专栏二

上一篇的传送门&#xff1a; 分布式搜索引擎elasticsearch专栏一-CSDN博客 这一篇博文主要讲解elasticsearch的数据搜索功能。下面会分别使用DSL和RestClient实现搜索。 1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsea…

基于ssm的勤工助学管理系统+数据库+报告+免费远程调试

项目介绍: 基于ssm的勤工助学管理系统。Javaee项目&#xff0c;ssm项目。采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc Mybatisplus VuelayuiMaven来实现。有管理员和老…

​酒店小程序开发的功能与优势解析

随着科技的快速发展和移动互联网的普及&#xff0c;越来越多的服务行业开始尝试利用小程序来提供便捷的服务。对于酒店业来说&#xff0c;开发一个酒店小程序不仅可以提升用户体验&#xff0c;还有助于提高运营效率。本文将详细介绍酒店小程序的开发功能以及它的优势。 一、酒…

Echarts 利用多X轴实现未来15天天气预报

Echarts 利用多X轴实现未来15天天气预报 UI 设计图 Echarts 实现效果 代码实现 代码分解 echarts 图表上下均显示数据 通过设置 grid.top 和 grid.bottom 设置白天和夜间天气展示区域 grid: {top: 36%,bottom: 36%,left: 5%,right: 5%}, 天气图标的设置 由于 axisLabel 的…

Redis中AOF、RDB和复制功能对过期键的处理

AOF、RDB和复制功能对过期键的处理 生成RDB文件 在执行SAVE命令或者BGSAVE命令创建一个新的RDB文件时&#xff0c;程序会对数据库中的键进行检查&#xff0c;已过期的键不会被保存到新创建的RDB文件中。 例子 举个例子&#xff0c;如果数据库中包含三个键k1、k2、k3&#x…

六种GPU虚拟化:除了直通、全虚拟化 (vGPU)还有谁?

在大类上计算虚拟化技术有这3种&#xff1a; 软件模拟、直通独占(如网卡独占、显卡独占)、直通共享&#xff08;如vCPU 、vGPU&#xff09;。但对于显卡GPU而言我总结细化出至少这6种分类&#xff1a; 第一种、软件模拟&#xff08;eg sGPU&#xff09;, 又叫半虚拟化。第二种…

活用 C语言之union的精妙之用

一、union的基本定义 Union的中文叫法又被称为共用体、联合或者联合体。它的定义方式与结构体相同,但意义却与结构体完全不同。下面是union的定义格式: union 共用体名 {成员列表}共用体变量名;它与结构体的定义方式相同,但区别在于共用体中的成员的起始地址都是相同的,…

Python环境下基于1D-CNN的轴承故障诊断及TSNE特征可视化

1D CNN 处理一维信号具有显著优势&#xff0c;已在很多领域得到初步应用&#xff1a; 心电图监测&#xff1a;将1DCNN应用于心脏病监测&#xff0c;其方法是针对每一个心脏病人的&#xff0c;即对于每个心律失常患者使用该患者特有的训练数据&#xff0c;专门训练出一个紧凑的…

仿牛客项目Day11 Redis

Redis概念 Redis是一种Nosql非关系型数据库&#xff0c;以键值对的形式存储数据 Redis中的所有数据都存在内存中&#xff0c;访问速度很快。同时&#xff0c;Redis还可以将内存中的数据以快照或日志的形式存到硬盘里&#xff0c;以保证数据的安全性 Redis要到github上去下载…

OCR研究背景及相关论文分享

光学字符识别&#xff08;Optical Character Recognition&#xff0c;OCR&#xff09;是指使用光学方法将图像中的文字转换为机器可编辑的文本的技术。OCR技术的研究和应用已有数十年的历史&#xff0c;其背景和发展受到多方面因素的影响。 技术需求背景 1.自动化文档处理&am…

【数字IC/FPGA】书籍推荐(1)----《轻松成为设计高手--Verilog HDL实用精解》

在下这几年关于数字电路、Verilog、FPGA和IC方面的书前前后后都读了不少&#xff0c;发现了不少好书&#xff0c;也在一些废话书上浪费过时间。接下来会写一系列文章&#xff0c;把一部分读过的书做个测评&#xff0c;根据个人标准按十分制满分来打分分享给大家。 书名&#xf…

uniapp(vue3) H5页面连接打印机并打印

一、找到对应厂商打印机的驱动并在windows上面安装。查看是否安装完成可以在&#xff1a;控制面板->查看设备和打印机&#xff0c;找到对应打印机驱动是否安装完成 二、打印机USB连接电脑 三、运行代码调用浏览器打印&#xff0c;主要使用的是window.print()功能。下面使用…