Redis第8章 对象

  • 前面几章介绍的都是Redis基于C优化的基础数据结构,这一章具体介绍五种对象类型的底层结构实现,并且在特定情况下结构会发生转化。

8.1 对象的类型与编码

  • Redis每创建一次键值对时至少要生成两个对象,键对象和值对象,每个对象都是用redisObject结构表示。
typedef struct redisObject {
  //类型
  unsigned type:4;
  //编码
  unsigned encoding:4;
  //指向底层实现数据结构的指针
  void *ptr;
  //...
} redisObject;
8.1.1 类型(TYPE)
  • 键的类型:键总是一个字符串。
  • 值的类型:值类型也就是最常见的5中基本类型,分别是字符串(REDIS_STRING)、列表(REDIS_LIST)、哈希(REDIS_HASH)、集合(REDIS_SET)、有序集合(REDIS_ZSET)。
  • TYPE命令:TYPE + key可以查看这个对象的值类型。
8.1.2 编码和底层实现(encoding或缩写ENC)
  • 编码一共有8种,对应的数据结构分别是:
    • REDIS_ENCODING_INT:long类型整数
    • REDIS_ENCODING_EMBSTR:embstr编码的简单动态字符串
    • REDIS_ENCODING_RAW:简单动态字符串
    • REDIS_ENCODING_HT:字典
    • REDIS_ENCODING_LINKEDLIST:双端链表
    • REDIS_ENCODING_ZIPLIST:压缩列表
    • REDIS_ENCODING_INTSET:整数集合
    • REDIS_ENCODING_SKIPLIST:跳跃表和字典
  • 每种基本类型至少使用了两种不同的编码,不同场景下有不同的解决方案,例如Redis会把数字和字符都算做基本类型里的REDIS_STRING,但底层存储时又会有不同的编码,从而提升效率,下面列举所有基本类型可能会使用到的底层编码。


    基本类型和编码的对应关系.png
  • OBJECT ENCODING命令:OBJECT ENCODING + key可以查看这个对象值的编码。

8.2 字符串对象

  • 字符串编码可以是REDIS_ENCODING_INT、REDIS_ENCODING_RAW或者REDIS_ENCODING_EMBSTR(这章后续用缩写int、raw、embstr缩写表示)。
  • 编码判定条件:
    • 浮点或整形数字不超过32位,用int
    • 超长数字、字符串并且小于等于39字节,用raw
    • 超长数字、字符串并且大于39字节,用embstr
  • raw和embstr的共同点和区别:
    • raw和embstr都是使用SDS结构保存数据,但embstr对SDS做了一些优化。
    • raw调用两次内存分配,分别创建了redisObject和sdshdr两个空间,embstr只调用一次,并且内存空间连续。
    • 释放时raw也是调用两次释放,embstr调用一次。
    • embstr本身就是短字符串时使用,内存连续所以效率更高。
    • embstr是只读的,创建后如果需要修改,直接会转化成raw。
  • 保存浮点类型时存的是embstr结构,执行一些数字操作时再转成浮点型。
  • 浮点型和长整型在数据过长的情况下也会扩展成embstr甚至raw


    embstr.png

    raw.png
8.2.1 编码的转换
  • 之前提到了在不同条件下string的底层数据结构实现不同,int和embstr会升级成raw,最常见的场景就是APPEND命令。
  • 例如初始化一个数字底层是int,拼接一个字符串后就变成了raw。
  • embstr同理,但embstr创建后是只读的,任何的修改操作都会直接升级成raw,int和embstr之间不会互相升级。
8.2.1 字符串命令的实现
字符串指令实现.png

8.3 列表

  • 列表对象编码可以是ziplist或linkedlist。
  • ziplist和linkedlist的区别:
    • ziplist地址连续,而且很少浪费空间,大概就能猜到肯定是节点数少时候会用到ziplist
    • linkedlist是节点间引用连接,每个节点又是一个独立的字符串对象,是上一个小结介绍的字符串对象,字符串对象是唯一被其他对象嵌套的对象。


      linkedlist.png

      ziplist.png
8.3.1 编码的转换
  • 所有元素长度小于64字节,并且元素数量小于512时用ziplist,否则使用linkedlist
8.3.2 列表指令的实现
list指令实现.png

8.4 哈希对象

  • 哈希对象编码可以是ziplist或hashtable。
  • ziplist:使用ziplist时因为内存连续,每次添加节点时都会按key、value的顺序添加到列表尾部。


    ziplist.png
  • hashtable:使用字典实现,字典结构的Key和value都各自是一个字符串对象。


    hashtable.png
8.4.1 编码转换
  • 所有键和值都小于64字节,并且节点数量小于512个时使用ziplist,否则使用hashtable。
8.4.1 哈希命令的实现
hash命令.png

8.5 对象集合

  • 对象集合编码可以是intset或hashtable。
  • intset:


    intset.png
  • hashtable:对象集合的hashtable所有的value都是Null


    hashtable.png
8.5.1 编码的转换
  • 集合都是整数并且元素小于512时,是intset否则用hashtable
8.5.2 对象集合命令的实现
API1.png

API2.png

8.6 有序集合对象

  • 有序集合对象编码可以是ziplist或skiplist。
  • ziplist:


    ziplist.png
  • skiplist:当zset使用skiplist时,会同时包含一个字典和一个跳跃表。字典的key是数据,value是分值,跳跃表的object是数据,score是分值。两种数据结构通过指针共享数据和分值。
  • 为什么同时使用跳跃表和字典:还是性能问题。
    • 字典可以最快的找到对应数据,但zset还有一些范围操作命令,所以引入了跳跃表,只有跳跃表数据查找效率又会低,所以最后的解决方案就是同时引入跳跃表和字典。


      skiplist.png
8.6.1 编码的转换
  • 数量小于128并且元素数量小于64字节用ziplist否则用skiplist。
8.6.2 有序集合命令的实现
有序集合API.png

8.7 类型检查与命令多态

  • Redis操作类型分为两种:
    • 可以对任何类型的键执行:DEL、EXPIRE、RENAME、TYPE等等
    • 对特定类型使用:set、get是字符串,HSET、HGET是哈希等等
8.7.1 类型检查的实现
  • 在执行特定类型的命令前,Redis会先检查redisObject结构的type属性是否符合要求,否则报错。
8.7.2 多态命令的实现
  • 命令根据不同的底层数据结构实现,执行不同的操作。

8.8 内存回收

  • Redis的回收使用引用计数的方式实现。
    • 创建时计数+1
    • 对象被新程序使用时+1
    • 对象不被程序使用时-1
    • 对象计数为0时释放内存

8.9 对象共享

  • 对象共享和内存回收相关,共享的对象有新引用时计数+1。
  • Redis初始化服务器时会创建0-9999的字符串共享变量供程序使用。
  • Redis只对包含整数的字符串对象进行共享。

8.10 对象的空转时长

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

推荐阅读更多精彩内容

  • 1.Redis特性 1)速度快:数据存放在内存上、基于C语言实现、单线程架构预防多线程竞争问题;2)基于键值对的数...
    Sponge1128阅读 618评论 0 1
  • Redis主要支持的数据类型有5种:String ,Hash ,List ,Set ,和 Sorted Set。 ...
    爱情小傻蛋阅读 1,411评论 0 0
  • Redis用到的主要数据结构,如简单动态字符串、双端链表、字典、压缩列表、整数集合等。Redis并没有直接使用这些...
    HRADPX阅读 350评论 0 0
  • Redis的内存优化 声明:本文内容来自《Redis开发与运维》一书第八章,如转载请声明。 Redis所有的数据都...
    meng_philip123阅读 18,887评论 2 29
  • 住院记 7月中,I有点咳嗽,第三天我感觉有点严重了,于是带她去常去的那家药店买药。医师简单看了开了肺宁颗粒服用了一...
    Sindy2019阅读 146评论 0 0