一:底层数据结构

1、SDS

struct sdshdr {int len;int free;char buf[];
}

注:
1.1、'\0’结尾遵循C语言规范,可以直接调用C语言字符串的部分api
1.2、记录len无需遍历获取长度以及实现二进制安全(不同于C语言字符串,SDS可以保存任意格式的二进制数据,不用出现特殊字符提前结束的情况)
1.3、记录free实现空间预分配和惰性释放(以减少字符串变化带来的内存分配,同时避免内存泄露和溢出)

2、链表

typedef struct listNode {struct listNode *prev;struct listNode *next;void *value;
} listNode;typedef struct list {listNode *head;listNode *tail;unsigned long len;void *(*dup)(void *ptr);void (*free)(void *ptr);int (*match)(void *ptr, void *key);
} list;

注: 双向、无环、带头、带尾、带len

3、字典

hash表

typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;} v;struct dictEntry *next;
} dictEntry;typedef struct dictht {dictEntry **table;unsigned long size;// 哈希表大小掩码,用于计算索引值// 总是等于 size - 1unsigned long sizemask;unsigned long used;
} dictht;

字典
(即由两个hash表和其他一些信息字段组成)

typedef struct dict {// 类型特定函数dictType *type;// 私有数据void *privdata;// 哈希表dictht ht[2];// rehash 索引// 当 rehash 不在进行时,值为 -1(渐进rehash)int rehashidx;
} dict;

注:
3.1、Redis 计算哈希值和索引值的方法如下:
hash = dict->type->hashFunction(key); //使用字典设置的哈希函数,计算键 key 的哈希值
index = hash & dict->ht[x].sizemask; //使用哈希表的 sizemask 属性和哈希值,计算出索引值。ht[x] 可以是 ht[0] 或者 ht[1]
3.2、hash冲突是在头加(只能在头家因为dictEntry没有指向尾的指针)
3.3、因为大部分系统都采用写时复制技术,所以为了尽量避免内存复制,hash表在aop,或者rdb时的扩容因子由1变成5
3.4、rehash是渐进的。h[0]只减不加,并且结束后把h[1]给h[0]

4、跳表

typedef struct zskiplistNode {// 后退指针struct zskiplistNode *backward;// 分值double score;// 成员对象robj *obj;// 层struct zskiplistLevel {// 前进指针struct zskiplistNode *forward;// 跨度unsigned int span} level[];
} zskiplistNode;typedef struct zskiplist {// 表头节点和表尾节点struct zskiplistNode *header, *tail;// 表中节点的数量unsigned long length;// 表中层数最大的节点的层数int level;
} zskiplist;


注:
4.1、跳跃表是有序集合的底层实现之一, 除此之外它在 Redis 中没有其他应用。
4.2、每个跳跃表节点的层高都是 1 至 32 之间的随机数。
4.3、在同一个跳跃表中, 多个节点可以包含相同的分值, 但每个节点的成员对象必须是唯一的。
4.4、跳跃表中的节点按照分值大小进行排序, 当分值相同时, 节点按照成员对象的大小进行排序。

5、整数集合

typedef struct intset {// 编码方式uint32_t encoding;// 集合包含的元素数量uint32_t length;// 保存元素的数组,保存的整数类型取决于encodingint8_t contents[];
} intset;

注:升级机制以提高灵活性和节约内存(必要时才会使用大的类型)。且整数集合不存在降级

6、压缩列表



previous_entry_length:前一个节点的长度
encoding:记录了节点的 content 属性所保存数据的类型以及长度
content:节点值可以是一个字节数组或者整数, 值的类型和长度由节点的 encoding 属性决定

注:
6.1、是列表和hash对象的底层实现之一
6.2、当有多个连续介与250-253之间的节点是,添加新节点可能引起前面节点的扩容进而导致连锁更新。(原本的previous_entry_length由1个字节变成5个字节)。同理删除节点也可能导致连锁反应

二、对象

typedef struct redisObject {// 类型(可以是STRING,LIST,HASH,SET,ZSET)unsigned type:4;// 编码unsigned encoding:4;// 指向底层实现数据结构的指针void *ptr;// 引用计数int refcount// 最近活跃时间unsigned lru;
} robj;

对象类型可以是:

编码可以是:

各对象类型不同编码所表示的对象:

注:
1、redis对象也采用引用计数法回收对象
2、只能共享0-9999的整数字符串对象
3、lru会记录最后一次被访问时间,用于计算对象空转时长

三、浅浅扒一扒源码

以sds为例,每个底层数据结构都会有个文件去定义

而redis对象则是定义在server.h文件里——redisObject

数据库存储数据的则是在redisServerredisDb结构体(数据库对象)。redisServer是对服务器状态的一种描述,像端口号,是否开启aof,数据库字典等服务器信息…同样定义在server.h文件


与redisServer相对的则是client,client是对客户端连接状态的一种描述(会为每个连接新建一个client)。存储与客户端相关的数据,像db是连接的数据库,querybuf则是命令缓存,argc和argv则分别是命令参数的个数和值等信息…同样定义在server.h文件

上面提到了sds,那就顺便再讲讲redis_string(毕竟还挺常用)

redis_string

SDS初始化

SDS扩容


从上面可知:扩容时,当大小小于1M则 newLen*2,大于1M则再 newLen+1M。sds的这种空间预分配和动态扩容,使得其在append字符串时不必每次都copy字符串,只有当type发生变化时才需copy。

redis string最大512M从何何来


从上图可以看到,在t_string(即redis string对象)文件中有checkStringLength函数。上面明确了大小不能大于512M。所以这个512M是人为限制的,并不是指redis的string对象或者说sds的数据结构最大只能支持512M。个人人为其实sdshdr64的最大长度应该是 2 ^64。(其实在后面的新版本中,redis_string 引入了proto_max_bulk_len参数可以自定义配置字符串的最大长度,默认为512M)

embstr 44个字节从何而来?

在创建string对象时会根据长度是否大于44而决定是否用raw

那为什么是44不是45,46呢?

因为embstr若要使用一段连续的内存,那这个连续内存肯定要有个大小限制,这里的限制redis作者用64个字节。即取一个内存为64字节的redisObject。
其中redisObject本身数据占用 :
type(4bit),encoding(4bit),LRU(3字节),refCount(4字节),ptr(8字节)
最小的SDS为sdshdr8,其内存占用情况:
free(1字节),len(1字节),flags(1字节),content(x字节)
用64减一下,得到45,再减去 ‘/0’ 的结束符,得44字节。

参考文献:《Redis设计与实现》黄健宏著、redis-5.0.14源码

Redis源码篇(1)——底层数据结构与对象相关推荐

  1. redis源码剖析(3):基础数据结构dict

    目录 1.dict概述 2.字典的定义 3.哈希算法 4.字典的初始化及新增键值对 4.1 字典初始化 4.2 新增键值对 5.rehash(重新散列)操作 5.1 rehash操作方式 5.2 re ...

  2. Redis源码-Set:Redis Set存储原理、Redis Set集合操作命令、Redis Set两种存储底层编码intset+hashtable、Redis Set应用场景

    Redis源码-Set:Redis Set存储原理.Redis Set集合操作命令.Redis Set两种存储底层编码intset+hashtable.Redis Set应用场景 Redis数据类型 ...

  3. Redis源码-ZSet:Redis ZSet存储原理、Redis ZSet命令、 Redis ZSet两种存储底层编码ziplist/dict+skiplist、Redis ZSet应用场景

    Redis源码-ZSet:Redis ZSet存储原理.Redis ZSet命令. Redis ZSet两种存储底层编码ziplist/dict+skiplist.Redis ZSet应用场景 Red ...

  4. 转载一篇《Redis源码研究—哈希表》重点是如何重新哈希

    <Redis源码研究-哈希表>来自:董的博客 网址:http://dongxicheng.org/nosql/redis-code-hashtable/ 转载于:https://www.c ...

  5. 读懂 Redis 源码,我总结了这7点心得

    作者|Magic Kaito 来源|水滴与银弹 阅读本文大约需要 8 分钟. 你好,我是 Kaito. 用了这么久的 Redis,也翻了很多次源码,经常有人问我到底怎么读 Redis 源码. 一提到读 ...

  6. redis源码dict.c simple reading

    2019独角兽企业重金招聘Python工程师标准>>> 0. precondition 首先,你需要知道Redis的六种数据结构,包括list, set, zset, string, ...

  7. Redis源码学习(20),学习感悟

      最近学习Redis源码也有半个月的时间了,有不少收获也有不少感悟,今天来好好聊聊我学习的感悟. 1 发现问题   人非圣贤孰能无过,只要是人难免会犯错,回顾我之前的学习历程,其实是可以发现不少的问 ...

  8. redis源码笔记 - 刘浩de技术博客 - 博客园

    redis源码笔记 - 刘浩de技术博客 - 博客园 redis源码笔记 - 刘浩de技术博客 - 博客园 redis源码笔记 记录发现的一个hiredis的bug 摘要: hiredis是redis ...

  9. 出招吧!腾讯专家手敲《Redis源码日志笔记》,不服来对打!

    引言 本文分为六个部分,包括 Redis 源码日志,服务框架,基础数据结构,内功心法,应用,其他,从源码层面循序渐进的了解Redis.可以快速.有效地了解Redis 的内部构造以及运作机制,更好.更高 ...

最新文章

  1. Discuz DB层跨库映射关系表名前缀BUG修复后产生的新bug
  2. show一下新模板(分享全套CSS和图片)
  3. 具有ReadWriteLock的Java并发
  4. vue+elementUI 添加多个可以全选的多选框
  5. 【动态规划】牛客网:礼物的最大价值(数塔问题)
  6. Java基础笔记(三)
  7. 什么叫运营---一个人,一张网,一艘船,独钓寒江雪!
  8. 怎样用计算机算一条线的斜率,直线的斜率
  9. 火狐控制台的html,怎么使用火狐浏览器调试网页
  10. 【代码猴子-培养正确的编程态度和方法】--《编程匠艺》
  11. vue2源码解读笔记(一)
  12. 科银资本 Jayden Wei 专访:全球首个区块链经济特区即将诞生
  13. 【python】13位时间戳转成正常格式的时间
  14. Spring+SpringMVC+Jsp实现校园二手交易系统
  15. 技术人如何打造个人品牌,提高身价?
  16. window服务是什么?
  17. 让低版本的IE浏览器支持HTML5
  18. 图片转换成16进制数据,在显示成图片
  19. vue 电子表格Excel的上传导入、导出下载、读取本地Excel、json转Excel
  20. 毕业登记表批量打印参考代码

热门文章

  1. 美国顶尖大学特别的感恩节传统……
  2. 如何在HTML文档中显示空格
  3. 为什么微信连接不上服务器失败怎么回事啊,微信为什么一直提示连接失败请检查网络设置...
  4. 个人卖云服务器需要什么证,云服务器个人使用能做什么 云服务器要不要个人认证...
  5. LeetCode 71-80题
  6. 用计算机探索规律有什么知识点5上,用计算器探索规律知识点
  7. 大小写26个英文字母对应的ASCII值
  8. 干了这碗蛋炒饭 继续APP性能提升
  9. C++批量修改文件后缀名(提供多种方法)
  10. 怎样查询SCI和EI检索号