Redis-对象

2023-12-17 17:49:04

参考资料 极客时间Redis(亚风)

Redis对象

String

? 基本编码?式是RAW,基于简单动态字符串(SDS)实现,存储上限为512mb。
? 如果存储的SDS?度?于44字节,则会采?EMBSTR编码,此时object head与SDS是?段连续空间。申请内存时只需要调??次内存分配函数,效率更?。
? 如果存储的字符串是整数值,并且??在LONG_MAX范围内,则会采?INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS了。
对应第一种情况:
在这里插入图片描述
对应第二种情况:
在这里插入图片描述
对应第三种情况:
在这里插入图片描述

List

Redis采?QuickList实现List。

在这里插入图片描述

Set

Set是Redis中的集合,不?定确保元素有序,可以满?元素唯?、查询效率要求极?。
为了查询效率和唯?性,set采?HT编码(Dict)。Dict中的key?来存储元素,value统?为null.
当存储的所有数据都是整数,并且元素数量不超过set-max-intset-entries时,Set会采?lntSet编码,以节省内存。
第一中情况都是int类型:IntSet 实现

在这里插入图片描述
第二种:使用dict实现
在这里插入图片描述
上面过程对应的源码:

int setTypeAdd (robj *subject, sds value) {
	Long Long llval;
	if (subject->encoding==OBJ_ENCODING_HT) { 
	//已经是HT编码,直接添加元素
	dict *ht = subject->ptr;
	dictEntry *de = dictAddRaw(ht,value,NULL)if (de) {
	dictSetKey(ht, de, sdsdup (value))dictSetVal(ht,de, NULL)return 1 ; }
	}
	else if (subject->encoding == OBJ_ENCODING_INTSET){ 
	// ?前是INTSET
	// 判断value是否是整数
	if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {
	uint8_t success = 0;
	// 是整数,直接添加元素到set
	subject->ptr=intsetAdd (subject->ptr, llval, &success)if (success){
	/* 当intset元素数量超出set_max_intset_entries,则转为HT.*/
	size_t max_entries = server.set_max_intset_entries;
	if (max_entries >= 1<<30) max_entries=1<<30;
	if (intsetLen(subject->ptr) > max_entries)
	setTypeConvert(subject,OBJ_ENCODING_HT)return 1;
	}
	) else {
	// 不是整数,直接转为HT
	setTypeConvert(subject,OBJ_ENCODING_HT)serverAssert(dictAdd(subject->ptr,sdsdup (value),NULL)== DICT_OK)return 1;}
} }
ZSet

底层是用三种数据结构实现的(HT + SkipList + ZPlist)

typedef struct zset {
	// Dict指针
	dict *dict;
	// SkipList指针
	zskiplist *zsl;
} zset;

当元素数量不多时,HT和SkipList的优势不明显,?且更耗内存。因此zset还会采?ZipList结构来节省内存,不过需要同时满?两个条件:
① 元素数量?于zset-max-ziplist-entries,默认值128
② 每个元素都?于zset-max-ziplist-value字节,默认值64
源码如下:

// zadd添加元素时,先根据key找到zset,不存在则创建新的zset
zobj = LookupKeywrite(c->db, key)if (checkType (c, zobj,OBJ_ZSET)goto cleanup;
//判断是否存在
if (zobj == NULL) {// zset不存在
if (server.zset_max_ziplist_entries == 0
server.zset_max_ziplist_value < sdslen(c->argv [scoreidx+1]->ptr))
{ 
// zset_max_ziplist_entries设置为0就是禁?了ZipList,
//或者value??超过了zset_max_ziplist_value,采?HT + SkipList
Zobj = createzsetobject()} else {
// 否则,采? ZipList
zobj = createzsetziplistobject()}
dbAdd(c->db, key, zobj)}
zsetAdd(zobj,score, ele, flags, &retflags, &newscore)

Ziplist本身没有排序功能,?且没有键值对的概念,因此需要有zset通过编码实现:
? Ziplist是连续内存,因此score和element是紧挨在?起的两个entry,element在前,score在后
? score越?越接近队?,score越?越接近队尾,按照score值升序排列
在这里插入图片描述

Hash

Hash底层采?的编码与Zset也基本?致,只需要把排序有关的SkipList去掉即可:
? Hash结构默认采?ZipList编码,?以节省内存。Ziplist中相邻的两个entry 分别保存field和value
? 当数据量较?时,Hash结构会转为HT编码,也就是Dict,触发条件有两个:
① ZipList中的元素数量超过了hash-max-ziplist-entries(默认512)
② ZipList中的任意entry??超过了hash-max-ziplist-value(默认64字节)
在这里插入图片描述

// 插入命令
void hsetCommand(client *c) {
	int i, created = 0;
	robj *o;
	//判断hash的key是否存在,不存在则创建?个新的,默认采?ZipList编码
	if ((o = hashTypeLookupwriteOrCreate(c, c->argv[1]))==NULL) return;
	// 判断是否需要把ZipList转为Dict
	hashTypeTryConversion(o,c->argv,2,c->argc-1)// 循环遍历每?对field和value,井执?hset命令
	for (i = 2;i<c-argc; i += 2)
	created += !hashTypeSet(o,c->argv[I]->ptr,c->argv[i+1]->ptr, HASH_SET_COPY);
	}
	robj *hashTypeLookupwriteOrCreate(client *c, robj *key)
	// 查找key
	robj *o = LookupKeywrite(c->db, key)if (checkType(c,o, OBJ_HASH)return NULL;
	// 不存在,则创建新的
	if (o == NULL) {
	o = createHashobject()dbAdd (c->db, key,o)}
	return o;
}
robj *createHashobject (void) {
	// 默认采?zipList编码,申请ZipList内存空间
	unsigned char *z1 = ziplistNew();
	robj *o = createobject(OBJ_HASH, z1)//设置编码
	o->encoding = OBJ_ ENCODING_ZIPLIST;
	return 0;
}
// ziplist 在什么时候转换为HT
void hashTypeTryConversion(robj *o,robj **argv, int start, int end)
	int i;size_t sum = 0;
	// 本来就不是ZipList编码,什么都不?做了
	if (o->encoding != OBJ_ENCODING_ZIPLIST) return;
	//依次遍历命令中的field、 value参数
	for (i = s. start; i < c. end;j++)
	if (!sdsEncodedobject (argv[i]))
	continue;
	size_t len = sdsLen (argv[i]->ptr)// 如果field或value超过hash_max_ziplist_value,则转为HT
	if (len > server.hash_max_ziplist_value) {
	hashTypeConvert(o, OBJ_ENCODING_HT)return;
	}
	sum += len;
	}
	// ziplist??超过1G,也转为HT
	if (!ziplistSafeToAdd(o- >ptr, sum))
	hashTypeConvert(o, OBJ_ENCODING_HT)}
// 真正插入元素的逻辑
int hashTypeSet(robj *o,sds field, sds value, int flags){
	int update = 0// 判断是否为ZipList编码
	if (o->encoding == OBJ_ENCODING_ZIPLIST) {
	unsigned char *zl, *fptr, *vptr;
	zl = o->ptr;
	// 查询head指针
	fptr = ziplistIndex(zl, ZIPLIST_HEAD)if (fptr != NULL)// head不为空,说明ZipList不为空,开始查找key
	fptr = ziplistFind(zl, fptr, (unsigned char*) field, sdslen(value));
	if (fptr != NULL){//判断是否存在,如果已经存在则更新
	update=1;
	ziplistReplace(zl, vptr, (unsigned char*) value,sdslen (value))// 不存在,则直接push
	if(!update){ // 依次push新的field和value到zipList的尾部
	zl = ziplistPush(zl, (unsigned char*) field, sdslen(field),ZIPLIST_TAIL);
	zl = ziplistPush(zl, (unsigned char*)value, sdslen (value),ZIPLIST_TAIL)}
	o->ptr = zl;
	/* 插?了新元素,检查list?度是否超出,超出则转为HT */
	if (hashTypeLength(o)>server.hash_max_ziplist_entries)
	hashTypeConvert(o, OBJ_ENCODING_HT)} else if (o->encoding == OBJ_ENCODING_HT) {
	// HT编码,直接插?或覆盖
	} 
	return update;
}

文章来源:https://blog.csdn.net/qq_43259860/article/details/135044248
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。