从定义上来看, 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, 三种方式的内存布局分别如下:
字符串对象的相关接口如下:
分类 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 哈希对象