redis五种基本数据类型

redisObject

在了解redis基本数据类型之前,需要先了解redisObject对象,redis中所有键值都是redisObject对象。

typedef struct redisObject {
    // 类型
    unsigned type:4;
    // 编码
    unsigned encoding:4;
    // 指向数据的指针
    void *ptr;

    // 记录对象最后一次被程序访问时间,用于计算空转时长(当前时间-lru)
    unsigned lru:22; /* lru time (relative to server.lruclock) */
    // 引用计数,用于内存回收
    int refcount;
} robj;

type: 对象类型名称
如:string、hash、list、set、zset
encoding:对象编码类型
如:int、embstr、raw、hashtable、ziplist、intset、linkedlist、skiplist
*ptr:数据存放地址指针
lru:对象最后一次被访问的时间
refcount:引用计数

1、string

string类型在redis中是最基本的数据类型,其中redis的所有key都是string类型的。

string适用场景

计数器、粉丝数、图片、序列化后的对象

string底层使用三种结构存储:
①int:当存储的字符串全是数字时,使用int的方式进行存储
②embstr:当存储的字符串长度小于39个字节时,使用embstr方式存储(redis3.2版本后优化为44个字节)
③raw:当存储的字符串长度大于39个字节时,使用raw方式来存储(redis3.2版本后优化为44个字节)
int类型不用多说,embstr为什么是以39个字节为界?后续在哪优化为44个字节?embstr和raw有什么区别?话不多说,上图:

embstr

raw
embstr与raw的区别

先说一下区别,embstr和raw都是用来存储string数据的,并且都是使用SDS来存储,区别在于embstr是一块连续的内存,而raw是两个不连续的内存,因为raw是用来存长字符串的,所以SDS相对来说占用内存较大,而申请一块连续较大的内存是得不偿失的,embstr存数据时是只读的,如果需要改变embstr存储的value时,redis底层会将string的存储方式由embstr转换为raw,然后再去修改其value。embstr相对raw的优势在于申请内存和释放内存时只需要一次,而raw申请/释放两块不连续的内存需要两次,在效率上,embstr肯定是高于raw的。

SDS数据结构

RedisObject的数据结构我们上面讲过了,其中指针*ptr指向数据存放地址,在embstr和raw这两个结构中string的value都是存储在SDS中的,SDS是什么呢?
SDS(simple dynamic string):

struct shsshr<T>{
    T len; // 数组长度
    T alloc; // 数组容量
    unsigned flags; // sdshdr类型
    char buf[]; // 数组内容
}

len:数组长度
alloc:数组容量
buf[]:存放value的字符数组

为什么3.2版本之前的embstr是以39个字节为界,因为一块内存大小为64字节的sds,减去len、alloc等所占用的内存后,剩下的buf[]空间为39字节,在3.2版本时,redis对对sds内存进行了优化,使用sdshdr8代替sdshdr,节省了5个字节。

SDS优点

①记录value len,获取字符串长度时间复杂度为O(1)
②记录剩余空间,杜绝缓冲区溢出
③通过采用空间预分配和惰性空间释放策略,减少修改字符串带来的内存重新分配次数
④二进制安全

2、hash

hash是一个string类型的field和value的映射表,是键值对的集合,类似于一个map(field为键,value为值)。

hash使用场景

对象、键值对数据

hash

hash使用两种方式进行存储:ziplist、hashtable
ziplist:

  • hash对象保存的所有键值对的键和值的字符串长度都小于64字节
  • hash对象保存的键值对数量小于512个
    hash每次写数据时会对数据进行上面两个判断,若不符合这两个条件,hash类型的数据会转为hashtable进行存储。
ziplist
ziplist
  • zlbytes:ziplist的字节长度,占32位,4个字节,所以ziplist最长为(2^32) - 1字节
  • zltail:ziplist尾元素相对于ziplist其实地址的偏移量,占4个字节
  • zllen:ziplist的元素数量,占两个字节,当元素数目超过(2^16) - 1时,无法通过zllen字段获取ziplist的元素数目,必须遍历整个ziplist才能获取元素数目
  • entry:ziplist存储的元素,可以为byte数组或者整数
  • zlend:ziplist的结尾,占一个字节

ziplist中最重要的就是entry,接下来看看entry的结构:

  • prevlength:上一个节点的长度
  • encoding:节点的编码类型和数据长度(通过一串字符来确定data的数据类型)
  • data:节点的值(数字或字符串)

根据前一个节点的长度prevlength,可以将ziplist节点分为两类(节省空间):


entry
  • entry前8位小于254,则这8位就表示前一个节点的长度
  • entry的前8位等于254,则意味着前一个节点的长度无法用8位来表示,后面的32位表示前一个节点的长度

ziplist是一个类似于数组和双向链表的结构,顾名思义,使用ziplist(压缩列表)是为了节省空间。

  • ziplist与数组的区别:数组的数据类型固定,单个节点的长度固定,而ziplist可以存储不同类型的数据,并且单个节点的长度不固定
  • ziplist与双向链表的区别:双向链表的每个节点会存放前后节点的指针,并且节点之间内存不连续,而ziplist是一块连续的内存,节点会通过prevlength和自身length来索引前后的节点,相比于双向链表,ziplist减少了内存碎片和指针内存的占用。虽然在遍历和读取效率上低于链表,但是在空间上有很大的优势。

通过以上的一些分析,我们可以知道ziplist是为了节省空间,牺牲了读取效率,并且不适合做修改操作,如修改单个value或者往中间插入元素,这种操作会使该节点后面的元素全部后移,效率很低。

hashtable

redis的使用的hashtable就是一个很典型的hashtable数据结构,使用数组+链表的方式存储数据,通过field的hash值来确定数组下标,数组的单个元素为链表结构。

hashtable

hash数据类型使用hashtable是在单个元素过大或者元素数量过多时使用,原因就是hashtable虽然没有ziplist节省空间的优势,但是在遍历和读取上的效率是很高的,并且也能较好的支持修改操作,hashtable数据结构比较典型,就不过多赘述了。

3、list

list数据类型底层采用ziplist或linkedlist两种结构进行存储

list适用场景

队列、栈、时间线
ziplist:

  • list保存的元素长度小于64字节
  • list保存的元数量小于512个
    满足这两个条件则使用list进行存储,否则使用linkedlist
linkedlist

ziplist上面已经讲过了,linkedlist数据结构也比较简单。当list采用linkedlist存储时,由于双向链表的特性,list的增删是特别快的,另外list可以从头/尾插入/删除,所以比较适合做栈、队列。

4、set

set是string类型的无序集合,底层使用intset和hashtable来存储,set就像数学中的集合一样,比较方便做并集、交集、差集等操作。

set适用场景

共同关注、共同好友

inset:内部是一个整型数组

  • set对象所保存的元素全是整型
  • set对象所保存的元素小于512个

当set使用hashtable进行存储时,hashtable的key为元素值,value为null,通过key的hash值进行去重。

5、zset(sorted set)

zset底层的存储结构为ziplist或skiplist,zset的特点是每个value有一个score(分值)属性,通过分值进行排序。

zset适用场景

排行榜

ziplist:

  • zset保存的元素数量小于128个
  • zset所有元素的长度小于64个字节

当使用ziplist来作为zset底层的数据结构时,每个集合元素使用两个entry来保存,第一个entry保存元素的value,第二个entry来保存元素的score。
当使用skiplist作为zset的底层存储结构时,使用skiplist按序保存元素value及score,使用dict来保存元素value和score的映射关系

skiplist

skiplist中,每个节点不单单只包含指向下一个节点的指针,可能包含多个指向后续节点的指针,这样在查找的时候就可以跳过一些不必要的节点。虽然单个节点存放多个指向后续节点的指针对于内存的开销增大,但是可以提升查找、删除的效率。

skiplist

例如我们需要找到score为13的value,我们可以直接从头部level3的节点开始查,第一次查找就可以找到,跳过了2、5、9这些节点。

总结

redis常用的五中数据类型其实相对来说比较简单易用,了解其底层存储结构后,我们才能在各个场景选择最适用的数据类型去存储数据。五种数据类型底层都至少包含两种数据结构,一般第一种数据结构适用于数据量少,单个数据小的场景,例如:embstr、ziplist、intset,这些数据结构主要是为了节省内存空间,此时数据结构的查找、删除效率并不高,但是由于数据量少,单个数据小,所以并不会对redis读写效率有较大的影响。一般第二种数据结构适用于数据量多,单个数据大的场景,例如:raw、hashtable、skiplist,这写数据结构的优势在于读写效率高,但是对于内存的占用会更大,适合处理数据量多,单个数据大的场景。

redis版本更新较快,底层数据结构也有些变化,比如最新版的list底层已经使用quicklist来替代ziplist和linkedlist了,但是redis的核心思想是不会变的,数据量少、单个数据小时采用节省空间的策略,数据量多、单个数据大时采用提升读写效率额策略。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352