所有的的数据,在最底层要么是字符,要是数字,redis中的所有的key都是string类型的
string
在redis3.2之前,使用的上述结构,一个char数组,一个使用长度,一个还剩多长,但这样在string比较短时候,比较浪费空间。
zhi
这个数据结构我们称之为 简单动态字符串(simple dynamic string,SDS)。
在3.2之后改成了这样:
在string长度小于32的时候(图片右下角)使用sdshdr5,用一个8为位的flag存储类型(高三位)和长度(低五位)。在实际的应用中,这个类型没有没用上,因为他不能扩容,他会被转成成下面的数据结构存储。
在string长度小于 2的8次方-1 的时候,用一个8为位的flag存储类型(高三位)和 第五位不存储东西;再用一个8位的len存储长度;再用一个8位alloc存储申请的内存空间
在string长度小于 2的32次方-1 的时候,用一个8为位的flag存储类型(高三位)和 第五位不存储东西;再用一个32位的len存储长度;再用一个32位alloc存储申请的内存空间
以此类推直到64位。
数字的是时候(int)
redisObject最后的prt占8字节,当存入的时候会把我们的value,强转一下,如果可以那么就直接存在着。int 4 long8 folt4 double8 正好。bitmap也是直接存在这,最大空间为2的8*8次方
当字符串长度小于等于44,类型是embstr,为什么是44?
当小于等于44的时候能直接开辟一个连续的空间,也就是一个缓存行,正好将redisObject和sds正好存到这个缓存行行中。redisObject整个的空间是16字节,sds中除去数据部分还有4字节确定的空间,分别是char数组中最后的\0、len、alloc、flags,缓存行是64字节,64-16-4=44(字节)。
raw:字符串长度大于44。当对短字符串使用append的时候,直接变成embstr,因为涉及到了字符串的拼接,也就是扩容。
list
使用lpoprpush命令取数据,事务异常可以从备份list中,回滚
为什么没有直接使用一个双端链表,而是有用了一个ziplist呢?
因为对于每一个qiuicklistNode都有一个前后指针,每个node都有(双向指针16字节),多了就很浪费空间。ziplist是一个非常紧凑的数据结构,比较节省空间。
那为什么不直接用ziplist?又整除一个quicklist
ziplist是一个非常紧凑的数据类型,当数据量过大的时候,操作起来也比较麻烦。所以使用quicklist分开
ziplist
zlbytes:32bit,表示ziplist占用的字符总数。
zltail:32bit,表示ziplist表中最后一项entry在ziplist中的偏移字节数。通过zltail我们可以很方便的找到最后一项,从而可以在ziplist尾端快速的执行push或者pop操作。
zlen:16bit,表示ziplist中数据项entry的个数。
zlend:zuplist最后一个字节,是一个结束标记,值固定等于255
entry:表示真正存放数据的数据项,长度不定
prerawlen:前一个entry的数据长度
len:entry中数据长度
data:真实数据存储quicklist
quicklist中有quicklistNode,quicklistNode中是ziplist。可以通过参数设置没有个ziplsit的大小,也可以设置quicklist的压缩,压缩之后,所需要的连续的内存空间就会变小,中间的压缩,两头的不压缩,因为list中,只关注头结点。空间换时间,解决大数据量下ziplist寻址慢的问题。lsit是怎么实现阻塞的
redis本身就是一个全局的hash表,redisDb。在这个表中,会维护一个blocking_keys,这个的底层也是字典,会把阻塞的key和客户端连关联维护起来。hash
在ziplist中一个entry中存储key,下一个entry存储value。使用ziplist存储的时候,这个set是有序的,顺序就是你加入的顺序。但使用dict(hashtable)存储的时候是无序的。
redisDb中dict(hashtable)中有两个数组(ht[2]), 平时只会用到一个,收缩扩容的时候会用到第二个,根据key,hash到对应的数组位置,数组里存的是dictEntry,dictEntry中存储的是key和val指针,val指向redisObject,在这里redisObject的类型是hash,dict(hashtable)的两个数据,dictEntry存储key和val指正,val指向真正的值redisObject,redisObject现在是int、embstr、raw。set
元素是整形,并且数量少于阈值用intset存储,不满足条件的时候,使用value为null的dict(hashtable)存储。
contents数组中存储真正的int值。zset
当数据量小于阈值的时候使用ziplist存储,v1 s1 v2 s2。
大于阈值使用跳表存储
zset
dict 字典,就像hash一样key,value的存
zskiplist 跳表
zskiplist
zskiplistNode 头尾指针
length 元素个数
level 有效层级
zskiplistNode
sds 元素
double 分数
zskiplistNode 回退指针
zskiplistLevel(有向前的指针) 层级数组
backward回退的指针,但没有向前的指针。向前的指针在zskiplistLevel中,是forward。
zset中,数据在dict中存一份,zsl存一份。zskiplist存储这头尾节点,和当前的有效层。当查找150的时候,访问头节点的zskiplistNode中level数组中的有效层2层,2层指向了120,120小于150,那么便利120的level数组,第一个指向了尾,再看下一个,下一个指向了200,200大于150,那么走200的回退指针,发现是100,100小于150,那么150不存在。redis本身就是一个全局哈希表,单机的redis有16的db,每个db都有一个redisDb哈希表