Redis原始碼學習簡記(七)object原理與個人理解
Redis原始碼學習簡記(七)object原理與個人理解
object是redis中的封裝系統。其把string,list,set,zset與hash封裝成一個統一的物件,命名為robj。該資料結構中,儲存了型別,編碼,引用次數,資料與LRU替換演算法的一些資料。具體先看看這個資料結構的定義,在server.h中定義。
- //redis屬於key-value 資料庫
- //nosql資料庫,這種對映關係使用dict用來維護
- //而dict實體資料(dictentry)中有欄位void *value
- typedef struct redisObject {
- unsigned type:4;
- /*
- string 0
- list 1
- set 2
- zset 3
- hash 4
- 4個bit
- */
- unsigned encoding:4;
- //編碼方式 4個bit
- unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
- * LFU data (least significant 8 bits frequency
- * and most significant 16 bits access time). */
- //24bit,LRU替換演算法
- int refcount;//引用計數32bit 4位元組
- void *ptr;//32位系統4位元組 64位系統8位元組
- //指向真正資料
- } robj;
來詳細講一下這裡面的東西。首先type,定義如下,其實就是4個bit用來儲存0~4的資料。
- /* The actual Redis Object */
- #define OBJ_STRING 0 /* String object. */
- #define OBJ_LIST 1 /* List object. */
- #define OBJ_SET 2 /* Set object. */
- #define OBJ_ZSET 3 /* Sorted set object. */
- #define OBJ_HASH 4 /* Hash object. */
然後編碼方式encoding。編碼方式的話,總的來說有11種,也是用4個bit來存,4個bit最大為16種編碼。
- #define OBJ_ENCODING_RAW 0 /* Raw representation */
- #define OBJ_ENCODING_INT 1 /* Encoded as integer */
- #define OBJ_ENCODING_HT 2 /* Encoded as hash table */
- #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
- #define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
- #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
- #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
- #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
- #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
- #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
- #define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
接下來的為lru,用於儲存LRU替換演算法,現在先不深究,使用了LRU_BITS (24bits)三個位元組儲存。
refcount為引用指標,作用跟智慧指標相似,用於計算物件的引用次數,當物件引用次數為0時,則釋放其使用空間。
最後的void *ptr則為最終指向資料的指標。這些資料基本就是之前分析的那些型別的資料。
資料結構不難理解,然後看看他的基本實現函式吧。基本都在object.c中實現。
首先是建立,該函式根據型別與ptr指向的資料返回一個robj指標。
- robj *createObject(int type, void *ptr) {
- robj *o = zmalloc(sizeof(*o));//分配空間
- o->type = type;//設定型別
- o->encoding = OBJ_ENCODING_RAW;//原生編碼模式
- //這裡分了很多種,後面看到了再詳細討論
- o->ptr = ptr;//執行真正的資料
- o->refcount = 1;//引用計數
- /* Set the LRU to the current lruclock (minutes resolution), or
- * alternatively the LFU counter. */
- //對於頁面置換演算法先不考慮,後面研究到了再看。
- if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
- o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
- } else {
- o->lru = LRU_CLOCK();
- }
- return o;
- }
設定一種特殊的refcount。使得該物件時shred。增加引用於減少引用,都會檢查這種特殊的引用計數。下面是使得物件變為共享的方法,其實就是將refcout數值設為int_max.
- robj *makeObjectShared(robj *o) {
- serverAssert(o->refcount == 1);
- o->refcount = OBJ_SHARED_REFCOUNT;
- //使得物件為共享,設定為obj_shared_refcount 其大小為int_max
- return o;
- }
接下來是建立字串物件的兩種方法。
先來介紹原生字串的建立方法。簡單的呼叫sdsnewlen的方法,然後得到字串指標後在呼叫create進行建立操作。
- /* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
- * string object where o->ptr points to a proper sds string. */
- robj *createRawStringObject(const char *ptr, size_t len) {
- //建立RawStringObject型別
- //其實是一個sds型別,而在預設情況下,encoding為OBJ_ENCODING_RAW
- /*
- #define OBJ_STRING 0
- #define OBJ_LIST 1
- #define OBJ_SET 2
- #define OBJ_ZSET 3
- #define OBJ_HASH 4
- */
- return createObject(OBJ_STRING, sdsnewlen(ptr,len));
- }
另外一種建立的方法這是一種字串直接是新增在物件後面,該字串直接儲存在物件的後面。規定整個物件不大於64個位元組。
- /* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
- * an object where the sds string is actually an unmodifiable string
- * allocated in the same chunk as the object itself. */
- robj *createEmbeddedStringObject(const char *ptr, size_t len) {
- //先一步步看程式碼
- robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
- //分配了robj資料與sdshdr8的sds頭空間加上了len+1的長度,多出來的1長度儲存null
- struct sdshdr8 *sh = (void*)(o+1);
- //o+1根據robj的結構體的位元組進行增長
- //robj總共16位元組,那麼加上16剛剛好是sdshdr8的頭部
- o->type = OBJ_STRING;
- o->encoding = OBJ_ENCODING_EMBSTR;
- o->ptr = sh+1;
- //sh+1即加上sdshdr8的位元組數,而sdshdr8為3位元組即len+alloc+flags
- //那麼加1後,ptr指向的為sh->buf
- /*
- 在研究字元所佔的位元組數中發現一個有趣的現象;
- struct __attribute__ ((__packed__)) sdshdr8 {
- uint8_t len;
- uint8_t alloc;
- unsigned char flags;
- char buf[];
- };
- 由於char buf[] 會導致計算sdshdr8的大小不算上buf[]很奇怪
- 而使用指標的話,則會加上8位元組用於儲存
- 而[]中有值時也會增加其長度,若不寫值時則會不計算。而buf的地址為flags最後一位。
- */
- o->refcount = 1;
- //初始化引用計數
- if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
- o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
- } else {
- o->lru = LRU_CLOCK();
- }
- //對於頁面演算法先不研究。
- //sds的初始化
- sh->len = len;
- sh->alloc = len;
- sh->flags = SDS_TYPE_8;
- if (ptr == SDS_NOINIT)
- //sds_noint 為一個靜態const char *
- //判斷是否要初始化
- sh->buf[len] = '\0';
- else if (ptr) {
- //初始化的話則看ptr的值是否為NULL
- memcpy(sh->buf,ptr,len);
- sh->buf[len] = '\0';
- } else {
- memset(sh->buf,0,len+1);
- }
- return o;
- }
這兩種字串的編碼,通過一個總的字串介面進行呼叫。這裡面如上面所說的,EmbeddedString總共包括下面幾個部分
robj 16位元組 sdshdr8頭部3個位元組 加上一個 '\0' 一個位元組,最後儲存內容是44個位元組
3+16+1+44=64。剛好是64節裝完所有的資料。
- /* Create a string object with EMBSTR encoding if it is smaller than
- * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
- * used.
- *
- * The current limit of 44 is chosen so that the biggest string object
- * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
- #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
- robj *createStringObject(const char *ptr, size_t len) {
- //保證使用embeddedstringboject的大小為64否則呼叫creteRawStringObject
- //設定其長度最長為44+16+3+1=64
- //事實上sdshdr8可儲存256個字元
- if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
- return createEmbeddedStringObject(ptr,len);
- else
- return createRawStringObject(ptr,len);
- }
看完了字串的封裝。觀察一下整型的封裝方法。總的來說當能使用8個位元組32位儲存的時候,robj中的ptr直接就是該整數。相當於彙編中的立即數。若超出該範圍,那麼就是用字串的形式儲存該資料。這時ptr就當指標使用了。
- robj *createStringObjectFromLongLong(long long value) {
- robj *o;
- if (value >= 0 && value < OBJ_SHARED_INTEGERS) {
- incrRefCount(shared.integers[value]);
- //大概瞭解了shared 它是一個機構體,包含了大量共享的的資料。
- //struct sharedObjectsStruct
- o = shared.integers[value];
- } else {
- if (value >= LONG_MIN && value <= LONG_MAX) {
- o = createObject(OBJ_STRING, NULL);
- o->encoding = OBJ_ENCODING_INT;
- o->ptr = (void*)((long)value);
- //若能使用8位元組儲存,直接將其儲存到ptr中相當於彙編的立即數。
- } else {
- //存不了則使用sds的模式儲存
- o = createObject(OBJ_STRING,sdsfromlonglong(value));
- }
- }
- return o;
- }
儲存浮點數的方式,都是轉化為字串的形式。
- /* Create a string object from a long double. If humanfriendly is non-zero
- * it does not use exponential format and trims trailing zeroes at the end,
- * however this results in loss of precision. Otherwise exp format is used
- * and the output of snprintf() is not modified.
- *
- * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
- robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
- char buf[MAX_LONG_DOUBLE_CHARS];
- //humanfriendly用於處理浮點數,可以先不理會
- int len = ld2string(buf,sizeof(buf),value,humanfriendly);
- //將long double轉換成sds,然後進行儲存
- return createStringObject(buf,len);
- }
物件間字串的複製,僅限於字串儲存的資料(這裡包括了使用字串儲存的整型),或者使用立即數儲存的整型。
- /* Duplicate a string object, with the guarantee that the returned object
- * has the same encoding as the original one.
- *
- * This function also guarantees that duplicating a small integere object
- * (or a string object that contains a representation of a small integer)
- * will always result in a fresh object that is unshared (refcount == 1).
- *
- * The resulting object always has refcount set to 1. */
- robj *dupStringObject(const robj *o) {
- robj *d;
- serverAssert(o->type == OBJ_STRING);
- switch(o->encoding) {
- case OBJ_ENCODING_RAW:
- return createRawStringObject(o->ptr,sdslen(o->ptr));
- case OBJ_ENCODING_EMBSTR:
- return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));
- case OBJ_ENCODING_INT:
- d = createObject(OBJ_STRING, NULL);
- d->encoding = OBJ_ENCODING_INT;
- d->ptr = o->ptr;
- return d;
- default:
- serverPanic("Wrong encoding.");
- break;
- }
- }
接下來就是一些別的型別的建立,大多數都是使用該型別的create函式進行建立,後又釋放空間的函式,都是比較簡單。還有引用計數減少的函式,都比較直接簡單。直接看看就好了。
- robj *createQuicklistObject(void) {
- quicklist *l = quicklistCreate();
- robj *o = createObject(OBJ_LIST,l);
- o->encoding = OBJ_ENCODING_QUICKLIST;
- return o;
- }
- robj *createZiplistObject(void) {
- unsigned char *zl = ziplistNew();
- robj *o = createObject(OBJ_LIST,zl);
- o->encoding = OBJ_ENCODING_ZIPLIST;
- return o;
- }
- robj *createSetObject(void) {
- dict *d = dictCreate(&setDictType,NULL);
- robj *o = createObject(OBJ_SET,d);
- o->encoding = OBJ_ENCODING_HT;
- return o;
- }
- robj *createIntsetObject(void) {
- intset *is = intsetNew();
- robj *o = createObject(OBJ_SET,is);
- o->encoding = OBJ_ENCODING_INTSET;
- return o;
- }
- robj *createHashObject(void) {
- unsigned char *zl = ziplistNew();
- robj *o = createObject(OBJ_HASH, zl);
- o->encoding = OBJ_ENCODING_ZIPLIST;
- return o;
- }
- robj *createZsetObject(void) {
- zset *zs = zmalloc(sizeof(*zs));
- robj *o;
- zs->dict = dictCreate(&zsetDictType,NULL);
- zs->zsl = zslCreate();
- o = createObject(OBJ_ZSET,zs);
- o->encoding = OBJ_ENCODING_SKIPLIST;
- return o;
- }
- robj *createZsetZiplistObject(void) {
- unsigned char *zl = ziplistNew();
- robj *o = createObject(OBJ_ZSET,zl);
- o->encoding = OBJ_ENCODING_ZIPLIST;
- return o;
- }
- robj *createStreamObject(void) {
- stream *s = streamNew();
- robj *o = createObject(OBJ_STREAM,s);
- o->encoding = OBJ_ENCODING_STREAM;
- return o;
- }
- robj *createModuleObject(moduleType *mt, void *value) {
- moduleValue *mv = zmalloc(sizeof(*mv));
- mv->type = mt;
- mv->value = value;
- return createObject(OBJ_MODULE,mv);
- }
- void freeStringObject(robj *o) {
- if (o->encoding == OBJ_ENCODING_RAW) {
- sdsfree(o->ptr);
- }
- }
- void freeListObject(robj *o) {
- if (o->encoding == OBJ_ENCODING_QUICKLIST) {
- quicklistRelease(o->ptr);
- } else {
- serverPanic("Unknown list encoding type");
- }
- }
- void freeSetObject(robj *o) {
- switch (o->encoding) {
- case OBJ_ENCODING_HT:
- dictRelease((dict*) o->ptr);
- break;
- case OBJ_ENCODING_INTSET:
- zfree(o->ptr);
- break;
- default:
- serverPanic("Unknown set encoding type");
- }
- }
- void freeZsetObject(robj *o) {
- zset *zs;
- switch (o->encoding) {
- case OBJ_ENCODING_SKIPLIST:
- zs = o->ptr;
- dictRelease(zs->dict);
- zslFree(zs->zsl);
- zfree(zs);
- break;
- case OBJ_ENCODING_ZIPLIST:
- zfree(o->ptr);
- break;
- default:
- serverPanic("Unknown sorted set encoding");
- }
- }
- void freeHashObject(robj *o) {
- switch (o->encoding) {
- case OBJ_ENCODING_HT:
- dictRelease((dict*) o->ptr);
- break;
- case OBJ_ENCODING_ZIPLIST:
- zfree(o->ptr);
- break;
- default:
- serverPanic("Unknown hash encoding type");
- break;
- }
- }
- void freeModuleObject(robj *o) {
- moduleValue *mv = o->ptr;
- mv->type->free(mv->value);
- zfree(mv);
- }
- void freeStreamObject(robj *o) {
- freeStream(o->ptr);
- }
- void incrRefCount(robj *o) {
- if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;
- }
- void decrRefCount(robj *o) {
- if (o->refcount == 1) {
- switch(o->type) {
- case OBJ_STRING: freeStringObject(o); break;
- case OBJ_LIST: freeListObject(o); break;
- case OBJ_SET: freeSetObject(o); break;
- case OBJ_ZSET: freeZsetObject(o); break;
- case OBJ_HASH: freeHashObject(o); break;
- case OBJ_MODULE: freeModuleObject(o); break;
- case OBJ_STREAM: freeStreamObject(o); break;
- default: serverPanic("Unknown object type"); break;
- }
- zfree(o);
- } else {
- if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
- if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;
- }
- }
- /* This variant of decrRefCount() gets its argument as void, and is useful
- * as free method in data structures that expect a 'void free_object(void*)'
- * prototype for the free method. */
- void decrRefCountVoid(void *o) {
- decrRefCount(o);
- }
相關文章
- redis原始碼學習之lua執行原理Redis原始碼
- Redis阻塞(學習筆記七)Redis筆記
- redis原始碼分析(七)、redis命令學習總結—Redis 有序集合(sorted set)Redis原始碼
- redis原始碼學習Redis原始碼
- Redis 學習筆記(篇七):Redis 持久化Redis筆記持久化
- redis個人原始碼分析筆記3---redis的事件驅動原始碼分析Redis原始碼筆記事件
- Redis學習筆記(七) 資料庫Redis筆記資料庫
- redis個人原始碼分析2---dict的實現原理Redis原始碼
- Flex 佈局:個人的學習與理解Flex
- Redis學習筆記七:主從叢集Redis筆記
- redis原始碼學習之slowlogRedis原始碼
- redis 有序集合(sorted set)(redis學習七)Redis
- Redis 原始碼學習之事件驅動Redis原始碼事件
- Redis核心原理與實踐--Redis啟動過程原始碼分析Redis原始碼
- redis學習(七) 訊息通知Redis
- redis原始碼分析(六)、redis命令學習總結—Redis 集合(Set)Redis原始碼
- Springcloud原始碼學習筆記1—— Zuul閘道器原理SpringGCCloud原始碼筆記Zuul
- Retrofit原始碼學習筆記原始碼筆記
- redis個人原始碼分析筆記4--hash物件的儲存Redis原始碼筆記物件
- redis個人原始碼分析筆記5---RDB持久化機制Redis原始碼筆記持久化
- 《深入理解計算機系統原理》學習筆記與習題答案(一)計算機筆記
- redis原始碼學習之工作流程初探Redis原始碼
- Redis核心原理與實踐--事務實踐與原始碼分析Redis原始碼
- YYImage原始碼剖析與學習原始碼
- Retrofit 原始碼學習與使用原始碼
- 向優秀程式碼學習:Redis 原始碼概覽Redis原始碼
- vue原始碼學習:Object.defineProperty 物件屬性監聽Vue原始碼Object物件
- 原始碼學習之Spring容器建立原理原始碼Spring
- Vue原始碼學習(二十):$emit、$on實現原理Vue原始碼MIT
- fastapi個人學習記錄ASTAPI
- 小白的學習記錄——Redis的簡單使用Redis
- vue原始碼學習筆記1Vue原始碼筆記
- jQuery原始碼學習筆記一jQuery原始碼筆記
- 學習筆記 sync/RWMutex原始碼筆記Mutex原始碼
- redis原始碼分析(三)redis命令學習總結—string字串Redis原始碼字串
- redis原始碼分析(五)、redis命令學習總結—雜湊HashRedis原始碼
- redis原始碼分析(一)複習redis命令、持久化方案、主從同步原理、配置Redis原始碼持久化主從同步
- Redis安裝與配置(學習筆記一)Redis筆記