標籤:style class blog code http ext
Ziplist是用字串來實現的雙向鏈表,對於容量較小的索引值對,為其建立一個結構複雜的雜湊表太浪費記憶體,所以redis 建立了ziplist來存放這些索引值對,這可以減少存放節點指標的空間,因此它被用來作為雜湊表初始化時的底層實現。即ziplist 的內部結構。
Zlbytes是整個ziplist 所佔用的空間,必要時需要重新分配。
Zltail便於快速的訪問到表尾節點,不需要遍曆整個ziplist。
Zllen表示包含的節點數。
Entries表示使用者增加上去的節點。
Zlend是一個255的值,表示ziplist末尾
Ziplist比dict更節省記憶體,所以在建立hash的時候預設ziplist作為其底層實現,當有需要時,再轉換回來。
舉例:使用者建立一個以ziplist為底層的hash鍵:
Redis-cli > hset book name "programing"
首先進入hsetCommand()函數的hashTypeLookupWriteOrCreate()函數
void hsetCommand(redisClient *c) { int update; robj *o; if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return; hashTypeTryConversion(o,c->argv,2,3); hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]); update = hashTypeSet(o,c->argv[2],c->argv[3]); addReply(c, update ? shared.czero : shared.cone); signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id); server.dirty++;}robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) { robj *o = lookupKeyWrite(c->db,key); if (o == NULL) { o = createHashObject(); dbAdd(c->db,key,o); } else { if (o->type != REDIS_HASH) { addReply(c,shared.wrongtypeerr); return NULL; } } return o;}
先建立一個空的ziplist,編碼方式預設為ziplist ,再add這個Key(book)到DB中
主要的添加操作在hashTpyeSet()中
/* Add an element, discard the old if the key already exists. * Return 0 on insert and 1 on update. * This function will take care of incrementing the reference count of the * retained fields and value objects. */int hashTypeSet(robj *o, robj *field, robj *value) { int update = 0; if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *zl, *fptr, *vptr; field = getDecodedObject(field); value = getDecodedObject(value); zl = o->ptr; fptr = ziplistIndex(zl, ZIPLIST_HEAD); if (fptr != NULL) { fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1); if (fptr != NULL) { /* Grab pointer to the value (fptr points to the field) */ vptr = ziplistNext(zl, fptr); redisAssert(vptr != NULL); update = 1; /* Delete value */ zl = ziplistDelete(zl, &vptr); /* Insert new value */ zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr)); } } if (!update) { /* Push new field/value pair onto the tail of the ziplist */ zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL); zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL); } o->ptr = zl; decrRefCount(field); decrRefCount(value); /* Check if the ziplist needs to be converted to a hash table */ if (hashTypeLength(o) > server.hash_max_ziplist_entries) hashTypeConvert(o, REDIS_ENCODING_HT); } else if (o->encoding == REDIS_ENCODING_HT) { if (dictReplace(o->ptr, field, value)) { /* Insert */ incrRefCount(field); } else { /* Update */ update = 1; } incrRefCount(value); } else { redisPanic("Unknown hash encoding"); } return update;}
首先會搜尋ziplist ,如果發現有相同的索引值,則替換掉,如果找不到,則把新加入的索引值push到ziplist 的末尾,在源碼中可以發現當其長度大於hash_max_ziplist_entries就需要轉換為hash table的編碼方式。
完成上述操作之後,就使用addReply()把結果存到buffer中傳給用戶端。