Redis设计与实现:数据结构与对象

导读:本篇文章讲解 Redis设计与实现:数据结构与对象,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Redis数据库里面的每个键值对(key-value pair)都是由对象(object)组成的。其中:

  • 键:是一个字符串对象
  • 值:是字符串对象、列表对象等几种对象中的一种

为了更好地理解与使用Redis,需要了解对象所使用的底层数据结构。

这是Redis github项目:Redis

一,简单动态字符串SDS

Redis底层使用ANSI C语言编写,但没有直接使用C语言传统的字符串, 而是自己构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型。

C字符串只会用在一些无须对字符串值进行修改的地方,比如打印日志和提示:
在这里插入图片描述
当要使用一个可以被修改的字符串值时, Redis就会使用SDS来表示该字符串值:

127.0.0.1:6379> SET "Extreme Pizza" "300 Broadway, New York, NY"
OK
  • 健是一个字符串对象, 对象的底层实现是一个保存着字符串”Extreme Pizza”的SDS
  • 值也是一个字符串对象, 对象的底层实现是一个保存着字符串”300 Broadway, New York, NY”的SDS

SDS还被用作AOF模块中的AOF缓冲区以及客户端状态中的输入缓冲区。

源码级解析可以参考这篇文章:Simple Dynamic Strings

二,链表

链表在Redis中的应用非常广泛, 比如列表键的底层实现之一就是链表。 当一个列表键包含了数量比较多的元素, 又或者列表中包含的元素都是比较长的字符串时, Redis就会使用双向链表作为列表键的底层实现。

除了链表键之外, 发布与订阅、 慢查询、 监视器等功能也用到了链表, Redis服务器本身还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区。

127.0.0.1:6379> LLEN favorite_restaurants
(integer) 6
127.0.0.1:6379> LRANGE favorite_restaurants 0 -1
1) "Olive Garden"
2) "PF Chang's"
3) "Indian Tandoor"
4) "Longhorn Steakhouse"
5) "Outback Steakhouse"
6) "Red Lobster"
  • favorite_restaurants列表键的底层实现就是一个链表, 链表中的每个节点都保存了一个字符串值。
  • 底层实现中,通过为链表设置不同的类型特定函数, Redis的链表可以用于保存各种不同类型的值。

源码级解析可以参考这篇文章:双向链表(adlist)

三,字典

字典在Redis中的应用相当广泛, 比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增、 删、 查、 改操作也是构建在对字典的操作之上的。

除了用来表示数据库之外, 字典还是哈希键的底层实现之一。

Redis的字典使用哈希表作为底层实现, 一个哈希表里面可以有多个哈希表节点, 而每个哈希表节点就保存了字典中的一个键值对。

127.0.0.1:6379> HGETALL "Kyoto Ramen"
1) "rating"
2) "4.9"
3) "status"
4) "open"
5) "phone"
6) "555-555-0001"
  • “Kyoto Ramen”就由哈希表实现
  • 该表里包含3个哈希表节点,其中一个节点就保存了{“rating”: “4.9”}

源码级解析可以参考这篇文章:字典的实现

四,跳跃表

跳跃表(skiplist)是一种有序数据结构, 它通过在每个节点中维持多个指向其他节点的指针, 从而达到快速访问节点的目的。

Redis使用跳跃表作为有序集合键的底层实现之一。

Redis只在两个地方用到了跳跃表, 一个是实现有序集合键, 另一个是在集群节点中用作内部数据结构。

127.0.0.1:6379> ZREVRANGE ranking:restaurants 0 -1 WITHSCORES
 1) "Olive Garden"
 2) "100"
 3) "Longhorn Steakhouse"
 4) "88"
 5) "Red Lobster"
 6) "46"
 7) "Outback Steakhouse"
 8) "34"
 9) "PF Changs"
10) "23"
  • ranking:restaurants有序集合的所有数据都保存在一个跳跃表里面
  • 每个跳跃表节点(node)都保存了一家餐厅的评分信息,依据节点中double类型的浮点数的分值(score属性),所有评分信息有序排序。如果分值相同,则按字典顺序排序

源码级解析可以参考这篇文章:跳跃表的实现

五,整数集合

整数集合(intset)是集合键的底层实现之一,是用于保存整数值的集合抽象数据结构。

底层实现为contents 数组: 整数集合的每个元素都是contents数组的一个数组项(item), 各个项在数组中按值的大小从小到大有序地排列, 并且数组中不包含任何重复项。

127.0.0.1:6379> SADD numbers 1 3 5 7 9
(integer) 5
127.0.0.1:6379> OBJECT ENCODING numbers
"intset"

源码级解析可以参考这篇文章:Redis源码解析——有序整数集

六,压缩列表

压缩列表(Ziplist )是列表键和哈希键的底层实现之一。压缩列表是Redis为了节约内存而开发的, 是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。 一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

127.0.0.1:6379> RPUSH 1st 1 3 5 10086 "hello" "world"
(integer) 6
127.0.0.1:6379> LRANGE 1st  0 -1
1) "1"
2) "3"
3) "5"
4) "10086"
5) "hello"
6) "world"
127.0.0.1:6379>  OBJECT ENCODING 1st
"quicklist"
  • quicklist就是一个由ziplist组成的双向链表。

源码级解析可以参考这两篇文章:压缩列表(ziplist)快速列表(quicklist)

七,对象

Redis并没有直接使用这些数据结构来实现键值对数据库, 而是基于这些数据结构创建了一个对象系统, 这个系统包含字符串对象、 列表对象、 哈希对象、 集合对象和有序集合对象这五种类型的对象, 每种对象都用到了至少一种我们前面所介绍的数据结构。

Redis可以在执行命令之前, 根据对象的类型来判断一个对象是否可以执行给定的命令。
我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现, 从而优化对象在不同场景下的使用效率。

Redis的对象系统还实现了基于引用计数技术的内存回收机制,当程序不再
使用某个对象的时候,这个对象所占用的内存就会被自动释放;另外,Redis还通过引用计数技术实现了对象共享机制, 这一机制可以在适当的条件下, 通过让多个数据库键共享同一个对象来节约内存。

源码级解析可以参考这篇文章:对象系统概述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/98126.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!