深入底层C源码 Redis核心设计原理( 六 )


4.通过控制ziplist 的大小 , 则很好的解决了超大ziplist 的拷贝情况下对性能的影响 。每次改动只需要针对具体的小段ziplist 进行操作 。
5.Hash 数据结构1)介绍
【1】Hash 数据结构底层实现为一个字典( dict ),也是RedisBb用来存储K-V的数据结构,当数据量比较?。?或者单个元素比较小时,底层用ziplist存储,数据大小和元素数量阈值可以通过如下参数设置 。
hash-max-ziplist-entries512//ziplist 元素个数超过 512  , 将改为hashtable编码hash-max-ziplist-value64//单个元素大小超过 64 byte时 , 将改为hashtable编码2)发现说明
【1】为什么数据量小的时候采用ziplist存储?
【深入底层C源码 Redis核心设计原理】1.ziplist使用紧凑的连续内存块顺序存储数据,在list或者hash结构中,未使用listNode(24字节)和dictEntry(24字节)结构体来存储元素项 , 因此会节省内存 。
2.ziplist结构元素访问采用的是后向遍历(从后往前) , 因此在hash中可将热点的key或者在list中将热点的元素项放在最后,可以提升性能 。
3.因为ziplist的内存结构中 , 仅仅只使用了额外的11个字节来存储ziplist的属性,另外很重要的是ziplist使用后向遍历,当list或者hash中的元素较多时,可以根据元素的冷热性调整元素存储顺序 。
4.而在dictht结构体中,存储属性需要32个字节,其中元素dictEntry也是每个占用24个字节 。
6.Set 数据结构1)介绍
【1】Set 为无序的,自动去重的集合数据类型,Set 数据结构底层实现为一个value 为 null 的 字典( dict ),当数据可以用整形表示时,Set集合将被编码为intset数据结构 。
//在配置文件中设置set-max-intset-entries 512// intset 能存储的最大元素个数 , 超过则用hashtable编码【2】两个条件任意满足时Set将用hashtable存储数据 。1,元素个数大于 set-max-intset-entries , 2,元素无法用整形表示 。
2)intset数据结构
//intset内部其实是一个数组(int8_t coentents[]数组),而且存储数据的时候是有序的,因为在查找数据的时候是通过二分查找来实现的 。typedef struct intset {uint32_t encoding;// 编码方式uint32_t length;// 集合包含的元素数量int8_t contents[];// 保存元素的数组} intset;3)set存储过程
// set添加元素的处理函数 , 在文件t_set.c中//过程汇总//检查set是否存在不存在则创建一个set结合 。//根据传入的set集合一个个进行添加,添加的时候需要进行内存压缩 。//setTypeAdd执行set添加过程中会判断是否进行编码转换 。void saddCommand(client *c) {robj *set;int j, added = 0;// 取出集合对象set = lookupKeyWrite(c->db,c->argv[1]);// 对象不存在,创建一个新的,并将它关联到数据库if (set == NULL) {set = setTypeCreate(c->argv[2]->ptr);dbAdd(c->db,c->argv[1],set);}// 对象存在 , 检查类型else {if (set->type != OBJ_SET) {addReply(c,shared.wrongtypeerr);return;}}// 将所有输入元素添加到集合中for (j = 2; j < c->argc; j++) {// set 类型 添加元素if (setTypeAdd(set,c->argv[j]->ptr)) added++;}// 如果有至少一个元素被成功添加 , 那么执行以下程序if (added) {// 发送键修改信号signalModifiedKey(c,c->db,c->argv[1]);// 发送事件通知notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);}// 将数据库设为脏server.dirty += added;// 返回添加元素的数量addReplyLongLong(c,added);}//元素已经存在 直接返回 0,否则添加元素 返回 1//过程汇总//如果能够转成int的对象(isObjectRepresentableAsLongLong),那么就用intset保存 。//如果用intset保存的时候 , 如果长度超过512(REDIS_SET_MAX_INTSET_ENTRIES)就转为hashtable编码 。//其他情况统一用hashtable进行存储 。int setTypeAdd(robj *subject, sds value) {long long llval;// 字典if (subject->encoding == OBJ_ENCODING_HT) {// 将 value 作为键,NULL 作为值,将元素添加到字典中dict *ht = subject->ptr;dictEntry *de = dictAddRaw(ht,value,NULL);if (de) {dictSetKey(ht,de,sdsdup(value));dictSetVal(ht,de,NULL);return 1;}}// intsetelse if (subject->encoding == OBJ_ENCODING_INTSET) {// 判断是否可以用整形编码,可以的话用intset 编码if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {uint8_t success = 0;subject->ptr = intsetAdd(subject->ptr,llval,&success);if (success) {//如果元素个数超过set-max-intset-entries[ 默认 512 ]时,将转化为 hashtable 数据结构if (intsetLen(subject->ptr) > server.set_max_intset_entries)setTypeConvert(subject,OBJ_ENCODING_HT);return 1;}} else {//转整形失败,直接用hashtable存储setTypeConvert(subject,OBJ_ENCODING_HT);// 执行添加操作serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);return 1;}} else {// 未知编码serverPanic("Unknown set encoding");}return 0;}

推荐阅读