Redis-对象

参考资料 极客时间Redis(亚风)

Redis对象

String

• 基本编码⽅式是RAW,基于简单动态字符串(SDS)实现,存储上限为512mb。
• 如果存储的SDS⻓度⼩于44字节,则会采⽤EMBSTR编码,此时object head与SDS是⼀段连续空间。申请内存时只需要调⽤⼀次内存分配函数,效率更⾼。
• 如果存储的字符串是整数值,并且⼤⼩在LONG_MAX范围内,则会采⽤INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS了。
对应第一种情况:
在这里插入图片描述
对应第二种情况:
在这里插入图片描述
对应第三种情况:
在这里插入图片描述

List

Redis采⽤QuickList实现List。

在这里插入图片描述

Set

Set是Redis中的集合,不⼀定确保元素有序,可以满⾜元素唯⼀、查询效率要求极⾼。
为了查询效率和唯⼀性,set采⽤HT编码(Dict)。Dict中的key⽤来存储元素,value统⼀为null.
当存储的所有数据都是整数,并且元素数量不超过set-max-intset-entries时,Set会采⽤lntSet编码,以节省内存。
第一中情况都是int类型:IntSet 实现

在这里插入图片描述
第二种:使用dict实现
在这里插入图片描述
上面过程对应的源码:

int setTypeAdd (robj *subject, sds value) {Long Long llval;if (subject->encoding==OBJ_ENCODING_HT) { //已经是HT编码,直接添加元素dict *ht = subject->ptr;dictEntry *de = dictAddRaw(ht,value,NULL)if (de) {dictSetKey(ht, de, sdsdup (value))dictSetVal(ht,de, NULL)return 1 ; }}else if (subject->encoding == OBJ_ENCODING_INTSET){ // ⽬前是INTSET// 判断value是否是整数if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {uint8_t success = 0;// 是整数,直接添加元素到setsubject->ptr=intsetAdd (subject->ptr, llval, &success)if (success){/* 当intset元素数量超出set_max_intset_entries,则转为HT.*/size_t max_entries = server.set_max_intset_entries;if (max_entries >= 1<<30) max_entries=1<<30;if (intsetLen(subject->ptr) > max_entries)setTypeConvert(subject,OBJ_ENCODING_HT)return 1;}) else {// 不是整数,直接转为HTsetTypeConvert(subject,OBJ_ENCODING_HT)serverAssert(dictAdd(subject->ptr,sdsdup (value),NULL)== DICT_OK)return 1;}
} }
ZSet

底层是用三种数据结构实现的(HT + SkipList + ZPlist)

typedef struct zset {// Dict指针dict *dict;// SkipList指针zskiplist *zsl;
} zset;

当元素数量不多时,HT和SkipList的优势不明显,⽽且更耗内存。因此zset还会采⽤ZipList结构来节省内存,不过需要同时满⾜两个条件:
① 元素数量⼩于zset-max-ziplist-entries,默认值128
② 每个元素都⼩于zset-max-ziplist-value字节,默认值64
源码如下:

// zadd添加元素时,先根据key找到zset,不存在则创建新的zset
zobj = LookupKeywrite(c->db, key)if (checkType (c, zobj,OBJ_ZSET)goto cleanup;
//判断是否存在
if (zobj == NULL) {// zset不存在
if (server.zset_max_ziplist_entries == 0
server.zset_max_ziplist_value < sdslen(c->argv [scoreidx+1]->ptr))
{ 
// zset_max_ziplist_entries设置为0就是禁⽤了ZipList,
//或者value⼤⼩超过了zset_max_ziplist_value,采⽤HT + SkipList
Zobj = createzsetobject()} else {
// 否则,采⽤ ZipList
zobj = createzsetziplistobject()}
dbAdd(c->db, key, zobj)}
zsetAdd(zobj,score, ele, flags, &retflags, &newscore)

Ziplist本身没有排序功能,⽽且没有键值对的概念,因此需要有zset通过编码实现:
• Ziplist是连续内存,因此score和element是紧挨在⼀起的两个entry,element在前,score在后
• score越⼩越接近队⾸,score越⼤越接近队尾,按照score值升序排列
在这里插入图片描述

Hash

Hash底层采⽤的编码与Zset也基本⼀致,只需要把排序有关的SkipList去掉即可:
• Hash结构默认采⽤ZipList编码,⽤以节省内存。Ziplist中相邻的两个entry 分别保存field和value
• 当数据量较⼤时,Hash结构会转为HT编码,也就是Dict,触发条件有两个:
① ZipList中的元素数量超过了hash-max-ziplist-entries(默认512)
② ZipList中的任意entry⼤⼩超过了hash-max-ziplist-value(默认64字节)
在这里插入图片描述

// 插入命令
void hsetCommand(client *c) {int i, created = 0;robj *o;//判断hash的key是否存在,不存在则创建⼀个新的,默认采⽤ZipList编码if ((o = hashTypeLookupwriteOrCreate(c, c->argv[1]))==NULL) return;// 判断是否需要把ZipList转为DicthashTypeTryConversion(o,c->argv,2,c->argc-1)// 循环遍历每⼀对field和value,井执⾏hset命令for (i = 2;i<c-argc; i += 2)created += !hashTypeSet(o,c->argv[I]->ptr,c->argv[i+1]->ptr, HASH_SET_COPY);}robj *hashTypeLookupwriteOrCreate(client *c, robj *key)// 查找keyrobj *o = LookupKeywrite(c->db, key)if (checkType(c,o, OBJ_HASH)return NULL;// 不存在,则创建新的if (o == NULL) {o = createHashobject()dbAdd (c->db, key,o)}return o;
}
robj *createHashobject (void) {// 默认采⽤zipList编码,申请ZipList内存空间unsigned char *z1 = ziplistNew();robj *o = createobject(OBJ_HASH, z1)//设置编码o->encoding = OBJ_ ENCODING_ZIPLIST;return 0;
}
// ziplist 在什么时候转换为HT
void hashTypeTryConversion(robj *o,robj **argv, int start, int end)int i;size_t sum = 0;// 本来就不是ZipList编码,什么都不⽤做了if (o->encoding != OBJ_ENCODING_ZIPLIST) return;//依次遍历命令中的field、 value参数for (i = s. start; i < c. end;j++)if (!sdsEncodedobject (argv[i]))continue;size_t len = sdsLen (argv[i]->ptr)// 如果field或value超过hash_max_ziplist_value,则转为HTif (len > server.hash_max_ziplist_value) {hashTypeConvert(o, OBJ_ENCODING_HT)return;}sum += len;}// ziplist⼤⼩超过1G,也转为HTif (!ziplistSafeToAdd(o- >ptr, sum))hashTypeConvert(o, OBJ_ENCODING_HT)}
// 真正插入元素的逻辑
int hashTypeSet(robj *o,sds field, sds value, int flags){int update = 0// 判断是否为ZipList编码if (o->encoding == OBJ_ENCODING_ZIPLIST) {unsigned char *zl, *fptr, *vptr;zl = o->ptr;// 查询head指针fptr = ziplistIndex(zl, ZIPLIST_HEAD)if (fptr != NULL)// head不为空,说明ZipList不为空,开始查找keyfptr = ziplistFind(zl, fptr, (unsigned char*) field, sdslen(value));if (fptr != NULL){//判断是否存在,如果已经存在则更新update=1;ziplistReplace(zl, vptr, (unsigned char*) value,sdslen (value))// 不存在,则直接pushif(!update){ // 依次push新的field和value到zipList的尾部zl = ziplistPush(zl, (unsigned char*) field, sdslen(field),ZIPLIST_TAIL);zl = ziplistPush(zl, (unsigned char*)value, sdslen (value),ZIPLIST_TAIL)}o->ptr = zl;/* 插⼊了新元素,检查list⻓度是否超出,超出则转为HT */if (hashTypeLength(o)>server.hash_max_ziplist_entries)hashTypeConvert(o, OBJ_ENCODING_HT)} else if (o->encoding == OBJ_ENCODING_HT) {// HT编码,直接插⼊或覆盖} return update;
}

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

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

相关文章

web(HTML之表单练习)

使用HTML实现该界面&#xff1a; 要求如下&#xff1a; 用户名为文本框&#xff0c;名称为 UserName&#xff0c;长度为 15&#xff0c;最大字符数为 20。 密码为密码框&#xff0c;名称为 UserPass&#xff0c;长度为 15&#xff0c;最大字符数为 20。 性别为两个单选按钮&a…

三层交换的原理

一.三层交换技术 1.什么是三层交换机 要实现vlan间通信&#xff0c;就需要路由&#xff0c;解决办法要么是二层交换机加路由器形成单臂路由&#xff0c;要么就是直接使用三层交换机。 ①什么是单臂路由&#xff1a; ②单臂路由实现不同vlan间通信的原理&#xff1a; 路由器…

os功能模板

【 一 】简介 os 就是 “operating system” 的缩写&#xff0c;顾名思义&#xff0c;os 模块提供的就是各种 Python 程序与操作系统进行交互的接口。通过使用 os 模块&#xff0c;一方面可以方便地与操作系统进行交互&#xff0c;另一方面页可以极大增强代码的可移植性。如果该…

Linux-----8、相关符号

# 相关符号 # 1、名词解释 标准输入&#xff08;stdin&#xff09;&#xff1a;键盘上的输入 文件描述符—>0 标准输出&#xff08;stdout&#xff09;&#xff1a;屏幕上 正确 的输出 文件描述符—>1 标准错误&#xff08;stderr&#xff09;&#xff1a;屏幕上 错误…

为什么MCU在ADC采样时IO口有毛刺?

大家在使用MCU内部ADC进行信号采样一个静态电压时&#xff0c;可能在IO口上看到这样的波形。这个时候大家一般会认识是信号源有问题&#xff0c;但仔细观察会发现这个毛刺的频率是和ADC触发频率一样的。 那么为什么MCU在ADC采样时IO口会出现毛刺呢&#xff1f;这个毛刺对结果有…

ActionCLIP:A New Paradigm for Video Action Recognition

文章目录 ActionCLIP: A New Paradigm for Video Action Recognition动机创新点相关工作方法多模态框架新范式预训练提示微调 实验实验细节消融实验关键代码 总结相关参考 ActionCLIP: A New Paradigm for Video Action Recognition 论文&#xff1a;https://arxiv.org/abs/21…

【Spark面试】Spark面试题答案

目录 1、spark的有几种部署模式&#xff0c;每种模式特点&#xff1f;&#xff08;☆☆☆☆☆&#xff09; 2、Spark为什么比MapReduce块&#xff1f;&#xff08;☆☆☆☆☆&#xff09; 3、简单说一下hadoop和spark的shuffle相同和差异&#xff1f;&#xff08;☆☆☆☆☆…

mybatis中oracle的sql没走索引导致特别慢(未加jdbcType的)

如果直接跑sql是能走索引很快&#xff0c;在mybatis中不能&#xff0c;可能就是jdbcType的原因。 比如&#xff0c;我有一个属性A&#xff0c;在表里面是VARCHAR2类型&#xff0c;但是在mybatis中的sql是#{a}&#xff0c;缺少jdbcTypeJdbcType.VARCHAR&#xff0c;就会导致myba…

【精选】计算机网络教程(第3章数据链路层)

目录 前言 第3章数据链路层 1、差错检测&#xff08;CRC&#xff09; 2、点对点协议&#xff08;了解应用场景&#xff09; 3、什么是碰撞域&#xff0c;什么是广播域 碰撞域&#xff08;Collision Domain&#xff09;&#xff1a; 广播域&#xff08;Broadcast Domain&a…

1.Mybatis框架基本使用

特点: mybatis是一款优秀的持久层框架 支持定制化的SQL、存储过程以及高级映射 mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和java的POJO实例 优点: 1.简单、灵活、sql和代码分离,提高可维护性 2.提供映射标签,支持对象与数据库的orm字段关系映射 3.提…

python+pytest接口自动化(16)-接口自动化项目中日志的使用 (使用loguru模块)

通过上篇文章日志管理模块loguru简介&#xff0c;我们已经知道了loguru日志记录模块的简单使用。在自动化测试项目中&#xff0c;一般都需要通过记录日志的方式来确定项目运行的状态及结果&#xff0c;以方便定位问题。 这篇文章我们使用loguru模块来记录接口自动化测试中的日…

灾备建设中,虚拟机异构平台恢复技术原理与应用

在如今混合云环境下&#xff0c;实现异构虚拟化恢复与迁移面临着极大挑战。不同于市面上有代理的恢复方案&#xff0c;虚拟机无代理跨平台恢复解决方案利用自主研发的转换引擎&#xff08;VMCE&#xff09;对已备份虚拟机文件进行高效的存储格式转换和配置信息转换&#xff0c;…