百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

redis的5种对象与8种数据结构之字符串对象(上)

mhr18 2024-10-31 13:23 34 浏览 0 评论

对象

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

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

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

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

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

总结说明

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

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

对象结构

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

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

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

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

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

内存回收--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的共享对象结构图如下图所示:

总结说明

当服务器考虑将一个键的值引用共享对象时,键的值作为目标对象,程序需要先检查共享对象和目标对象的类型是否完全相同,只有在完全相同的情况下,共享对象才会被引用。而一个共享对象保存的值越复杂,验证共享对象与目标对象所需的复杂度就会越高,消耗的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为了避免这种不必要的影响,应该不支持手动创建共享对象。

相关推荐

Java培训机构,你选对了吗?(java培训机构官网)

如今IT行业发展迅速,不仅是大学生,甚至有些在职的员工都想学习java开发,需求量的扩大,薪资必定增长,这也是更多人选择java开发的主要原因。不过对于没有基础的学员来说,java技术不是一两天就能...

产品经理MacBook软件清单-20个实用软件

三年前开始使用MacBookPro,从此再也不想用Windows电脑了,作为生产工具,MacBook可以说是非常胜任。作为产品经理,值得拥有一台MacBook。MacBook是工作平台,要发挥更大作...

RAD Studio(Delphi) 本月隆重推出新的版本12.3

#在头条记录我的2025#自2024年9月,推出Delphi12.2版本后,本月隆重推出新的版本12.3,RADStudio12.3,包含了Delphi12.3和C++builder12.3最...

图解Java垃圾回收机制,写得非常好

什么是自动垃圾回收?自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制。所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象;而未使用中的对象(未引用...

Centos7 初始化硬盘分区、挂载(针对2T以上)添加磁盘到卷

1、通过命令fdisk-l查看硬盘信息:#fdisk-l,发现硬盘为/dev/sdb大小4T。2、如果此硬盘以前有过分区,则先对磁盘格式化。命令:mkfs.文件系统格式-f/dev/sdb...

半虚拟化如何提高服务器性能(虚拟化 半虚拟化)

半虚拟化是一种重新编译客户机操作系统(OS)将其安装在虚拟机(VM)上的一种虚拟化类型,并在主机操作系统(OS)运行的管理程序上运行。与传统的完全虚拟化相比,半虚拟化可以减少开销,并提高系统性能。虚...

HashMap底层实现原理以及线程安全实现

HashMap底层实现原理数据结构:HashMap的底层实现原理主要依赖于数组+链表+红黑树的结构。1、数组:HashMap最底层是一个数组,称为table,它存放着键值对。2、链...

long和double类型操作的非原子性探究

前言“深入java虚拟机”中提到,int等不大于32位的基本类型的操作都是原子操作,但是某些jvm对long和double类型的操作并不是原子操作,这样就会造成错误数据的出现。其实这里的某些jvm是指...

数据库DELETE 语句,还保存原有的磁盘空间

MySQL和Oracle的DELETE语句与数据存储MySQL的DELETE操作当你在MySQL中执行DELETE语句时:逻辑删除:数据从表中标记为删除,不再可见于查询结果物理...

线程池—ThreadPoolExecutor详解(线程池实战)

一、ThreadPoolExecutor简介在juc-executors框架概述的章节中,我们已经简要介绍过ThreadPoolExecutor了,通过Executors工厂,用户可以创建自己需要的执...

navicat如何使用orcale(详细步骤)

前言:看过我昨天文章的同鞋都知道最近接手另一个国企项目,数据库用的是orcale。实话实说,也有快三年没用过orcale数据库了。这期间问题不断,因为orcale日渐消沉,网上资料也是真真假假,难辨虚...

你的程序是不是慢吞吞?GraalVM来帮你飞起来性能提升秘籍大公开

各位IT圈内外的朋友们,大家好!我是你们的老朋友,头条上的IT技术博主。不知道你们有没有这样的经历:打开一个软件,半天没反应;点开一个网站,图片刷不出来;或者玩个游戏,卡顿得想砸电脑?是不是特别上火?...

大数据正当时,理解这几个术语很重要

目前,大数据的流行程度远超于我们的想象,无论是在云计算、物联网还是在人工智能领域都离不开大数据的支撑。那么大数据领域里有哪些基本概念或技术术语呢?今天我们就来聊聊那些避不开的大数据技术术语,梳理并...

秒懂列式数据库和行式数据库(列式数据库的特点)

行式数据库(Row-Based)数据按行存储,常见的行式数据库有Mysql,DB2,Oracle,Sql-server等;列数据库(Column-Based)数据存储方式按列存储,常见的列数据库有Hb...

AMD发布ROCm 6.4更新:带来了多项底层改进,但仍不支持RDNA 4

AMD宣布,对ROCm软件栈进行了更新,推出了新的迭代版本ROCm6.4。这一新版本里,AMD带来了多项底层改进,包括更新改进了ROCm的用户空间库和AMDKFD内核驱动程序之间的兼容性,使其更容易...

取消回复欢迎 发表评论: