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等数据结构的存 ...
随机推荐
- 17、Flask实战第17天:Flask-cookie
cookie的基本概念 在网站中,http请求是无状态的.也就是说即使第一次和服务器连接并且登录成功后,第二次请求服务器依然不能知道当前请求的是哪个用户. cookie的出现就是为了解决这个问题,第一 ...
- 子域名/目录暴力工具Gobuster
子域名/目录暴力工具Gobuster Gobuster是Kali Linux默认安装的一款暴力扫描工具.它是使用Go语言编写的命令行工具,具备优异的执行效率和并发性能.该工具支持对子域名和Web目 ...
- [BZOJ2753]滑雪与时间胶囊
第一问直接把可以走的边连起来bfs一遍即可 第二问可以用类似kruskal的方法,只不过排序的依据应该变为第一关键字为终点高度(从大到小),第二关键字为边权(从小到大),只排序可以走的边 因为同样高度 ...
- Java序列化对象为字符串并将字符串反序列化为对象
对象的序列化与反序列化其实就是将对象的状态保存下来,一般是保存到文件中,但是其实更常用的是将对象序列化为字符串保存到数据库中,然后在需要读取对象的情况下将字符串反序列化为对象. 可以序列化的类必须 ...
- 记一次深刻的教训-----将mat数据转化为SequenceFile
深刻的体会就是,“java.lang.NullPointer.Exception”就是空指针异常可能是由于数组部分元素未被初始化引起的. 1)使用jmatio将mat数据转化为SequenceFile ...
- ToggleButton控件,Switch控件
(一) 1.效果图 2. activity_main.xml <?xml version="1.0" encoding="utf-8"?> & ...
- Github上的iOS资料-个人记录
动画 awesome-ios-animation收集了iOS平台下比较主流炫酷的几款动画框架 RCTRefreshControlqq的下拉刷新 TBIconTransitionKiticon 的点击动 ...
- windows SFC(System File Checker) 命令的使用
SFC(System File Checker)可以扫描所有受保护的系统文件的完整性,并使用正确的 Microsoft 版本替换. 步骤:点击开始,输入cmd: 右键,以管理员身份运行 输入sfc/s ...
- 【AS3 Coder】任务八:没剧情还玩毛RPG
使用框架:AS3任务描述:了解RPG游戏中剧情播放器的制作原理及流程难度系数:3(了解原理,能根据XML文件播放剧情) / 5(会制作剧情编辑器) 本章源码下载:http://www.iamseven ...
- oracle数据库修改编码
(1)SYSTEM 用户登录SQLPLUS SYS是sysdba用户,不能直接登录 (2)SYSDBA登录 CONN / as sysdba; (3)查看数据库字符集 ...