redis学习之数据结构与对象(一)
1.SDS
简介:Redis没有采用C语言的以空字符串结尾的字符数组,而是构建一种简单动态字符串(Simple dynamic string,SDS),并将它作为string的表示。
struct sdshdr {// buf 中已占用空间的长度int len;// buf 中剩余可用空间的长度int free;// 数据空间char buf[]; // 依然以’\0’结尾
};
SDS与C语言的字符串的区别
- 常数复杂度获取字符串长度。
- 通过free杜绝缓存区溢出
- 空间预分配(如果len小于1MB,那么分配的free将等于len,如果len大于1MB那么分配free1MB)和惰性空间释放(缩短SDS时不立即回收内存而是记录到free中)
二进制安全,所以能保存图片,视频,音频文件
常用操作:- get:sdsrange—O(n)
- set:sdscpy—O(n)
- create:sdsnew—O(1)
- len:sdslen—O(1)
2.链表
typedef struct listNode {// 前置节点struct listNode *prev;// 后置节点struct listNode *next;// 节点的值void *value;} listNode;typedef struct list {// 表头节点listNode *head;// 表尾节点listNode *tail;// 节点值复制函数void *(*dup)(void *ptr);// 节点值释放函数void (*free)(void *ptr);// 节点值对比函数int (*match)(void *ptr, void *key);// 链表所包含的节点数量unsigned long len;} list;
常用操作:
- rpush: listAddNodeHead —O(1)
- lpush: listAddNodeTail —O(1)
- push:listInsertNode —O(1)
- index : listIndex —O(N)
- pop:ListFirst/listLast —O(1)
- llen:listLength —O(N)
3.字典
字典在底层是由哈希表实现的,所以我们从哈希表开始入手。
(1)哈希表
typedef struct dictht {// 哈希表数组dictEntry **table;// 哈希表大小unsigned long size;// 哈希表大小掩码,用于计算索引值// 总是等于 size - 1unsigned long sizemask;// 该哈希表已有节点的数量unsigned long used;
} dictht;
(2)哈希节点
typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;} v;// 指向下个哈希表节点,形成链表struct dictEntry *next;
(3)字典
typedef struct dict {// 类型特定函数dictType *type;// 私有数据void *privdata;// 哈希表dictht ht[2];// rehash 索引// 当 rehash 不在进行时,值为 -1int rehashidx; /* rehashing not in progress if rehashidx == -1 */// 目前正在运行的安全迭代器的数量int iterators; /* number of iterators currently running */} dict;
(4)字典特定函数
/** 字典类型特定函数*/
typedef struct dictType {// 计算哈希值的函数unsigned int (*hashFunction)(const void *key);// 复制键的函数void *(*keyDup)(void *privdata, const void *key);// 复制值的函数void *(*valDup)(void *privdata, const void *obj);// 对比键的函数int (*keyCompare)(void *privdata, const void *key1, const void *key2);// 销毁键的函数void (*keyDestructor)(void *privdata, void *key);// 销毁值的函数void (*valDestructor)(void *privdata, void *obj);} dictType;
总结:字典这种数据结构看起来十分复杂,其实十分简单,只要看过Java的HashMap的源代码都应该很容易懂。
注意:
1. 哈希表有一个数据结构,unsigned long sizemask;
有什么用呢,就是让本来的 “哈希节点的key % size” 计算出索引变成 “哈希节点的key & sizemask”,位运算更加高效,当然前提是哈希节点是2的x次幂。
2. rehash也是跟Java相似,为什么dict的ht[2]有两个哈希表呢,原来dict[0]是原来的哈希表,dict[1]是扩容后的哈希表。
常用操作:
- Hset: dictAdd/dictReplace —-O(1)
- Hget: dictFetchValue—O(1)
- Randomkey:dictGetRandomKey—O(1)
- Hdel/del: dictDelete—O(1)
4.整数集合
整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。
typedef struct intset {// 编码方式uint32_t encoding;// 集合包含的元素数量uint32_t length;// 保存元素的数组int8_t contents[];} intset;
常用操作:
- sadd:intsetAdd—O(1)
- smembers:intsetGetO(1)—O(N)
- srem:intsetRemove—O(N)
- slen:intsetlen —O(1)
5.压缩列表
压缩列表是列表键和hash键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么redis就会使用压缩列表来做列表键的底层实现。
zlbytes代表整个压缩列表占用的内存字节数。
zltail代表压缩列表起始地址到尾节点的偏移量。
zllen节点数量
entry 节点
zlend 0xFF标记压缩列表末端。
常用操作:
- Push/hset:ziplistPush/ziplistInsert 平均O(N), 最坏O(N2)
- Llen/hlen:ziplislen平均O(N), 最坏O(N2)
- Hget:ziplistGet —-O(1)
- Index:ziplistIndex—-O(N)
- Hdel/pop:ZiplistDelete—平均O(N), 最坏O(N2)
- 遍历:ziplistPrev/ziplistNext—-O(1)
6. 对象
字符串对象
字符串对象的编码可以是int,raw,embstr
如果是整数值,则编码是int,如果是小于32字节的字符串,则编码是embstr,如果是大于32字节的字符串,则编码是raw。
下面是他们的对象结构:
embstr和raw的区别是 raw编码调用了两次内存分配函数来分配redisObject结构和sdshdr结构,而embstr只需要调用一次分配一段连续的空间。
列表对象
列表对象的编码可以是ziplist或者linkedlist。
当列表对象可以同时满足一下两个条件时,列表对象使用ziplist编码:
- 列表保存的所有字符串元素的长度都小于64字节;
- 列表对象保存的元素数量小于512个。
不能满足这两个条件的都是用LinkedList编码。
下面就是这个对象的两种编码的结构:
每个StringObject实际上就是一个sdshdr。
哈希对象
哈希对象的编码可以是ziplist或者是hashtable。
当哈希对象同时满足以下两个条件时,哈希对象使用ziplist对象:
- 哈希对象保存的所有键和值的字符串长度都小于64个字节;
- 哈希对象保存的键值对数量小于512个
不能满足这两个条件的哈希对象需要使用hashtable编码。
下面就是这个对象的两种编码的结构:
集合对象
集合对象的编码可以是intset和hashtable
当集合对象同时满足以下条件时,对象使用intset编码:
- 集合对象保存的所有元素都是整数值
- 集合对象保存的元素数量不超过512个
不能满足这两个条件的集合对象需要使用hashtable编码。
下面就是这个对象的两种编码的结构:
有序集合对象
有序集合对象的编码可以是ziplist和skiplist
当集合对象同时满足以下条件时,对象使用intset编码:
- 有序集合保存的元素数量小于128个
- 有序集合保存的所有元素成员的长度都小于64字节
不能满足这两个条件的集合对象需要使用skiplist编码。
下面就是这个对象的两种编码的结构:
ziplist编码的有序集合对象其实跟集合对象的差不多,区别是集合元素按分值从小到大进行排序。
7.对象的引用计数
因为C语言并不具备自动内存回收功能,所以redis对象系统构建了一个引用计数refcount技术来实现内存回收机制,通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象进行内存回收。
对象的引用计数信息会随着对象的使用状态而不断变化:
- 在创建一个新对象时,引用计数的值会被初始化为1;
- 当对象被一个新程序使用时,它的引用计数值会被增1
- 当对象不再被一个程序使用时,它的引用计数会减1
- 当对象的引用计数为0时,对象所占用的内存会被释放。
redis学习之数据结构与对象(一)相关推荐
- 【Redis笔记】数据结构和对象:字典
字典:是保存键值对的抽象数据结构. 在字典中,一个键(Key)可以和一个值(value)进行关联(键映射为值),这些关联的键和值就成键值对. 字典中的每个键是独一无二的,程序可以通过键来查找值,或者通 ...
- 【Redis笔记】数据结构与对象:链表
链表在Redis中的应用: 列表键的底层实现之一是链表: 发布与订阅,慢查询,监视器等功能也用到了链表: Redis服务器本身使用链表保存多个客户端的状态信息: 使用链表来构建客户端输出缓冲区: 链表 ...
- Redis实战(2)-数据结构之字符串String实战之存储对象
概述:本系列博文所涉及的相关内容来源于debug亲自录制的实战课程:缓存中间件Redis技术入门与应用场景实战(SpringBoot2.x + 抢红包系统设计与实战),感兴趣的小伙伴可以点击自行前往学 ...
- 十二张图带你了解 Redis 的数据结构和对象系统
来自公众号:程序员历小冰 Redis是一个开源的 key-value 存储系统,它使用六种底层数据结构构建了包含字符串对象.列表对象.哈希对象.集合对象和有序集合对象的对象系统.今天我们就通过12张图 ...
- 十二张图详解Redis的数据结构和对象系统
回顾:大数据平台技术栈 (ps:可点击查看),今天就来说说其中的Redis! 本文来自:张狗蛋的技术之路 Redis是一个开源的 key-value 存储系统,它使用六种底层数据结构构建了包含字符串对 ...
- redis(2)--数据结构与对象
简单动态字符串 (SDS)simple dynamic string 定义 struct sdshdr {// 记录 buf 数组中**已使用**字节的数量 // 等于 SDS 所保存字符串的长度 i ...
- 【Redis-09】面试题之Redis数据结构与对象-RedisObject(下篇)
承接上篇[Redis-08]面试题之Redis数据结构与对象-RedisObject(上篇) 8. type-字符串string 8.1 字符串的三种encoding编码(int + embstr ...
- Redis 的数据结构和对象系统 侵立删
Redis是一个开源的 key-value 存储系统,它使用六种底层数据结构构建了包含字符串对象.列表对象.哈希对象.集合对象和有序集合对象的对象系统.今天我们就通过12张图来全面了解一下它的数据结构 ...
- redis之数据结构与对象篇(终章)
欢迎阅读大魔王的睡前私语系列,这是Redis第四篇文章 压缩列表 压缩列表是列表键和哈希键的底层实现之一.当一个列表键只包含少量列表项,并且每个列表项要么是小整数值,要么是长度比较短的字符串,那么Re ...
最新文章
- SmsManager#sendTextMessage的BUG
- python 回文链表
- BASIC-13 数列排序
- lisp eval apply_SICP远古魔法-LISP概览(1-2)
- 35岁的互联网人出路在哪?
- javascript数组对象
- pyinstaller 打包出现问题
- 翁恺java答案第五周_[JAVA]翁恺老师零基础学java语言第五周素数例题中continue的困惑...
- 使用SQL查询所有数据库名和表名
- altium Designer布等长线、蛇形线
- 电大计算机绘图三锥图形,电大机械制图课程教学辅导
- polyval polyvalm
- PS4 5.05安装Linux系统,PS4主机刷机教程以及游戏安装教程,到5.05 4.55 4.05等系统
- 【转】欧姆龙FINS通讯命令实例详解
- Image Pro Plus6进行荧光分析与荧光照片合成学习笔记
- Java学习---控制流程与方法
- QR码国家标准(图片和表格后续会更新)
- 人机情绪互通:新的机器学习模型可以预测人类情绪
- 深入了解 Swarm:去中心化存储和通信系统
- Windows XPE 安装