日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

Redis源码剖析(九)对象系统概述

發(fā)布時間:2024/4/19 windows 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis源码剖析(九)对象系统概述 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在Redis的源碼中,到處可見robj類型的變量,在介紹其他模塊時,只是將它看成Redis的數(shù)據(jù)類型,并沒有深入探究。而事實上,它是對象系統(tǒng),提供了對多種類型的封裝,Redis可以根據(jù)數(shù)據(jù)的具體形式,采用不同的類型進行存儲,一方面提高了靈活性,一方面也為節(jié)省內(nèi)存提供了便利,因為Redis所有的數(shù)據(jù)都是直接存在內(nèi)存中的,所以需要想方設(shè)法節(jié)省內(nèi)存

對象結(jié)構(gòu)

redisObject結(jié)構(gòu)中包含了對象系統(tǒng)的定義,記錄了數(shù)據(jù)類型,數(shù)據(jù)編碼格式,最后一次訪問的時間,引用計數(shù),值

//server.h /* 對象系統(tǒng)的定義 */ typedef struct redisObject {unsigned type:4; //類型,可以是string, hash, list, set和zset(宏定義給出)unsigned encoding:4; //編碼,表示ptr底層數(shù)據(jù)以何種方式存儲unsigned lru:LRU_BITS; //最后一次訪問的時間int refcount; //引用計數(shù)void *ptr; //實際存放的值 } robj;

:n是位域,顯式指出該變量占用的位數(shù),上述定義中,type占4位,encoding占4位,二者共占8位,即1個字節(jié)

類型

類型就是命令指出的數(shù)據(jù)格式

命令操作舉例
SET鍵為字符串對象,值為字符串對象SET db redis
SADD鍵為字符串對象,值為集合對象SADD db redis mongodb mysql
RPUSH鍵為字符串對象,值為列表對象RPUSH db redis mongodb mysql
HMSET鍵為字符串對象,值為哈希對象HMSET profile name Tom age 25 sex male
ZADD鍵為字符串對象,值為有序集合對象ZADD price 8.5 apple 5.0 banana 6.0 cherry

這5種類型由宏定義給出

//server.h #define OBJ_STRING 0 #define OBJ_LIST 1 #define OBJ_SET 2 #define OBJ_ZSET 3 #define OBJ_HASH 4

Redis提供了TYPE命令用于返回不同數(shù)據(jù)的類型

127.0.0.1:6379> SET db redis OK 127.0.0.1:6379> TYPE db //SET,字符串類型值 string 127.0.0.1:6379> SADD db_sadd redis mongodb mysql (integer) 3 127.0.0.1:6379> TYPE db_sadd //SADD,集合類型值 set 127.0.0.1:6379> ZADD price 8.5 apple 5.0 banana 6.0 cherry (integer) 3 127.0.0.1:6379> TYPE price //ZADD,有序集合類型值 zset 127.0.0.1:6379> RPUSH db_rpush redis mongodb mysql (integer) 3 127.0.0.1:6379> TYPE db_rpush //RPUSH,列表類型值 list 127.0.0.1:6379> HMSET profile name Tom age 25 sex male OK 127.0.0.1:6379> TYPE profile //HMSET,哈希表類型值 hash

編碼

編碼代表數(shù)據(jù)實際的存儲格式,實際保存的類型和提供的類型不一定相同,舉個例子,如果使用SET version 10添加一個字符串類型的鍵值對

#define OBJ_ENCODING_RAW 0 /* Raw格式,常規(guī)字符串類型 */ #define OBJ_ENCODING_INT 1 /* 整數(shù)形式 */ #define OBJ_ENCODING_HT 2 /* 哈希表 */ #define OBJ_ENCODING_ZIPMAP 3 /* 壓縮字典 */ #define OBJ_ENCODING_LINKEDLIST 4 /* 雙端鏈表 */ #define OBJ_ENCODING_ZIPLIST 5 /* 壓縮列表 */ #define OBJ_ENCODING_INTSET 6 /* 整數(shù)集合 */ #define OBJ_ENCODING_SKIPLIST 7 /* 跳表 */ #define OBJ_ENCODING_EMBSTR 8 /* EMBSTR格式,適用于存儲較短的字符串類型,比Raw少申請一次內(nèi)存 */ #define OBJ_ENCODING_QUICKLIST 9 /* 快速列表 */

Redis提供OBJECT ENCODING命令獲取鍵對應(yīng)的值在底層的編碼格式

底層數(shù)據(jù)結(jié)構(gòu)編碼常亮OBJECT ENCODING命令輸出
整數(shù)OBJ_ENCODING_INT“int”
embstr編碼字符串OBJ_ENCODING_EMBSTR“embstr”
raw編碼字符串OBJ_ENCODING_RAW“raw”
字典OBJ_ENCODING_HT“hashtable”
雙端鏈表OBJ_ENCODING_LINKEDLIST“l(fā)inkedlist”
壓縮列表OBJ_ENCODING_ZIPLIST“ziplist”
整數(shù)集和OBJ_ENCODING_INTSET“intset”
跳表OBJ_ENCODING_SKIPLIST“skiplist”
127.0.0.1:6379> set db_set_embstr redis OK 127.0.0.1:6379> OBJECT ENCODING db_set_embstr //短字符串采用embstr編碼 "embstr" 127.0.0.1:6379> set db_set_raw "long long long long long long long long long long ago ..." OK 127.0.0.1:6379> OBJECT ENCODING db_set_raw //長字符串采用raw編碼 "raw" 127.0.0.1:6379> SADD numbers 1 3 5 (integer) 3 127.0.0.1:6379> OBJECT ENCODING numbers //只有數(shù)字,采用整數(shù)集合 "intset" 127.0.0.1:6379> SADD numbers "seven" (integer) 1 127.0.0.1:6379> OBJECT ENCODING numbers //增加了一個字符串,不能再采用整數(shù)集合,改為哈希表 "hashtable"

可以看到,Redis會自適應(yīng)改變數(shù)據(jù)底層的編碼格式,而不是固定和一種類型綁定,這大大提高了靈活性

最后一次訪問時間

用來記錄最后一次訪問該數(shù)據(jù)的時間,可以獲得該數(shù)據(jù)的空轉(zhuǎn)時長,使用頻率等

引用計數(shù)

模仿C++的智能指針,使多個對象共享同一個底層數(shù)據(jù),以便于節(jié)省內(nèi)存占用,當(dāng)引用計數(shù)為0時,Redis會釋放該對象的內(nèi)存。

對象創(chuàng)建

對象操作主要涉及根據(jù)不同類型創(chuàng)建不同對象等操作

最基本的創(chuàng)建對象操作由createObject函數(shù)完成,函數(shù)根據(jù)給定類型和值創(chuàng)建編碼格式為raw的對象,其它創(chuàng)建對象的函數(shù)大多數(shù)都是直接或間接調(diào)用該函數(shù)

//object.c /* 根據(jù)type和ptr創(chuàng)建編碼為raw字符串的對象 */ robj *createObject(int type, void *ptr) {/* 申請對象內(nèi)存空間 */robj *o = zmalloc(sizeof(*o));/* 設(shè)置類型,編碼,值,引用計數(shù)初始化為1 */o->type = type;o->encoding = OBJ_ENCODING_RAW;o->ptr = ptr;o->refcount = 1;/* Set the LRU to the current lruclock (minutes resolution). *//* 計算當(dāng)前時間,賦值給lru作為最后一次訪問時間 */o->lru = LRU_CLOCK();/* 返回對象指針 */return o; }

創(chuàng)建字符串類型對象

字符串有raw和embstr兩種類型和編碼格式,raw適用于長字符串,需要執(zhí)行兩次動態(tài)內(nèi)存的申請,而embstr適用于短字符串,僅僅需要一次內(nèi)存申請,在創(chuàng)建字符串類型的對象時,Redis會判斷數(shù)據(jù)的長度以決定采用哪一個

//object.c #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44 /* 創(chuàng)建字符串類型對象,根據(jù)數(shù)據(jù)長度不同選擇不同的類型格式 */ robj *createStringObject(const char *ptr, size_t len) {/* 根據(jù)長度不同選擇不同的編碼方式 */if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)return createEmbeddedStringObject(ptr,len);elsereturn createRawStringObject(ptr,len); }

可以看到,長度小于44的字符串默認(rèn)都采用embstr,而大于44的采用raw

raw類型的字符串對象創(chuàng)建直接調(diào)用createObject函數(shù)即可,因為raw類型的字符串底層編碼也是raw

//object.c /* 創(chuàng)建raw字符串類型變量 */ robj *createRawStringObject(const char *ptr, size_t len) {/* sdsnewlen()創(chuàng)建一個長度為len的sds字符串 */return createObject(OBJ_STRING,sdsnewlen(ptr,len)); }

sdsnewlen函數(shù)是創(chuàng)建一個長度為len,值為ptr的sds變量

embstr類型的字符串創(chuàng)建不可以調(diào)用createObject函數(shù),由于采用embstr編碼格式,數(shù)據(jù)分布是不同的,需要重新實現(xiàn)創(chuàng)建函數(shù)

//object.c /* 創(chuàng)建類型為embstr,編碼為embstr的字符串對象 */ robj *createEmbeddedStringObject(const char *ptr, size_t len) {/* 和sds對象的創(chuàng)建有關(guān) */robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);struct sdshdr8 *sh = (void*)(o+1);/* 設(shè)置類型,編碼,數(shù)據(jù),引用計數(shù),最后一次訪問時間 */o->type = OBJ_STRING;o->encoding = OBJ_ENCODING_EMBSTR;o->ptr = sh+1;o->refcount = 1;o->lru = LRU_CLOCK();/* 將數(shù)據(jù)復(fù)制給sds對象,和sds有關(guān) */sh->len = len;sh->alloc = len;sh->flags = SDS_TYPE_8;if (ptr) {memcpy(sh->buf,ptr,len);sh->buf[len] = '\0';} else {memset(sh->buf,0,len+1);}return o; }

此外,Redis還提供根據(jù)長整型,長浮點型創(chuàng)建一個字符串類型對象,本質(zhì)都一樣,這里不再一一贅述

創(chuàng)建其它類型對象

除了字符串類型對象之外,其它類型對象的創(chuàng)建都顯得比較簡單,僅僅是創(chuàng)建一個相應(yīng)類型的變量,然后調(diào)用createObject函數(shù),返回后將編碼格式改成對應(yīng)類型的編碼格式

//object.c /* 創(chuàng)建快速列表對象 */ robj *createQuicklistObject(void) {quicklist *l = quicklistCreate();robj *o = createObject(OBJ_LIST,l);o->encoding = OBJ_ENCODING_QUICKLIST;return o; }/* 創(chuàng)建壓縮列表對象 */ robj *createZiplistObject(void) {unsigned char *zl = ziplistNew();robj *o = createObject(OBJ_LIST,zl);o->encoding = OBJ_ENCODING_ZIPLIST;return o; }/* 創(chuàng)建集合對象 */ robj *createSetObject(void) {dict *d = dictCreate(&setDictType,NULL);robj *o = createObject(OBJ_SET,d);o->encoding = OBJ_ENCODING_HT;return o; }/* 創(chuàng)建整數(shù)集合對象 */ robj *createIntsetObject(void) {intset *is = intsetNew();robj *o = createObject(OBJ_SET,is);o->encoding = OBJ_ENCODING_INTSET;return o; }/* 創(chuàng)建哈希對象 */ robj *createHashObject(void) {unsigned char *zl = ziplistNew();robj *o = createObject(OBJ_HASH, zl);o->encoding = OBJ_ENCODING_ZIPLIST;return o; }/* 創(chuàng)建有序集合對象 */ 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; }/* 創(chuàng)建集合壓縮列表對象 */ robj *createZsetZiplistObject(void) {unsigned char *zl = ziplistNew();robj *o = createObject(OBJ_ZSET,zl);o->encoding = OBJ_ENCODING_ZIPLIST;return o; }

小結(jié)

本篇主要是對Redis對象系統(tǒng)的一個概述,核心目的就是弄清楚Redis底層的類型和編碼都有哪些,接下來會對每個數(shù)據(jù)結(jié)構(gòu)進行具體的分析,到時候還會引用本篇的部分代碼。分析數(shù)據(jù)結(jié)構(gòu)是最無聊的事情,也正因為如此才沒有在最開始分析,不過為了后面的持久化功能,對象系統(tǒng)是不得不啃的骨頭,希望自己能夠堅持!

與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的Redis源码剖析(九)对象系统概述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。