竹笋

首页 » 问答 » 问答 » 浅析Redis中String数据类型及其
TUhjnbcbe - 2023/6/29 19:46:00
北京中科白癜风医院爱心公益 http://m.39.net/baidianfeng/a_6359028.html

从RdisObjct说起

在Rdis中,任意数据类型的键和值都会被封装为一个RdisObjct,也叫做Rdis对象,源码如下

我们来看一下这个结构体中的成员变量分别代表什么:

unsigndtyp:4:对象类型,分别是stringhashliststzst,占4个bit位,如下所示

#dfinOBJ_STRING0/*Stringobjct.*/#dfinOBJ_LIST1/*Listobjct.*/#dfinOBJ_SET2/*Stobjct.*/#dfinOBJ_ZSET3/*Sortdstobjct.*/#dfinOBJ_HASH4/*Hashobjct.*/

unsigndncoding:4:底层编码方式,共有11种,4个bit位

unsigndlru:LRU_BITS:该对象最后一次被访问的时间,占24个bit,在Rdis内存回收中起到关键作用

intrfcount:对象引用计数器,计数器为0则说明对象无人引用,可以被回收

void*ptr:指针,指向存放实际数据的空间

我们注意到,在Rdis中有5中数据结构(用户使用的),但在底层却有11种编码方式,Rdis会根据存储的数据类型、存储数据的大小,选择不同的编码方式,以获得最优的性能。一种数据结构会对应多种数据结构,如下表所示。

下面,我们现在介绍以下String数据类型,及其底层的编码方式。

Rdis数据结构--String

String类型的基本介绍和命令

String类型,也就是字符串类型,是Rdis中最简单的存储类型。它可以存储字符串、整数或浮点数。下面是一些String类型常用的命令

1.SETkyvalu:设置指定ky的值为指定的字符串或数字。

2.GETky:获取指定ky的值。

3.本地虚拟机rdis:0stky01valu01"OK"本地虚拟机rdis:0gtky01"valu01"

4.INCRky:将指定ky的值加1,如果该ky不存在,则先将其设置为0,再进行加1操作。

5.DECRky:将指定ky的值减1,如果该ky不存在,则先将其设置为0,再进行减1操作。

6.INCRBYkyincrmnt:将指定ky的值增加指定的增量。

7.DECRBYkydcrmnt:将指定ky的值减少指定的减量。

8.APPENDkyvalu:将指定的值追加到指定ky的值的末尾。

9.STRLENky:返回指定ky的值的长度。

10.GETRANGEkystartnd:返回指定ky的值的子字符串,根据起始位置和结束位置指定。

11.SETRANGEkyoffstvalu:将指定ky的值从指定偏移位置开始,替换为指定的字符串。

12.MSETky1valu1[ky2valu2...]:同时设置多个ky的值。(”[]”中括号内表示可选)

13.MGETky1[ky2...]:获取多个ky的值。

??这里仅给出SET、GET命令,其他的请自行测试。这些命令只是RdisString类型命令的一小部分,Rdis还提供了其他更多的命令来处理String类型的数据。你可以参考Rdis官方文档以获取完整的命令列表和详细的命令说明。

String类型的底层实现

在Rdis中,String类型的数据结构并不是采用C语言中自带的字符串类型,C语言中的数据结构存在很多问题,比如:

获取字符串长度的需要通过运算

非二进制安全

不可修改

因此,String在Rdis中有其他三种编码方式:int、mbstr、raw。其中,raw和mbstr类型,都是基于动态字符串(SDS)实现的,下面我们先来看看动态字符串的结构是怎样的。

动态字符串(SDS)

动态字符串的结构体如下

这里解释一下结构体中各个成员变量的作用:

ln:已经保存的字符串字节数,不包含结束标示

alloc:申请的总的字节数,不包含结束标示

flags:不同的SDS的头类型,用来控制SDS的头大小

buf[]:真正存储数据

我们先来聊一下flags这个成员变量。在rdis中其实定义了5个SDS结构体(其中hisdshdr5已经弃用)如图所示。他们之间的主要区别在于ln和alloc的长度不同。

在rdis中,为了尽可能地节省内存空间,当字符串长度在不同的区间时,会选择不同的结构体,例如:

当字符串长度在0~个字节之间时,会选择hisdshdr8,这样一来,用于表示字符串字节数和申请的总字节数的空间就会被大大节省,以此类推。

例如,一个包含字符串“nam”的sds结构如下:

SDS之所以叫做动态字符串,是因为它具备动态扩容的能力,例如一个内容为“hllo”的SDS,假如我们要给这个SDS追加一段字符串”world”,这里首先会申请新内存空间:

如果新字符串小于1M,则新空间为扩展后字符串长度的两倍+1

如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1。

这种机制称为内存预分配。内存预分配可以减少进行内存重新分配的开销,减少内存碎片,使得rdis的性能得到提高,空间利用率也得到提高。

String的三种编码方式

RAW

raw是string的基本编码方式,基于简单动态字符串(SDS)实现,存储上限为mb。当一个字符串采用raw的编码方式的时候,它的结构如图所示。

EMBSTR

如果存储在SDS中的数据小于等于44字节,则会采用EMBSTR编码,此时**RdisObjct与SDS是一段连续空间。而不是像RAW的编码方式一样,由ptr指向另外一片空间,**申请内存时只需要调用一次内存分配函数,效率更高。结构如下,

??为什么是44字节?Rdis默认的内存分配器jmalloc分配内存大小的单位是$2^n$,因此,如果分配的空间大小为2、4、8…字节等$2^n$字节,就不会产生内存碎片。

而rdisObjct和hisdshdr8中lnallocflags三个成员变量加起来刚刚好是16+4=20字节,如果char[](数据大小)的大小为44字节时,加起来刚刚好是64字节,也即26不会产生内存碎片。

RAW和EMBSTR的编码演示

INT

如果存储的字符串是整数值,并且大小在LONGMAX范围内,则会采用INT编码

直接将数据保存在RdisObjct的ptr指针位置(刚好8字节),不再需要SDS了。

INT编码演示

写在最后:在使用string类型时,尽可能让其长度小于44字节,或者使用整数表示,使其使用EMBSTR和INT编码

1
查看完整版本: 浅析Redis中String数据类型及其