8.对象

8.1对象的类型与编码

Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、encoding属性和ptr属性:

redisObject结构

8.1.1 类型

对于Redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是字符串对象、列表对象、哈希对象、集合对象和有序集合对象其中的一种。

TYPE命令返回的结果为数据库键对应的值对象的类型,而不是键对象的类型。

8.1.2 编码和底层实现

对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。

encoding属性记录了对象所使用的编码。每种类型的对象都至少使用了两种不同的编码。通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率。

8.2 字符串对象

字符串对象的编码可以int、raw或者embstr.

如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面,并将字符串对象的编码设置为int

如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串的值,并将对象的编码设置为raw

如果字符串对象保存的 是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。

embstr编码是专门用于保存段字符串的一种优化编码方式,它和raw编码一样,都使用redisObject结构和sdshdr结构来表示字符串对象,raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构。

embstr编码的字符串对象来保存短字符串值有以下好处:

1.embstr编码将创建字符串对象所需内存的次数从raw编码的两次降低为1次。

2.释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用2次内存释放函数

3.embstr编码的字符串对象的所有数据都保存在一块连续的内存中,能更好地利用缓存带来的优势。


8.2.1 编码的转换

int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转化为raw编码的字符串对象。

embstr编码的字符串对象实际上是只读的。当我们对embstr编码的字符串对象指向任何修改命令时,程序会先将对象的编码从embstr转换成raw,然后再执行修改命令。

8.2.2 字符串命令的实现


8.3 列表对象

列表对象的编码可以是ziplist或者linkedlist

ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节(entry)点保存了一个列表元素。

linkedlist编码的列表对象使用双端链表作为底层实现,每个双端链表节点(node)都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。

8.3.1 编码转换

当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:

1.列表对象保存的所有字符串元素的长度都小于64字节

2.列表对象保存的元素数量小于512个

不能满足这两个条件的列表对象需要使用linkedlist编码

8.3.2 列表命令的实现


8.4 哈希对象

哈希对象的编码可以是ziplist或者hashtable

ziplist编码的哈希对象底层是压缩列表实现的。保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,值的节点在后

先添加到哈希对象中的键值对会被放在压缩列表的表头方向,后被添加的键值对会被放到表尾方向

hashtable编码的哈希对象使用字典作为底层实现

8.4.1 编码转换

当哈希表同时满足以下两个条件时,哈希对象使用ziplist编码:

1.哈希对象保存的所有键值对的键和值的字符串长度都小于64

2.哈希对象保存的键值对小于512个

不满足这两个条件的哈希对象需要使用hashtable编码


8.5 集合对象

集合对象的编码可以是intset或者hashtable

intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有对象都被保存在整数集合里面

8.5.1 编码的转换

当集合对象同时满足以下两个条件时,集合对象使用intset编码:

1.集合对象保存的所有元素都是整数值

2.集合对象保存的键值对小于512个

不满足这两个条件的对象集合需要使用hashtable进行编码

8.5.2 集合命令的实现

因为集合键的值为集合对象,所以用于集合键的所有命令都是针对集合对象来构建的


集合命令的实现方法

8.6 有序集合对象

有序集合对象的编码可以是ziplist或者skiplist

ziplist编码的压缩对象使用压缩列表作为底层实现,每个集合元素使用两个挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值。压缩列表內的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素被放置在靠近表尾的方向。

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表:


zset结构

zset结构同时使用跳跃表和字典来保存有序集合元素,但这两种数据结构都会通过指针来共享相同元素的成员和分值,所以同时使用跳跃表和字典来保存集合元素不会产生任何重复成员或分值

8.6.1 编码的转换

当有序集合对象可以同时满足以下两个条件时,对象使用ziplist编码:

1.有序集合保存的元素数量小于128个

2.有序集合保存的所有元素成员的程度都小于64字节

不满足以上两个条件就使用skiplist

8.6.2 有序集合命令的实现


8.7 类型检查与命令多态

Redis中用于操作键的命令基本可以分为2类:

1.可以对任何类型的键执行,比如DEL命令,EXPIRE命令,RENAME命令,TYPE命令,OBJECT命令

2.只能对特定类型的键执行,比如:

SET、GET APPEND STRLEN命令只能对字符串键执行

HDEL HSET HGET HLEN等命令只能对哈希键执行

RPUSH LPOP LINSERT LLEN只能对列表键执行

SADD SPOP SINTER SCARD只能对集合键执行

ZADD ZCARD ZRANK ZSCORE只能对有序集合键执行

8.7.1 类型检查的实现

类型检查时通过redisObject中的type属性来实现的:

1.在执行一个类型特定命令之前,服务器会先检查输入数据库键的值对象是否为执行命令所需的类型,如果是的话,服务器就对键执行指定的命令

2.否则,服务器将拒绝执行命令,并向客户端返回一个类型错误

8.7.2 多态命令的实现

可以将DEL EXPIRE TYPE等命令也称为多态,是基于类型的多态——一个命令可以同时处理多钟不同类型的键

LLEN命令多态是基于编码的多态——一个命令可以同时用于多种不同编码

8.8 内存回收

Redis构建了一个引用计数技术实现内存回收机制,通过这一机制,程序可以跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收

每个对象的引用计数信息由redisObject结构的refcount属性记录


对象的引用计数信息会随着对象的使用状态而不断变化:

在创建一个新对象时,引用计数的值会被初始化为1

当对象被一个新程序使用时,它的引用计数值会被增一

当对象不再被一个程序使用时,他的引用计数值会被减一

当对象的引用计数值变为0时,对象所占用的内存会被释放

8.9 对象共享

对象的引用计数属性海带有对象共享的作用

在Redis中,让多个键共享同一个值对象需要执行以下两个步骤:

1.将数据库键的值指针指向一个现有的值对象

2.将被共享的值对象的引用计数增一

共享机制对节约内存非常有帮助,数据库中保存的相同值对象越多,对象共享机制就能节约越多内存

目前来说,redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0-9999的字符串对象时,服务器就会使用这些共享对象,而不是创建新对象

8.10 对象的空转时长

redisObject结构包含的最后一个属性为lru属性,该属性记录了对象最后一次被命令程序访问的时间


redisObject结构的lru属性

OBJECT IDLETIME命令可以打印出给定键的空转时长,这一空转时长就是通过将当前时间减去键的值对象的lru时间计算得出的。

键的空转时长还有一项作用:如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存。

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

推荐阅读更多精彩内容

  • Redis的内存优化 声明:本文内容来自《Redis开发与运维》一书第八章,如转载请声明。 Redis所有的数据都...
    meng_philip123阅读 18,874评论 2 29
  • 参考来源 Redis的内存优化 Redis所有的数据都在内存中,而内存又是非常宝贵的资源。对于如何优化内存使用一直...
    秦汉邮侠阅读 1,280评论 0 2
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,089评论 1 32
  • 关于共享知识的争论早已有了许多。人们关心不关心、看好不看好最近的「知乎 live」「分答」,总要揪扯到「共享经济」...
    小田一成阅读 348评论 0 0
  • 还有6天就要中考了,感赏孩子信心满满、轻松偷快的状态,感赏孩子面对脚感染蜂窝织炎痛苦时的坚强与治疗时的坚持,感赏孩...
    5652d0b3acbe阅读 192评论 0 0