Redis对象(三) - 其它特性

类型检查和多态命令的实现

redis中用于键操作的命令基本上可以分为两类:

  1. 可以对任何类型的键执行, eg. del, expire, rename, type, object

  2. 只能对特定命令执行的键,

    eg. setgetappendstrlen 等命令只能对字符串键执行

    hdelhsethgethlen 等命令只能对hash键执行

    rpushlpoplinsertllen等只能对列表键执行

    saddspopsinterscard等命令只能对集合键执行

    zaddzcardzrankzcore 等命令只能对有序集合键执行

类型检查的实现

类型特定命令所进行的类型检查是通过redisObject结构的type属性 来实现的.

  1. 在执行一个类型特定命令之前, 服务器先检查输入数据库键的值对象是否为执行命令所需要的类型, 是、就执行
  2. 否则, server拒绝执行、并向client返回一个类型错误

eg. 对于llen命令:

在执行llen命令前、server会先检查输入数据库键的值对象是否为列表类型, 即: 检查redisObjecttype属性是否为redis_list, 是的话、执行 llen命令, 否则返回类型错误

多态命令的实现

Redis除了会根据值对象的类型来判断是否能执行特定命令外、还会根据值对象的编码方式、选择正确的命令实现代码来执行命令

eg. 对一个键执行 llen命令, 则服务器除了要确保执行命令的是列表键之外, 还要根据键的值对象所使用的编码来选择正确的llen命令实现

  1. 若列表对象的编码为 ziplist, 那么说明列表对象的实现为压缩列表, 程序将使用 ziplistLen 函数来返回列表的长度
  2. 若列表对象的编码为 linkedlist, 说明列表对象的实现为双端链表, 程序将使用 listLength 函数来返回列表的长度

借用面向对象的术语来说、可以认为llen命令的实现是多态的, 只要执行 llen 命令的是列表键、无论值对象是 ziplist 还是 linkedlist 编码、命令都可以正常执行

delexpire等命令和llen命令的区别在于、前者是基于类型的多态, 一个命令可以同时处理多种不同类型的键、而后者是基于编码的多态: 一个命令可以同时用于处理多种不同的编码

内存回收

因为C语言并不具备内存回收功能, redis 在自己的对象系统中构建了一个引用计数(reference counting) 技术来实现内存回收机制, 通过引用计数机制、程序可以通过跟踪对象的引用计数信息、在适当的时候自动释放对象并进行内存回收

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

typedef struct redisObject {
  // ...
  int refcount; // 引用计数
  // ...
} robj;

对象的引用技术信息会随着对象的使用状态不断变化

  1. 创建一个新的对象时、引用计数初始化为1
  2. 对象被一个新的程序引用时、引用计数值 +1
  3. 对象不再被一个程序引用时、引用计数值 -1
  4. 对象的引用计数值变为0时、对象所占用的内存会被释放

下边是修改对象引用计数的API

函数 作用
incrRefCount 将对象的引用计数值+1
decrRefCount 将对象的引用计数值-1, 当对象的引用计数值=0时、释放对象
resetRefCount 将对象的引用计数值设为0, 但不释放对象、需要重设对象引用值是使用

其它不同类型的对象也会经历类似的过程

共享对象

除了实现引用计数内存回收机制外、对象的引用计数属性还带有对象共享的作用.

eg. A键创建了一个包含整数值100的字符串对象作为值对象, 此时若B键也想要创建一个同样保存了整数值100的字符串对象作为值对象、那么Server有两种做法:

  1. 为键B创建一个包含整数值100的字符串对象
  2. 让键A和键B共享同一个字符串对象

明显, 第二种方式更节约内存, 在Redis中、多个键共享同一个值需要执行以下步骤:

  • 将数据库键的值指向一个现有的值对象
  • 将被共享的值对象的引用计数+1

**注意: **

创建共享字符串对象的数量可以通过修改 redis.h/redis_shared_integers 常量来修改

eg, 创建一个值为100的键a, 使用object refcount 命令查看a的引用计数, 会发现值为2

redis> set a 100
OK
redis> object refcount a
(integer) 2

引用这个值对象的两个程序分表是持有这个值对象的服务器程序, 及共享这个值对象的键A

另外: 这些共享对象不单单只有字符串键可以使用, 那些在数据结构中嵌套了字符串对象的对象(linkedlist编码的列表对象、hashtable编码的hash对象、hashtable编码的集合对象及zset编码的有序集合对象)等都可以使用这些共享对象

思考

为什么redis不共享包含字符串的对象?

当服务器考虑将一个共享对象设置为键的值对象时、程序需要检查给定的共享对象和键想创建的目标对象是否完全相同, 只有在共享对象和目标对象完全相同的情况下、程辉才会将共享对象的用作键的值对象、而一个共享对象保存的值越复杂、验证两者相同的复杂度就会越高, 消耗的CPU时间也会越多

  • 若共享对象保存整数值的字符串对象、那么验证操作的复杂度为 O(1)
  • 若共享对象是保存字符串值的字符串对象、那么验证操作的复杂度为 O(N)
  • 若共享对象是包含了多个值(或者对象)的对象, 比如列表对象或者hash对象、验证的复杂度将是O(N²)

因此、尽管共享更复杂的对象可以节约更多内存、但受到CPU时间的限制、redis只对包含整数值的字符串对象进行共享

对象的空转时长

除了前边介绍过的typeencodingptrrefcount 4个属性外, redisObject结构包含的最后一个属性为 lru属性, 它记录了对象最后一次被命令访问的时间

typedef struct redisObject {
  // ...
  unsigned lru:22;
  // ...
} robj;

object idletime 命令可以打印出给定键的空转时长, 就是通过当前时间 - 键的lru时间得到的

注意:

Object idletime的实现是特殊的, 它在访问键的时候、不会修改值对象的lru属性

除了使用 命令打印键的空转时长, lru属性还用于回收内存, 当设置了 maxmemory 选项, 且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru 时、当服务器占用内存超过了 maxmemory设置的上限值时, 空转时长较高的键会优先被服务器释放.

重点回顾

  1. redis数据库的中每个键值对的键和值都是一个对象
  2. redis共有字符串、列表、hash、结合、有序集合五种类型的对象, 每种类型的对象至少有2种或以上的编码方式, 不同的编码可以在不同的场景上优化对象的使用概率
  3. 服务器在执行某些命令之前、会先检查给定键的类型能否执行
  4. redis的对象系统带有引用计数实现的内存回收机制, 当一个对象不再被使用时、该对象占用的内存会被自动释放
  5. redis会共享值为 0 到 9999 的字符串对象
  6. 对象会记录自己最后一次被访问的时间, 这个时间还可以用于计算对象的空转时长
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容

  • 对象 当称呼一个数据库键为"字符串键"、"列表键"时,指的是这个键对应的值为"字符串对象"、"列表对象"。 Red...
    xMustang阅读 263评论 0 0
  • Redis的内存优化 声明:本文内容来自《Redis开发与运维》一书第八章,如转载请声明。 Redis所有的数据都...
    meng_philip123阅读 18,881评论 2 29
  • Redis 是一个键值对数据库(key-value DB),数据库的值可以是字符串、集合、列表等多种类型的对象,而...
    吴昂_ff2d阅读 3,131评论 0 5
  • 对象 redis没有直接使用SDS、链表、字典、压缩列表、整数集合等数据结构来实现 键值对数据库,而是基于这些数...
    稻壳_be03阅读 508评论 0 0
  • 使用对象的好处:在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令。2.可以针对不同的使用场景,为...
    黑金星阅读 250评论 0 1