数据结构论坛

首页 » 分类 » 定义 » 重学RedisRedis常用数据类型存
TUhjnbcbe - 2025/8/3 15:15:00
北京治疗白癜风大约花多少钱啊 https://m.39.net/disease/a_5469158.html

一、SDS

1,SDS源码解读

sds(SimpleDynamicString),Simple的意思是简单,Dynamic即动态,意味着其具有动态增加空间的能力,扩容不需要使用者关心。String是字符串的意思。说白了就是用C语言自己封装了一个字符串类型,这个项目由Redis作者antirez创建,作为Redis中基本的数据结构之一,现在也被独立出来成为了一个单独的项目。

sds有两个版本,在Redis3.2之前使用的是第一个版本,其数据结构如下所示:

typedefchar*sds;//注意,sds其实不是一个结构体类型,而是被typedef的char*structsdshdr{unsignedintlen;//buf中已经使用的长度unsignedintfree;//buf中未使用的长度charbuf[];//柔性数组buf};

但是在Redis3.2版本中,对数据结构做出了修改,针对不同的长度范围定义了不同的结构,如下,这是目前的结构:

typedefchar*sds;struct__attribute__((__packed__))sdshdr5{//对应的字符串长度小于15unsignedcharflags;charbuf[];};struct((__packed__))sdshdr8{//对应的字符串长度小于18uint8_tlen;/*used*///目前字符创的长度,使用1个byteuint8_talloc;//已经分配的总长度,使用1个byteunsignedcharflags;//flag用3bit来标明类型,类型后续解释,其余5bit目前没有使用。使用1byte。charbuf[];//柔性数组,以\0结尾};struct((__packed__))sdshdr16{//对应的字符串长度小于uint16_tlen;/*used,使用2byte*/uint16_talloc;/*excludingtheheaderandnullterminator,使用2byte*/unsignedcharflags;/*3lsboftype,5unusedbits*/charbuf[];};struct((__packed__))sdshdr32{//对应的字符串长度小于uint32_tlen;/*used,使用4byte*/uint32_talloc;/*excludingtheheaderandnullterminator,使用4byte*/unsignedcharflags;charbuf[];};struct((__packed__))sdshdr64{//对应的字符串长度小于uint64_tlen;/*used*/uint64_talloc;/*excludingtheheaderandnullterminator*/unsignedcharflags;charbuf[];};

2,SDS的特点

二进制安全的数据结构,不会产生数据的丢失

内存预分配机制,避免了频繁的内存分配。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。(字符串最大长度为M)

兼容c语言函数库

二、Redis中几种数据结构

redisDb默认情况下有16个,每个redisDb内部包含一个dict的数据结构,dict内部包含dictht数组,数组个数为2,主要用于hash扩容使用。dictht内部包含dictEntry的数组,dictEntry其实就是hash表的一个key-value节点,如果冲突通过[链地址法]解决

1,redisServer

数据结构redisServer是一个redis服务端的抽象,定义在server.h中。中的属性非常多,以下为节选的一部分,简单介绍下

structredisServer{/*General*/pid_tpid;/*Mainprocesspid.*/......inthz;/*serverCron()callsfrequencyinhertz*/redisDb*db;dict*
  
  //当内存不足时,Redis会根据LRU算法回收一部分键所占的空间,而该eviction_pool是一个长为16数组,保存可能被回收的键
  
  //eviction_pool中所有键按照idle空转时间,从小到大排序,每次回收空转时间最长的键structevictionPoolEntry*eviction_pool;/*Evictionpoolofkeys*/intid;/*数据库ID*/longlongavg_ttl;/*键的平均过期时间*/}redisDb;

3,dict

dict是redis中的字典,定义在dict.h文件中,其主要的属性如下

typedefstructdict{dictType*type;void*privdata;dicththt[2];//方便渐进的rehash扩容,dict的hashtablelongrehashidx;/*rehashingnotinprogressifrehashidx==-1*/unsignedlongiterators;/*numberofiteratorscurrentlyrunning*/}dict;

ht[2]:哈希表数组,为了扩容方便有2个元素,其中一个哈希表正常存储数据,另一个哈希表为空,空哈希表在rehash时使用

rehashidx:rehash索引,当不在进行rehash时,值为-1

4,dictht

dictht是哈希表结构,定义在文件中,其重要的属性如下

typedefstructdictht{dictEntry**table;unsignedlongsize;unsignedlongsizemask;unsignedlongused;}dictht;

**table:key-value键值对节点数组,类似Java中的HashMap底层数组

size:哈希表容量大小

sizemask:总是等于size-1,用于计算索引值

used:哈希表实际存储的dictEntry数量

5,dictEntry

dictEntry是redis中的**key-value键值对节点,是实际存储数据的节点**,定义在

typedefstructdictEntry{void*key;union{void*val;uint64_tu64;int64_ts64;doubled;}v;structdictEntry*next;}dictEntry;

*key:键对象,总是一个字符串类型的对象SDS

*val:值对象,可能是任意类型的对象。对应常见的5种数据类型:string,hash,list,set,zset

*next:尾指针,指向下一个节点

三、数据类型

1,Redis数据对象结构

Redis数据库中所有数据都以key-value节点dictEntry存储,其中key和value都是一个redisObject结构体对象,只不过key总是一个字符串类型的对象(SDS),value则可能是任意一种数据类型的对象。结构体定义在中如下所示

typedefstructredisObject{unsignedtype:4;//占用4bitunsignedencoding:4;//占用4bitunsignedlru:LRU_BITS;/*占用24bitLRUtime(relativetogloballru_clock)or*LFUdata(leastsignificant8bitsfrequency*andmostsignificant16bitsaccesstime).*/intrefcount;//占用4bytevoid*ptr;//占用8byte总空间:4bit+4bit+24bit+4byte+8byte=16byte}robj;

可以看到该结构体中重要的属性如下,不同的对象具有不同的类型type,同一个类型的type会有不同的存储形式encoding

type:该属性标明了数据对象的类型,比如String,List等

encoding:这个属性指明了对象底层的存储结构,比如ZSet类型对象可能的存储结构有ZIPLIST和SKIPLIST

*ptr:指向底层存储结构的指针

2,Redis数据类型及存储结构

Redis中数据类型及其存储结构定义在文件中

/*TheactualRedisObject*/#defineOBJ_STRING0/*Stringobject.*/#defineOBJ_LIST1/*Listobject.*/#defineOBJ_SET2/*Setobject.*/#defineOBJ_ZSET3/*Sortedsetobject.*/#defineOBJ_HASH4/*Hashobject.*/#defineOBJ_MODULE5/*Moduleobject.*/#defineOBJ_STREAM6/*Streamobject.*/#defineOBJ_ENCODING_RAW0/*Rawrepresentation*/#defineOBJ_ENCODING_INT1/*Encodedasinteger*/#defineOBJ_ENCODING_HT2/*Encodedashashtable*/#defineOBJ_ENCODING_ZIPMAP3/*Encodedaszipmap*/#defineOBJ_ENCODING_LINKEDLIST4/*Nolongerused:oldlistencoding.*/#defineOBJ_ENCODING_ZIPLIST5/*Encodedasziplist*/#defineOBJ_ENCODING_INTSET6/*Encodedasintset*/#defineOBJ_ENCODING_SKIPLIST7/*Encodedasskiplist*/#defineOBJ_ENCODING_EMBSTR8/*Embeddedsdsstringencoding*/#defineOBJ_ENCODING_QUICKLIST9/*Encodedaslinkedlistofziplists*/#defineOBJ_ENCODING_STREAM10/*Encodedasaradixtreeoflistpacks*/

四、Redis中常用数据类型和结构

1,字符串对象String

OBJ_STRING字符串对象底层数据结构一般为简单动态字符串(SDS),但其存储方式可以是OBJ_ENCODING_INT、OBJ_ENCODING_EMBSTR和OBJ_ENCODING_RAW,不同的存储方式代表着对象内存结构的不同。

a)OBJ_ENCODING_INT

如果保存的字符串长度小于20并且可以解析为整数(值范围为:-2^63~2^63-1),那么这个整数就会直接保存在redisObject的ptr属性里

b)OBJ_ENCODING_EMBSTR

长度小于44(OBJ_ENCODING_EMBSTR_SIZE_LIMIT)的字符串将以简单动态字符串(SDS)的形式存储,但是会使用malloc方法一次分配内存,将redisObject对象头和SDS对象连续存在一起。因为默认分配空间为64byte,而其中value为string类型采用sdshdr8中len、alloc、flags各占用1byte,buf以\0占用1byte,redisObject占用16字节,剩余buff可使用为64-4-16=44byte。

c)OBJ_ENCODING_RAW

字符串将以简单动态字符串(SDS)的形式存储,需要两次malloc分配内存,redisObject对象头和SDS对象在内存地址上一般是不连续的

d)检测

#string类型查看redis的存储SETkeyvalue//存入字符串键值对STRLENkey//查看key的长度(占用的byte字节)OBJECTENCODINGkey//查看key在redis中的存储类型SETRANGEkeyoffsetvalue//修改key从offset(字符偏移量)字符修改为value,如果原本为embstr修改后也会变成raw。GETRANGEkeystartend//获取key的部分值

2,列表对象list

OBJ_LIST列表对象的底层存储结构有过3种实现,分别是OBJ_ENCODING_LINKEDLIST、OBJ_ENCODING_ZIPLIST和OBJ_ENCODING_QUICKLIST,其中OBJ_ENCODING_LINKEDLIST在3.2版本以后就废弃了。使用命令:OBJECTENCODINGkey查看存储类型。

a)OBJ_ENCODING_LINKEDLIST

底层采用双端链表实现,每个链表节点都保存了一个字符串对象,在每个字符串对象内保存了一个元素。

b)OBJ_ENCODING_ZIPLIST

底层实现类似数组,使用特点属性保存整个列表的元信息,如整个列表占用的内存大小,列表保存的数据开始的位置,列表保存的数据的个数等,其保存的数据被封装在zlentry。

zlbytes:记录整个压缩列表占用的内存字节数。uint_32_t,4byte。

zltail:记录压缩列表表尾节点距离起始地址有多少字节,通过这个偏移量,程序无需遍历整个压缩列表就能确定表尾节点地址。uint_32_t,4byte。

zlen:记录压缩列表包含的节点数量。uint_16_t,2byte。

entryX:压缩列表的各个节点,节点长度由保存的内容决定。

zlend:特殊值(

0xFFF

),用于

标记压缩列表末端

。uint_8_t,1byte。

prerawlen:表示当前节点的前一个节点长度

len:当前节点的长度

data:当前节点的数据

c)OBJ_ENCODING_QUICKLIST

底层采用双端链表结构,不过每个链表节点都保存一个ziplist,数据存储在ziplist中

d)redis.conf配置

通过设置每个ziplist的最大容量,quicklist的数据压缩范围,提升数据存取效率。

list-max-ziplist-size-2//单个ziplist节点最大能存储8kb,超过则进行分裂,将数据存储在新的ziplist节点中list-

1
查看完整版本: 重学RedisRedis常用数据类型存