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