Redis专题(2):Redis数据结构底层探秘
作者:网友投稿 时间:2019-06-13 01:47
上篇文章Redis闲谈(1):构建知识图谱介绍了Redis的基本概念、优缺点以及它的内存淘汰机制,相信大家对Redis有了初步的认识。互联网的很多应用场景都有着Redis的身影,它能做的事情远远超出了我们的想像。Redis的底层数据结构到底是什么样的呢,为什么它能做这么多的事情?本文将探秘Redis的底层数据结构以及常用的命令。
本文知识脑图如下:

一、Redis的数据模型
用 键值对 name:"小明"来展示Redis的数据模型如下:

dictEntry:在一些编程语言中,键值对的数据结构被称为字典,而在Redis中,会给每一个key-value键值对分配一个字典实体,就是“dicEntry”。dicEntry包含三部分: key的指针、val的指针、next指针,next指针指向下一个dicteEntry形成链表,这个next指针可以将多个哈希值相同的键值对链接在一起,通过链地址法来解决哈希冲突的问题
sds :Simple Dynamic String,简单动态字符串,存储字符串数据。
redisObject:Redis的5种常用类型都是以RedisObject来存储的,redisObject中的type字段指明了值的数据类型(也就是5种基本类型)。ptr字段指向对象所在的地址。
RedisObject对象很重要,Redis对象的类型、内部编码、内存回收、共享对象等功能,都是基于RedisObject对象来实现的。
这样设计的好处是:可以针对不同的使用场景,对5种常用类型设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
Redis将jemalloc作为默认内存分配器,减小内存碎片。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分了许多小的内存块单位;当Redis存储数据时,会选择大小最合适的内存块进行存储。
二、Redis支持的数据结构
Redis支持的数据结构有哪些?
如果回答是String、List、Hash、Set、Zset就不对了,这5种是Redis的常用基本数据类型,每一种数据类型内部还包含着多种数据结构。
用encoding指令来看一个值的数据结构。比如:
127.0.0.1:6379> set name tom
OK
127.0.0.1:6379> object encoding name
"embstr
此处设置了name值是tom,它的数据结构是embstr,下文介绍字符串时会详解说明。
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> object encoding age
"int"
如下表格总结Redis中所有的数据结构类型:

补充说明:
假如面试官问:Redis的数据类型有哪些?回答:String、list、hash、set、zet
一般情况下这样回答是正确的,前文也提到Redis的数据类型确实是包含这5种,但细心的同学肯定发现了之前说的是“常用”的5种数据类型。其实,随着Redis的不断更新和完善,Redis的数据类型早已不止5种了。
登录Redis的官方网站打开官方的数据类型介绍(https://redis.io/topics/data-types-intro):

发现Redis支持的数据结构不止5种,而是8种,后三种类型分别是:
位数组(或简称位图):使用特殊命令可以处理字符串值,如位数组:您可以设置和清除各个位,将所有位设置为1,查找第一个位或未设置位,等等。
HyperLogLogs:这是一个概率数据结构,用于估计集合的基数。不要害怕,它比看起来更简单。
Streams:仅附加的类似于地图的条目集合,提供抽象日志数据类型。
本文主要介绍5种常用的数据类型,上述三种以后再共同探索。
1. string字符串
字符串类型是Redis最常用的数据类型,在Redis中,字符串是可以修改的,在底层它是以字节数组的形式存在的。
Redis中的字符串被称为简单动态字符串「SDS」,这种结构很像Java中的ArrayList,其长度是动态可变的。
struct SDS<T> {
T capacity; // 数组容量
T len; // 数组长度
byte[] content; // 数组内容
}

content[] 存储的是字符串的内容,capacity表示数组分配的长度,len表示字符串的实际长度。
字符串的编码类型有int、embstr和raw三种,如上表所示,那么这三种编码类型有什么不同呢?
int 编码:保存的是可以用 long 类型表示的整数值。
raw 编码:保存长度大于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。
embstr 编码:保存长度小于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。
设置一个值测试一下:
127.0.0.1:6379> set num 300
127.0.0.1:6379> object encoding num
"int"
127.0.0.1:6379> set key1 wealwaysbyhappyhahaha
OK
127.0.0.1:6379> object encoding key1
"embstr"
127.0.0.1:6379> set key2 hahahahahahahaahahahahahahahahahahahaha
OK
127.0.0.1:6379> strlen key2
(integer) 39
127.0.0.1:6379> object encoding key2
"embstr"
127.0.0.1:6379> set key2 hahahahahahahaahahahahahahahahahahahahahahaha
OK
127.0.0.1:6379> object encoding key2
"raw"
127.0.0.1:6379> strlen key2
(integer) 45
aw类型和embstr类型对比:
embstr编码的结构:

raw编码的结构:




