Redis中的数据结构 (11)

redisObject

从定义上来看, redisObject有:

与Value Type一致的Object Type, 即type字段

特定的Object Encoding, 即encoding字段, 表明对象底层使用的数据结构类型

记录最末一次访问时间的lru字段

引用计数refcount

指向底层数据结构实例的ptr字段

redisObject的通用操作API如下:

API 功能
char *strEncoding(int encoding)   返回各种编码的可读字符串表达  
void decrRefCount(robj *o);   引用计数-1. 若减后引用计数会降为0, 则会自动调用 freeXXXObject函数释放对象  
void decrRefCountVoid(void *o);   功能同decrRefCount, 只不过接收的是void * 型参数  
void incrRefCount(robj *o);   引用计数+1  
robj *makeObjectShared(robj *o);   将对象置为"全局共享对象", 所谓的"全局只读共享对象", 有以下特征
0. 内部引用计数为 INT_MAX
0. 引用计数操作函数对其不起作用
0. 多纯种共享读是安全的, 不需要加锁
0. 禁止写操作
 
robj *resetRefCount(robj *obj);   将引用计数置为0, 但不会调用freeXXXObject函数释放对象  
robj *createObject(int type, void *ptr);   创建一个对象, 对象类型由参数指定, 对象底层编码指定为RAW, 底层数据由参数提供, 对象引用计数为1.
并初始化lru字段. 若服务器采用LRU算法, 则置该字段的值为当前分钟级别的一个时间戳. 若服务器采用LFU算法, 则置为一个计数值.
 
unsigned long long estimateObjectIdleTime(robj *o)   获取一个对象未被访问的时间, 单位为毫秒.
由于redisObject中lru字段有24位, 并不是无限长, 所以有循环溢出的风险, 当发生循环溢出时(即当前LRU时钟计数比对象中的lru字段小), 那么该函数始终保守认为循环溢出只发生了一次
 
3.1 字符串对象

字符串对象支持三种编码方式: INT, RAW, EMBSTR, 三种方式的内存布局分别如下:

stringObject

字符串对象的相关接口如下:

分类 API名 功能
创建接口   robj *createEmbeddedStringObject(const char *ptr,size_t len)   创建一个编码为EMBSTR的字符串对象.
即底层使用SDS, 且SDS与RedisObject位于同一块连续内存上
 
--   robj *createRawStringObject(const char *ptr,size_t len)   创建一个编码为RAW的字符串对象.
即底层使用SDS, 且SDS由RedisObject间接持有
内部是先用入参创建一个SDS, 然后用这个SDS再去调用createObject
 
--   robj *createStringObject(const char *ptr,size_t len)   创建一个字符串对象.
当len参数的值小于或等于OBJ_ENCODING_EMBSTR_SIZE_LIMIT时, 编码方式为EMBSTR, 否则为RAW
内部是通过调用createRawStringObject与createEmbeddedStringObject来创建不同编码的字符串对象的
 
--   robj *createStringObjectFromLongLong(long long value)   根据整数值, 创建一个字符串对象.
若可复用全局共享字符串对象池中的对象, 则会尽量复用. 否则以最节省内存的原则, 来决定对象的编码
 
--   robj *createStringObjectFromLongDouble(long double value,int humanfriendly)   根据浮点数值, 创建一个字符串对象
其中参数humanfriendly不为0, 则字符串以小数形式表达. 否则以exp计数法表达.根据字符串表达的长短, 编码可能是RAW, 或EMBSTR
 
释放接口   void freeStringObject(robj *o)   释放字符串对象.
若字符串对象底层使用SDS, 则调用sdsfree释放这个SDS.
否则什么也不做
 
读写接口   robj *dupStringObject(const robj *o)   创建一个字符串对象的深拷贝副本. 不影响原字符串对象的引用计数.
创建的副本与原字符串毫无关联
 
--   int isSdsRepresentableAsLongLong(sds s,long long *llval)   判断SDS字符串是否是一个取值在long long数值范围内的数值的字符串表达. 如果是, 就把相应的数值置在出参中
内部调用的是string2ll来判断

严格来讲这不应该算是RedisObject的接口函数, 而应当算是SDS的接口函数"
 
--   int isObjectRepresentableAsLongLong(robj *o,long long *llval)   判断字符串对象是否是一个取值在long long数值范围内的数值的字符串表达. 如果是, 就把相应的数值置在出参中.  
--   robj *tryObjectEncoding(robj *o)   尝试缩减这个字符串对象的内存占用.

策略为:
如果字符串对象代表的是一个位于long取值范围内的数值, 则尝试返回全局共享字符串对象池里的等价对象. 若由于服务器配置等原因不成功, 则尝试将对象编码改为INT
如果以上都不成功, 则尝试将对象的编码改为EMBSTR
若以上都不成功, 则在对象的编码为RAW的状态下, 至少调用sdsRemoveFreeSpace来移除掉内部SDS中, 闲置的内存空间
 
--   robj *getDecodedObject(robj *o)   返回字符串对象的一个浅拷贝.
在编码为RAW或EMBSTR时, 底层数据引用计数+1, 返回一个共享句柄
在编码为INT时, 返回一个编码为RAW或EMBSTR的新副本的句柄. 新旧对象之间无关
 
--   size_t stringObjectLen(robj *o)   返回字符串对象中的字符个数  
--   int getDoubleFromObject(const robj *o,double *target)   从字符串对象中解析出数值, 兼容整数值  
--   int getLongLongFromObject(robj *o,long long *target)   从字符串对象中解析出整数值, 不兼容浮点数值  
--   int getLongDoubleFromObject(robj *o,long double *target)   从字符串对象中解析出数值, 兼容整数值  
--   int compareStringObjects(robj *a, robj *b)   二进制比较两个字符串对象. 若有字符串对象使用的是INT编码, 则先会把ptr中的数值转化为字符串表达, 然后再去比较  
--   int collateStringObjects(robj *a, robj *b)   底层调用strcoll去比较两个字符串对象. 比较的大小结果受LC_LOCALE的影响  
--   int equalStringObjects(robj *a, robj *b)   字符串判等  
--   #define sdsEncodedObject(objptr)   宏, 判断字符串对象的内部是否为SDS实现. 即编码为RAW或EMBSTR  
3.2 哈希对象

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzpsx.html