Redis数据对象及底层编码实现

最近在研究redis,稍微看了一下huanggz的《redis设计与实现》及源码中的相关实现,感觉作者对于redis确实有一定程序的了解,把问题说明很很清楚。本文算是对下午的学习的一个记录。

作为内存数据库,由于其采用的c语言没有内置相关的数据结构,redis自己实现了几个底层的数据结构,具体包括如下:
1. 简单动态字符串
Redis 没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组,以下简称 C 字符串), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将 SDS 用作 Redis 的默认字符串表示。
这里写图片描述

这里写图片描述
2. 双端链表
链表提供了高效的节点重排能力, 以及顺序性的节点访问方式, 并且可以通过增删节点来灵活地调整链表的长度。作为一种常用数据结构, 链表内置在很多高级的编程语言里面, 因为 Redis 使用的 C 语言并没有内置这种数据结构, 所以 Redis 构建了自己的链表实现。
这里写图片描述

这里写图片描述
3. 字典
字典, 又称符号表(symbol table)、关联数组(associative array)或者映射(map), 是一种用于保存键值对(key-value pair)的抽象数据结构。
这里写图片描述
4. 跳跃表
跳跃表(skiplist)是一种有序数据结构, 它通过在每个节点中维持多个指向其他节点的指针, 从而达到快速访问节点的目的。
跳跃表支持平均 O(\log N) 最坏 O(N) 复杂度的节点查找, 还可以通过顺序性操作来批量处理节点。
在大部分情况下, 跳跃表的效率可以和平衡树相媲美, 并且因为跳跃表的实现比平衡树要来得更为简单, 所以有不少程序都使用跳跃表来代替平衡树。
Redis 使用跳跃表作为有序集合键的底层实现之一: 如果一个有序集合包含的元素数量比较多, 又或者有序集合中元素的成员(member)是比较长的字符串时, Redis 就会使用跳跃表来作为有序集合键的底层实现。
这里写图片描述
5. 整数集合
整数集合(intset)是集合键的底层实现之一: 当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现。
这里写图片描述

这里写图片描述
6. 压缩列表
压缩列表(ziplist)是列表键和哈希键的底层实现之一。

当一个列表键只包含少量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做列表键的底层实现。
这里写图片描述

这里写图片描述

这里写图片描述

这几个数据结构是redis的类型基础,也是理解redis的命令的重要知识。尽管redis拥有了这么多的adt,但是redis并没有直接使用这些数据结构,而是构建了一个对象系统,核心就是redisObject。
个人觉得作者是采用了面向对象的概念,用c语言实现多态,具体来说就是在redisObject这个类里面设置了type与ptr这两个域,其中type指定了对象的类型,而ptr则指向具体的数据。每当我们在redis数据库新建一个键值对时,我们至少会创建两个对象:
1、键对象:键值对的键,字符串类型
2、值对象:键值对的值,其类型根据需要确定。
redis总结包括5种对象,每个对象都由一个redisObject结构表示。如下:

typedef struct redisObject {
    // 类型
    unsigned type:4;
    // 编码
    unsigned encoding:4;
    // 指向底层实现数据结构的指针
    void *ptr;
    // ...
} robj;

感觉兴趣的读者可以自己去看一下这本书及源代码。下面简单记录一下5对象可能采用的数据结构及一些有用的知识。
1、字符串对象
字符串对象的编码可以是int、embstr及raw。
2、列表对象
列表对象的编码可以是ziplist 或者linkedlist。
3、哈希对象
哈希对象的编码可以是ziplist或者hashtable。
4、集合对象
集合对象的编码可以是intset或者hashtable。
5、有序集合对象
有序集合对象的编码可以是ziplist或者skiplist。

重要知识:
1、redis数据库中的每个键值对的“键”与“值”都是一个redisObject对象;
2、redis共有字符串、列表、哈希、集合及有序集合5种类型的对象,每种类型的对象都有至少两种或者以上的编码方式,不同的编码可以在不同的场景上使用,以优化对象的使用效率,整体上的原则是当数据量比较少时,采用一种编码方式,当数据量比较大时,采用另一种编码方式,后者可以更消耗内存(这点可以通过配置文件实现,但不是全部的条件都可以配置,此外,具体的默认配置在不同版本也不一样)。从这种上也可以看出来redis作者声明的关于redis是追求性能这一点的;
3、服务器在执行某些命令之前,会先检查给定键的类型能否执行指定的命令,而检查一个键的类型就是检查键的值的类型;
4、redis的对象系统带有引用计数实现的内存回收机制,当一个对象不再被使用时,该对象所占用的内存就会被自动释放;
5、redis会共享值为0到9999的字符串对象,本质上是共享值为整数类型的字符串对象;
6、对象会记录自己的最后一次被访问的时间,这个时间可以用于计算对象的空转时间,当需要清理内存空间时可以提供依据。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值