Redis之intset数据结构
0.前言
redis中intset是一个整数集合, 只能存储整数类型的数据, 可以是16位, 32位, 或者是64位, 是以升序排列的数组进行保存数据,下面会介绍具体数据结构和对其操作过程.
1.数据结构定义
typedef struct intset {
/*编码*/
uint32_t encoding;
/*长度*/
uint32_t length;
/*集合内容,按升序排列数组*/
int8_t contents[];
} intset;
2.创建集合
创建集合需要分配下内存空间, 初始化结构体内变量
intset *intsetNew(void) {
intset *is = zmalloc(sizeof(intset));
is->encoding = intrev32ifbe(INTSET_ENC_INT16);
is->length = 0;
return is;
}
3.添加元素
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
/*为了节省空间, 判断添加的元素需要编码为何种数据类型, 比如int16, int32, int64*/
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 1;
/*如果intset编码位数无法容纳新元素,则需要重新更新整个intset编码*/
if (valenc > intrev32ifbe(is->encoding)) {
/* 更新编码并添加新元素 */
return intsetUpgradeAndAdd(is,value);
} else {
/*搜索新添加元素是否已经存在,存在则返回失败,此函数在查找一节会详细讲解*/
if (intsetSearch(is,value,&pos)) {
if (success) *success = 0;
return is;
}
/*扩展内存空间*/
is = intsetResize(is,intrev32ifbe(is->length)+1);
if (pos < intrev32ifbe(is->length))
/*如果添加元素位置不是一整块内存尾部,则需将其后面元素后移一个元素位置*/
intsetMoveTail(is,pos,pos+1);
}
/*pos位置处赋值*/
_intsetSet(is,pos,value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}
/*根据元素大小决定元素存储长度*/
static uint8_t _intsetValueEncoding(int64_t v) {
if (v < INT32_MIN || v > INT32_MAX)
return INTSET_ENC_INT64;
else if (v < INT16_MIN || v > INT16_MAX)
return INTSET_ENC_INT32;
else
return INTSET_ENC_INT16;
}
/*重置intset空间大小,每次zrealloc扩展内存大小*/
static intset *intsetResize(intset *is, uint32_t len) {
uint32_t size = len*intrev32ifbe(is->encoding);
is = zrealloc(is,sizeof(intset)+size);
return is;
}
/*向后移动元素*/
static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {
void *src, *dst;
uint32_t bytes = intrev32ifbe(is->length)-from;
uint32_t encoding = intrev32ifbe(is->encoding);
if (encoding == INTSET_ENC_INT64) {
src = (int64_t*)is->contents+from;
dst = (int64_t*)is->contents+to;
bytes *= sizeof(int64_t);
} else if (encoding == INTSET_ENC_INT32) {
src = (int32_t*)is->contents+from;
dst = (int32_t*)is->contents+to;
bytes *= sizeof(int32_t);
} else {
src = (int16_t*)is->contents+from;
dst = (int16_t*)is->contents+to;
bytes *= sizeof(int16_t);
}
memmove(dst,src,bytes);
}
/* 更新集合编码并添加新元素 */
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
uint8_t curenc = intrev32ifbe(is->encoding);
uint8_t newenc = _intsetValueEncoding(value);
int length = intrev32ifbe(is->length);
int prepend = value < 0 ? 1 : 0;
/* 设置新编码,并扩展足够内存空间*/
is->encoding = intrev32ifbe(newenc);
is = intsetResize(is,intrev32ifbe(is->length)+1);
/* 取出原来空间中元素,从后开始往前依次放入新的位置 */
while(length--)
_intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
/* 放置value值,要么在数组头,要么在数组尾部 */
if (prepend)
_intsetSet(is,0,value);
else
_intsetSet(is,intrev32ifbe(is->length),value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}
4.查找元素
查找元素依靠intsetFind函数进行,调用intsetSearch进行实际查找
uint8_t intsetFind(intset *is, int64_t value) {
/*判断待查元素编码是否符合条件,不符合直接返回false,否则进入intsetSearch进行实际查找*/
uint8_t valenc = _intsetValueEncoding(value);
return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);
}
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
int64_t cur = -1;
/* 集合为空,直接返回第一个位置 */
if (intrev32ifbe(is->length) == 0) {
if (pos) *pos = 0;
return 0;
} else {
/* _intsetGet函数仅仅获取set集合中pos位置的值, 如果待查元素大于集合尾部元素,则直接返回待查元素位置为集合长度*/
if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
if (pos) *pos = intrev32ifbe(is->length);
return 0;
/*如果待查元素小于集合头部元素,则直接返回待查元素位置为0*/
} else if (value < _intsetGet(is,0)) {
if (pos) *pos = 0;
return 0;
}
}
/*二分查找*/
while(max >= min) {
mid = ((unsigned int)min + (unsigned int)max) >> 1;
cur = _intsetGet(is,mid);
if (value > cur) {
min = mid+1;
} else if (value < cur) {
max = mid-1;
} else {
break;
}
}
/*找到元素返回1,否则返回0,pos为元素应该位置*/
if (value == cur) {
if (pos) *pos = mid;
return 1;
} else {
if (pos) *pos = min;
return 0;
}
}
5.删除元素
intset *intsetRemove(intset *is, int64_t value, int *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 0;
/*查找元素是否存在*/
if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {
uint32_t len = intrev32ifbe(is->length);
if (success) *success = 1;
/*删除元素,并移动其他元素覆盖原来位置,这里没有缓存空间,而是直接重置原来空间,可能是为了节省内存*/
if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
is = intsetResize(is,len-1);
is->length = intrev32ifbe(len-1);
}
return is;
}
总结
intset实质就是一个有序数组,可以看到添加删除元素都比较耗时,查找元素是O(logN)时间复杂度,不适合大规模的数据。我们在进行sadd,sdel等对无序集合进行操作时,并不是一定使用intset进行保存数据,后面我们讲到这几个命令时会详细讲解,操作时使用的存储策略。
Redis之intset数据结构的更多相关文章
- Redis 5种数据结构使用及注意事项
1优缺点 非常非常的快,有测评说比Memcached还快(当大家都是单CPU的时候),而且是无短板的快,读写都一般的快,所有API都差不多快,也没有MySQL Cluster.MongoDB那样更新同 ...
- Redis(二)--- Redis的底层数据结构
1.Redis的数据结构 Redis 的底层数据结构包含简单的动态字符串(SDS).链表.字典.压缩列表.整数集合等等:五大数据类型(数据对象)都是由一种或几种数结构构成. 在命令行中可以使用 OBJ ...
- 【Redis】内部数据结构自顶向下梳理
本博客将顺着自顶向下的思路梳理一下Redis的数据结构体系,从数据库到对象体系,再到底层数据结构.我将基于我的一个项目的代码来进行介绍:daredis.该项目中,使用Java实现了Redis中所有的数 ...
- redis 五种数据结构详解(string,list,set,zset,hash)
redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...
- (2)redis的基本数据结构是动态数组
redis的基本数据结构是动态数组 一.c语言动态数组 先看下一般的动态数组结构 struct MyData { int nLen; ]; }; 这是个广泛使用的常见技巧,常用来构成缓冲区.比起指针, ...
- 2.Redis五种数据结构
2.Redis五种数据结构2.1 预备2.1.1 全局命令2.1.2 数据结构和内部编码2.1.3 单线程架构2.2 字符串2.2.1 命令2.2.2 内部编码2.2.3 典型使用场景2.3 哈希2. ...
- Redis指令与数据结构(二)
0.Redis目录结构 1)Redis介绍及部署在CentOS7上(一) 2)Redis指令与数据结构(二) 3)Redis客户端连接以及持久化数据(三) 4)Redis高可用之主从复制实践(四) 5 ...
- redis 五种数据结构详解(string,list,set,zset,hash),各种问题综合
redis 五种数据结构详解(string,list,set,zset,hash) https://www.cnblogs.com/sdgf/p/6244937.html redis 与 spring ...
- 【Redis】redis 五种数据结构详解(string,list,set,zset,hash)
redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...
随机推荐
- hdu6086(AC 自动机)
hdu6086 题意 字符串只由 \(01\) 组成,求长度为 \(2L\) 且包含给定的 \(n\) 个子串的字符串的个数(且要求字符串满足 \(s[i] \neq s[|s| - i + 1]\) ...
- hexo 的错误
错误如下 Connection to github.com closed by remote host. fatal: The remote end hung up unexpectedly erro ...
- SNMP OID批量枚举工具
SNMP OID批量枚举工具 SNMP信息包含大量的系统.设备信息.这些信息均使用OID方式表示.由于OID编号规则复杂,不便于记忆.为了快速通过OID批量获取指定信息,Kali Linux新增了 ...
- 概述struts,以及struts如何实现MVC架构的
概述MVC体系结构? 答:MVC包括三类对象,model是应用对象,view是视图,controller是控制器,它定义用户界面对用户输入的响应方式. 在MVC体系中,模型通常被称为”业务逻辑”,是真 ...
- Spring学习——DI(依赖注入)
IOC容器,处理对象依赖关系 IOC与DI: IOC :是一个容器,创建对象的容器 DI :在容器创建对象后,处理对象的依赖关系,也叫依赖注入! 方式1:通过set方法注入值 可以给普通属性.集合属性 ...
- 在C#中快速查询文件
相信使用过Everything的人都对其超快的搜索速度印象非常深刻,它的主要原理是通过扫描NTFS磁盘的USN Journal读取的文件列表,而不是磁盘目录,由于USN Journal非常小,因此能实 ...
- SQL Server on Linux: How? Introduction: SQL Server Blog
SQL Server Blog Official News from Microsoft’s Information Platform https://blogs.technet.microsoft. ...
- 腾讯云会话服务器node+nginx
1.除了一个正常的服务器还需要一个会话服务器(websocket),利用node加socket.io来做 2.正常安装Nginx yum install nginx 3.Nginx的配置内容略微不同( ...
- PHP正则表达式之快速学习法
1.入门简介 简单的说,正则表达式是一种可以用于模式匹配和替换的强有力的工具.我们可以在几乎所有的基于UNIX系统的工具中找到正则表达式的身影,例如,vi编辑器,Perl或PHP脚本语言,以及awk或 ...
- DL380 G6 BIOS刷新方法
bios下载地址SP44873.exe (5.9 MB) http://h20000.www2.hp.com/bizsupport/TechSupport/SoftwareDescription.js ...