玩转云端丨redis的5种对象与8种数据结构之字符串对象(上)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 引言 本文是对《redis设计与实现(第二版)》中数据结构与对象相关内容的整理与说明。本篇文章只对对象结构,1种对象——字符串对象。以及字符串对象所对应的两种编码——raw和embstr,进行了详细介绍。

引言

本文是对《redis设计与实现(第二版)》中数据结构与对象相关内容的整理与说明。本篇文章只对对象结构,1种对象——字符串对象。以及字符串对象所对应的两种编码——raw和embstr,进行了详细介绍。表达一些本人的想法与看法,也希望更多朋友一起来讨论,分享交流。

IMG_20190724_194834__B_64d11cd

作者:太阳

云掣科技-数据库团队

数据库工程师

对象

redis使用对象来表示数据库中的键和值,每次当我们在redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)。

redis的每种对象都由对象结构(redisObject)与对应编码的数据结构组合而成,redis支持5种对象类型,分别是字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(zset),而每种对象类型至少对应两种编码方式,不同的编码方式所对应的底层数据结构是不同的。

每个对象会用到的编码以及对应的数据结构详见下表:

image

每种对象对应两至三种编码,除skiplist编码需要用到两种数据结构(字典+跳跃表)外,其余编码均用到一种底层的数据结构。

同一个对象类型,在不同的场景下用到的编码(数据结构)不同,redis支持8种编码以及8种底层的数据结构。这种方式更加灵活,可以帮助redis获得更高的性能以及尽量占用更少的内存。比如如果字符串对象中要存储的字符串内容所占字节较小,会用embstr编码的格式,如果要存储的内容所占字节较大,会用raw编码的格式,具体细节后文会详细说明。

总结说明

上文说过,redis中的键和值都是由对象组成的,而对象是由对象结构和数据结构共同组成的。redis中的键,都是用字符串来存储的,即对于redis数据库中的键值对来说,键总是一个字符串对象,而值可以是字符串对象、列表对象、哈希对象、集合对象或者有序集合对象中的其中一种。

键、值的整体大致结构可以如下图所示:

image

对象结构

对象结构(redisObject)共有5个属性,分别是type属性、encoding属性、ptr属性、refcount属性、lru属性。

其中type属性、encoding属性、ptr属性和保存数据有关:

type属性:表示该对象的类型是什么;
encoding属性:表示这个对象使用的底层数据结构是什么;
ptr属性:是一个指向底层数据结构的指针;

refcount属性是一个引用计数属性,可以用于内存回收和对象共享;
lru属性,记录了对象最后一次被命令程序访问的时间,可以计算出某个键的空转时长。

对象结构的逻辑图如下所示:

image

内存回收--refcount属性

在对象结构中,有refcount这个属性,该属性用于记录对象的引用计数信息,redis利用引用计数(referencecounting)技术实现内存回收机制,通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。

具体策略:
在创建一个新对象时,引用计数的值会被初始化为1;
当对象被一个新程序使用时,它的引用计数值会被+1;
当对象不再被一个程序使用时,它的引用计数值会被-1;
当对象的引用计数值变为0时,对象所占用的内存会被释放。

对象共享--refcount属性

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

对象结构中,refcount是引用指针属性,如果有N个键共享一个值,refcount对应的值就为N。创建共享字符串对象的数量可以通过redis.h/redis_shared_intengers常量来修改。object refcount命令可以查看某个键对应的值被引用了多少次。

让多个键共享一个值,需要执行以下两个步骤:

将键的值指针,指向被共享的值对象;

被共享的值对象的引用计数器加一,即refcount属性的值加一。

引用数为2的共享对象结构图如下图所示:

image

总结说明

当服务器考虑将一个键的值引用共享对象时,键的值作为目标对象,程序需要先检查共享对象和目标对象的类型是否完全相同,只有在完全相同的情况下,共享对象才会被引用。而一个共享对象保存的值越复杂,验证共享对象与目标对象所需的复杂度就会越高,消耗的CPU时间也会越多。

所以共享对象的优点是被其它键引用时,可以节省内存空间,缺点是被引用时需要进行判断,这个过程需要消耗CPU,如果共享对象简单,消耗很小的CPU并节省内存空间是值得的。

但如果对象共享很复杂,进行判断就需要消耗大量CPU,消耗大量CPU去节省内存空间是不值得的,因为redis本身的内存空间还是很大的。

知识点

redis支持5种对象,包括字符串对象、列表对象、哈希对象、集合对象以及有序集合对象。而字符串对象是redis中的一个基础对象,其它对象均可以在底层的数据结构内部嵌套字符串对象。

对于对象共享:
1、只有字符串对象才能被创建为共享对象,被其它字符串键使用;
2、用字符串对象创建的共享对象,不单单只有字符串键可以使用,那些在数据结构中嵌套了字符串对象的对象(linkedlist编码的列表对象、hashtable编码的哈希对象、hashtable编码的集合对象,以及skiplist编码的有序集合对象)都可以使用这些字符串共享对象。

Q&A

Q

为什么redis不共享列表对象、哈希对象、集合对象、有序集合对象,只共享字符串对象?
A

列表对象、哈希对象、集合对象、有序集合对象,本身可以包含字符串对象,复杂度较高。

如果共享对象是保存字符串对象,那么验证操作的复杂度为O(1);
如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为O(N);
如果共享对象是包含多个值的对象,其中值本身又是字符串对象,即其它对象中嵌套了字符串对象,比如列表对象、哈希对象,那么验证操作的复杂度将会是O(N的平方);
如果对复杂度较高的对象创建共享对象,需要消耗很大的CPU,用这种消耗去换取内存空间,是不合适的。

碎碎念

1、现在我们知道,redis为了避免额外的内存消耗,在初始化的时候,为0~9999这些整数创建了共享对象。那除了0~9999,redis内部是否还设置了其它类型的共享对象?但具体有哪些值被作为了共享对象还不是特别清楚,不过应该都是一些简单的值。

2、另外,0~9999整数是程序初始化时自动创建为共享对象的,我们是否可以手动创建共享对象?比如我们认为有很多键对应的值都是相同的,是否可以手动创建共享对象以节省内存?如果可以,又有哪些限制要求?

创建的共享对象,当其它键去引用共享对象时,需要进行判断,两者的类型完全相同才可以被应用,共享对象保存的内容越复杂,进行判断时需要消耗的CPU就越大。

redis初始化创建的0~9999的共享对象,结构很简单,进行判断时消耗的CPU很小。但是如果redis允许我们手动为某些值创建共享对象,它的结构只要稍微复杂一些,就需要消耗很大的CPU,这无疑是不合适的,所以redis为了避免这种不必要的影响,应该不支持手动创建共享对象。

欢迎各位共同参与讨论

一起交流沟通~

image.png

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
15天前
|
XML JSON NoSQL
Redis的常用数据结构之字符串类型
Redis的常用数据结构之字符串类型
19 0
|
22天前
|
存储 消息中间件 NoSQL
Redis数据类型详解:选择合适的数据结构优化你的应用
Redis数据类型详解:选择合适的数据结构优化你的应用
|
27天前
|
存储 NoSQL 算法
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)(二)
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)
43 0
|
9天前
|
Python
python学习-函数模块,数据结构,字符串和列表(下)
python学习-函数模块,数据结构,字符串和列表
49 0
|
22天前
|
存储 消息中间件 缓存
Redis 字符串:用一串数据解决多种问题
Redis 字符串:用一串数据解决多种问题
|
27天前
|
存储 NoSQL Redis
作者推荐 |【Redis技术进阶之路】「原理系列开篇」揭秘高效存储模型与数据结构底层实现(SDS)(三)
作者推荐 |【Redis技术进阶之路】「原理系列开篇」揭秘高效存储模型与数据结构底层实现(SDS)
28 0
|
存储 NoSQL 算法
Redis之小对象压缩
Redis之小对象压缩
835 1
|
15天前
|
NoSQL Linux Redis
06- 你们使用Redis是单点还是集群 ? 哪种集群 ?
**Redis配置:** 使用哨兵集群,结构为1主2从,加上3个哨兵节点,总计分布在3台Linux服务器上,提供高可用性。
221 0
|
24天前
|
负载均衡 监控 NoSQL
Redis的集群方案有哪些?
Redis集群包括主从复制(基础,手动故障恢复)、哨兵模式(自动高可用)和Redis Cluster(官方分布式解决方案,自动分片和容错)。此外,还有如Codis、Redisson和Twemproxy等第三方工具用于代理和负载均衡。选择方案需考虑应用场景、数据规模和并发需求。
183 2
|
29天前
|
NoSQL Redis
Redis集群(六):集群常用命令及说明
Redis集群(六):集群常用命令及说明
177 0