一、Redis 数据类型相关

数据类型实际描述的是 value 的类型,key 都是 string,常见数据类型(value)

string(embstr、raw、int)

list(quicklist(多个 ziplist 双向链表))

hash(ziplist、hashtable)

set(intset、hashtable)

sorted set(ziplist、skiplist)

bitmap

hyperloglog

每一种类型都用 redisObject 结构体来表示,每种类型根据情况不同,有不同的编码 encoding(即底层数据结构)

二、String

1.如果字符串保存的是整数值,则底层编码为 int,实际使用 long 来存储

2.如果字符串保存的是非整数值(浮点数字或其它字符)又分两种情况

长度 <= 39 字节,使用 embstr 编码来保存,即将 redisObject 和 sdshdr 结构体保存在一起,分配内存只需一次

长度 > 39 字节,使用 raw 编码来保存,即 redisObject 结构体分配一次内存,sdshdr 结构体分配一次内存,用指针相连

3.sdshdr 称为简单动态字符串,实现上有点类似于 java 中的 StringBuilder,有如下特性

单独存储字符长度,相比 char* 获取长度效率高(char* 是 C语言原生字符串表示)

支持动态扩容,方便字符串拼接操作

预留空间,减少内存分配、释放次数(< 1M 时容量是字符串实际长度 2 倍,>= 1M 时容量是原有容量 + 1M)

二进制安全,例如传统 char* 以 \0 作为结束字符,这样就不能保存视频、图片等二进制数据,而 sds 以长度来进行读取

三、List

1.3.2 开始,Redis 采用 quicklist 作为其编码方式,它是一个双向链表,节点元素是 ziplist

由于是链表,内存上不连续

操作头尾效率高,时间复杂度 O(1)

链表中 ziplist 的大小和元素个数都可以设置,其中大小默认 8kb

2.ziplist 用一块连续的内存存储数据,设计目标是让数据存储更紧凑,减少碎片开销,节约内存,它的结构如下

zlbytes – 记录整个 ziplist 占用字节数

zltail-offset – 记录尾节点偏移量

zllength – 记录节点数量

entry – 节点,1 ~ N 个,每个 entry 记录了前一 entry 长度,本 entry 的编码、长度、实际数据,为了节省内存,根据实际数据长度不同,用于记录长度的字节数也不同,例如前一 entry 长度是 253 时,需要用 1 个字节,但超过了 253,需要用 5 个字节

zlend – 结束标记

3.ziplist 适合存储少量元素,否则查询效率不高,并且长度可变的设计会带来连锁更新问题

四、Hash

1.在数据量较小时,采用 ziplist 作为其编码,当键或值长度过大(64)或个数过多(512)时,转为 hashtable 编码

2.hashtable 编码

        hash 函数,Redis 5.0 采用了 SipHash 算法

        采用拉链法解决 key 冲突


rehash 时机



当元素数 < 1 * 桶个数时,不扩容


当元素数 > 5 * 桶个数时,一定扩容


当 1 * 桶个数 <= 元素数 <= 5 * 桶个数时,如果此时没有进行 AOF 或 RDB 操作时


当元素数 < 桶个数 / 10 时,缩容



                rehash 要点


每个字典有两个哈希表,桶个数为 2 𝑛 2^n, 平时使用 ht [0] , ht [1] 开始为 null ,在扩容时新数组大小为元素个数 * 2


渐进式 rehash ,即不是一次将所有桶都迁移过去,每次对这张表 CRUD 仅迁移一个桶


active rehash , server 的主循环中,每 100 ms 里留出 1s 进行主动迁移


rehash 过程中,新增操作 ht [1] ,其它操作先操作 ht [0] ,若没有,再操作 ht [1]


redis 所有 CRUD 都是单线程,因此 rehash 一定是线程安全的


五、Sorted Set

1.在数据量较小时,采用 ziplist 作为其编码,按 score 有序,当键或值长度过大(64)或个数过多(128)时,转为 skiplist + hashtable 编码,同时采用的理由是

        只用 hashtable,CRUD 是 O(1),但要执行有序操作,需要排序,带来额外时间空间复杂度

        只用 skiplist,虽然范围操作优点保留,但时间复杂度上升

        虽然同时采用了两种结构,但由于采用了指针,元素并不会占用双份内存

2.skiplist (跳表)要点:多层链表、排序规则、 backward、level(span,forward)

        整个跳表是有序的,下图按照score排好序,如果score,再按其他数据排。backward可以用于倒叙遍历。level是层级,每个数据层级可能不一样,level里面有两个属性,forward是前向指针,span用于计算排名,记录两个节点之间的跨度

redis底层有用到zk吗 redis底层数据类型_数据库

六、跳表查询

skiplist 查找要点,从顶层开始

> 右边的,继续向右

= 找到了

< 右边的或右边为 NULL,下一层,重复 1、2 步骤

以查找 score = 22 为例,虽然跳表是有序的,但是底层是链表结构,不能使用二分法。多层链表就是为了减少比较次数,提高效率。先从第四层开始,同层的右侧元素null,则下一层,第三层,和右侧元素37比,下一层,第二层>右侧元素,向右小于37,下一层就找到22

redis底层有用到zk吗 redis底层数据类型_redis_02