redis 当中, sds字符串, adlist双向链表, dict字典, ziplist压缩链表, intset整数集合等均为底层数据结构redis
redis 并无使用这些基本数据结构来实现数据库应用, 而是基于这些底层数据结构之上, 构建了一个对象系统, 全部的操做都是基于对象来进行操做算法
对象结构数据库
// redis 对象结构 typedef struct redisObject { // 类型 unsigned type:4; // 编码 unsigned encoding:4; // 对象最后一次被访问的时间 unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */ // 引用计数 int refcount; // 指向实际值的指针 void *ptr; } robj;
对象类型api
// 对象类型 // 字符串 #define REDIS_STRING 0 // 链表 #define REDIS_LIST 1 // 集合 #define REDIS_SET 2 // 有序集合 #define REDIS_ZSET 3 // 哈希 #define REDIS_HASH 4
对象编码安全
// 对象编码 // 普通的 sds 结构 #define REDIS_ENCODING_RAW 0 /* Raw representation */ // 整型 #define REDIS_ENCODING_INT 1 /* Encoded as integer */ // 哈希 #define REDIS_ENCODING_HT 2 /* Encoded as hash table */ // 压缩图, 没看到有应用 #define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ // 普通链表 #define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ // 压缩链表 #define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ // 整数集合 #define REDIS_ENCODING_INTSET 6 /* Encoded as intset */ // 跳跃表 #define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ // emb 编码的字符串 #define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
引用计数服务器
总结数据结构
redis 的对象系统总共有 5 种类型, 可是在使用上每种类型根据不一样的使用场景会采用不一样的编码, 不一样的对象编码会使用不一样的底层存储结构, 这样作是为了性能考虑, 因为数据量的不一样, 使用不一样的结构对效率的影响不一样dom
对象类型函数
名称 类型 REDIS_STRING 字符串 REDIS_LIST 列表 REDIS_SET 集合 REDIS_ZSET 有序集合 REDIS_HASH 哈希 对象编码性能
名称 编码 REDIS_ENCODING_RAW 普通 sds 字符串 REDIS_ENCODING_INT 整数 REDIS_ENCODING_EMBSTR emb 编码的字符串 REDIS_ENCODING_LINKEDLIST 普通链表 REDIS_ENCODING_ZIPLIST 压缩链表 REDIS_ENCODING_HT 哈希 REDIS_ENCODING_SKIPLIST 跳跃表 REDIS_ENCODING_INTSET 整数集合 REDIS_ENCODING_ZIPMAP 压缩图 对象类型与编码之间的关系
类型 编码 对象 REDIS_STRING REDIS_ENCODING_RAW 普通 sds 字符串 REDIS_STRING REDIS_ENCODING_INT 整数字符串 REDIS_STRING REDIS_ENCODING_EMBSTR emb 编码字符串 REDIS_LIST REDIS_ENCODING_LINKEDLIST 普通双向链表 REDIS_LIST REDIS_ENCODING_ZIPLIST 压缩链表实现的链表结构 REDIS_SET REDIS_ENCODING_HT 哈希结构实现的集合 REDIS_SET REDIS_ENCODING_INTSET 整数集合实现的结合 REDIS_ZSET REDIS_ENCODING_SKIPLIST 跳跃表实现的有序集合 REDIS_ZSET REDIS_ENCODING_ZIPLIST 压缩链表实现的有序集合 REDIS_HASH REDIS_ENCODING_HT 哈希表实现的哈希 REDIS_HASH REDIS_ENCODING_ZIPLIST 压缩链表实现的哈希
字符串对象
long long 整型在 redis 中是使用字符串存储的
// 根据传入的 long long 整数, 建立字符串对象 robj *createStringObjectFromLongLong(long long value) { robj *o; /* * value 大小符合 REDIS_SHARED_INTEGERS 共享整数范围 * 返回一个共享对象 */ if (value >= 0 && value < REDIS_SHARED_INTEGERS) { incrRefCount(shared.integers[value]); o = shared.integers[value]; } else { // 不符合共享范围,建立一个新的整数对象 if (value >= LONG_MIN && value <= LONG_MAX) { // 设置相应的 robj 的值 o = createObject(REDIS_STRING, NULL); o->encoding = REDIS_ENCODING_INT; o->ptr = (void*)((long)value); // 值不能用 long 类型保存(long long 类型),将值转换为字符串, // 并建立一个 REDIS_ENCODING_RAW 的字符串对象来保存值 } else { o = createObject(REDIS_STRING,sdsfromlonglong(value)); } } return o; }
对于整数, redis 有一个共享整数的概念, 用于将一些经常使用范围的整数在 redis 启动时就建立成对象, 后续全部落在这个范围内的整数, 均使用这个共享对象, 节省了大量整数的对象建立过程
共享整数范围阈值为 10000
#define REDIS_SHARED_INTEGERS 10000
整数的编码类型不必定都是 REDIS_ENCODING_INT, 当整数值超过了 long 的范围时, 存储的编码类型会变为 REDIS_ENCODING_RAW
long double 浮点型在 redis 中也是使用字符串存储的
// 将 long double 浮点型存储为字符串 robj *createStringObjectFromLongDouble(long double value) { char buf[256]; int len; // 使用 17 位小数精度,这种精度能够在大部分机器上被 rounding 而不改变 len = snprintf(buf,sizeof(buf),"%.17Lf", value); // 移除尾部的 0 if (strchr(buf,'.') != NULL) { char *p = buf+len-1; while(*p == '0') { p--; len--; } // 若是不须要小数点,那么移除它 if (*p == '.') len--; } // 建立对象 return createStringObject(buf,len); }
字符串须要 2 个结构体, robj 和 sdshdr, robj 用来封装成 redis object, sdshdr 用来存储真正的字符串内容
建立 emb 编码与 raw 编码的字符串有一个阈值 REDIS_ENCODING_EMBSTR_SIZE_LIMIT, 当超过这个阈值, 当小于等于这个阈值时, 以 emb 编码; 不然以 raw 编码, REDIS_ENCODING_EMBSTR_SIZE_LIMIT 值为 39
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39 // 建立字符串对象 robj *createStringObject(char *ptr, size_t len) { // 当小于等于 REDIS_ENCODING_EMBSTR_SIZE_LIMIT时, 以 emb 编码 if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) return createEmbeddedStringObject(ptr,len); else // 不然以 raw 编码 return createRawStringObject(ptr,len); }
建立 emb 编码的字符串仅须要 1 次内存分配, raw 编码的字符串须要 2 次内存分配, 相应的释放内存时也是如此
emb 编码的字符串 robj, sdshdr 内存是连续的, 而 raw 编码的字符串内存是分开的, emb 编码的字符串性能更好
建立 emb 编码字符串
// 建立 emb 编码的字符串 robj *createEmbeddedStringObject(char *ptr, size_t len) { // 分配 robj, sdshdr 内存 robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1); struct sdshdr *sh = (void*)(o+1); // 设置 robj 的值 o->type = REDIS_STRING; o->encoding = REDIS_ENCODING_EMBSTR; o->ptr = sh+1; o->refcount = 1; o->lru = LRU_CLOCK(); // 设置 sdshdr 的值 sh->len = len; sh->free = 0; if (ptr) { memcpy(sh->buf,ptr,len); sh->buf[len] = '\0'; } else { memset(sh->buf,0,len+1); } return o; }
建立 raw 编码字符串
// 建立 raw 编码字符串 robj *createRawStringObject(char *ptr, size_t len) { // sdsnewlen 函数分配了 sdshdr 的内存 return createObject(REDIS_STRING,sdsnewlen(ptr,len)); }
robj *createObject(int type, void *ptr) { // 分配 robj 的内存 robj *o = zmalloc(sizeof(*o)); // 设置 robj 的值 o->type = type; o->encoding = REDIS_ENCODING_RAW; o->ptr = ptr; o->refcount = 1; o->lru = LRU_CLOCK(); return o; }
字符串比较根据不一样的 flag 使用不一样的比较方法
flags
// 二进制安全的方式, 调用 memcmp 函数 #define REDIS_COMPARE_BINARY (1<<0) // 根据 ascii 码比较, 调用 strcoll 函数 #define REDIS_COMPARE_COLL (1<<1)
链表对象
对象嵌套
redis object 里有个对象嵌套的概念, 就是底层存储的数据, 例如字符串, 实际上也是以字符串对象 sdshdr 存在的
redis 链表对象的编码分为 linkedlist (双端链表), ziplist (压缩链表)
redis 链表对象的编码转换
当节点的长度大于 64 字节, 转换为双端链表
void listTypeTryConversion(robj *subject, robj *value) { // 确保 subject 为 ZIPLIST 编码 if (subject->encoding != REDIS_ENCODING_ZIPLIST) return; if (sdsEncodedObject(value) && // 看字符串是否过长 sdslen(value->ptr) > server.list_max_ziplist_value) // 将编码转换为双端链表 listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); }
当链表存储的节点数量大于等于 512 个, 转换为双端链表
void listTypePush(robj *subject, robj *value, int where) { // 是否须要转换编码? listTypeTryConversion(subject,value); if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) >= server.list_max_ziplist_entries) listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); // 省略 ... }
server.list_max_ziplist_value 值在初始化服务器配置时设置为 REDIS_LIST_MAX_ZIPLIST_VALUE
server.list_max_ziplist_entries 值在初始化服务器配置时设置为 REDIS_LIST_MAX_ZIPLIST_ENTRIES
// 链表结构从 ziplist 编码转换为 linkedlist 编码的阈值 // 最大节点数量阈值 #define REDIS_LIST_MAX_ZIPLIST_ENTRIES 512 // 最大字符串长度阈值 #define REDIS_LIST_MAX_ZIPLIST_VALUE 64 void initServerConfig() { // 省略 server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES; server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE; // 省略 ... }
集合对象
redis 集合的编码转换
集合的 intset 编码存储的均为整数
集合的操做 api 在存入 intset 编码时, 均会调用 isObjectRepresentableAsLongLong 函数, 来判断是不是整数集合保存的元素数量不能超过 512 (REDIS_SET_MAX_INTSET_ENTRIES)
int setTypeAdd(robj *subject, robj *value) { // 省略 ... // 添加成功 // 检查集合在添加新元素以后是否须要转换为字典 if (intsetLen(subject->ptr) > server.set_max_intset_entries) setTypeConvert(subject,REDIS_ENCODING_HT); // 省略 ... }
server.set_max_intset_entries, 若集合的元素数量大于此值, 会将 intset 编码转换为 ht 编码
#define REDIS_SET_MAX_INTSET_ENTRIES 512 void initServerConfig() { // 省略 ... server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES; // 省略 ... }
有序集合对象
有序集合结构
// 有序集合 typedef struct zset { // 字典, 键为成员, 值为分值 dict *dict; // 跳跃表, 按分值排序成员 zskiplist *zsl; } zset;
有序集合 ziplist 编码与 skiplist 编码之间的转换
当执行集合命令时, 判断一些规则, 来决定是否使用 skiplist 编码
有序集合保存的元素数量大于 (128) server.zset_max_ziplist_entries
#define REDIS_ZSET_MAX_ZIPLIST_ENTRIES 128
有序集合保存的元素大小大于 (64) server.zset_max_ziplist_value
#define REDIS_ZSET_MAX_ZIPLIST_VALUE 64
命令距离
void zaddGenericCommand(redisClient *c, int incr) { // 省略 ... if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries) zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); if (sdslen(ele->ptr) > server.zset_max_ziplist_value) zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); // 省略 ... }
哈希对象
hash 的 ziplist 编码将键和值分别存放在 ziplist 的 2 个节点, 键在前, 值在后
redis hash 的编码转换
当知足如下 2 个条件时, redis hash 对象会使用 ziplist 编码
当 hash 的键值对数量小于 512 (server.hash_max_ziplist_entries)
#define REDIS_HASH_MAX_ZIPLIST_ENTRIES 512 int hashTypeSet(robj *o, robj *field, robj *value) { // 省略 ... // 检查在添加操做完成以后,是否须要将 ZIPLIST 编码转换成 HT 编码 if (hashTypeLength(o) > server.hash_max_ziplist_entries) hashTypeConvert(o, REDIS_ENCODING_HT); // 省略 ... }
当 hash 的全部键和值的字符串长度均小于 64 字节 (server.hash_max_ziplist_value)
#define REDIS_HASH_MAX_ZIPLIST_VALUE 64 void hashTypeTryConversion(robj *o, robj **argv, int start, int end) { int i; // 若是对象不是 ziplist 编码,那么直接返回 if (o->encoding != REDIS_ENCODING_ZIPLIST) return; // 检查全部输入对象,看它们的字符串值是否超过了指定长度 for (i = start; i <= end; i++) { if (sdsEncodedObject(argv[i]) && sdslen(argv[i]->ptr) > server.hash_max_ziplist_value) { // 将对象的编码转换成 REDIS_ENCODING_HT hashTypeConvert(o, REDIS_ENCODING_HT); break; } } }
类型检查
命令多态
内存回收
对象共享
- 不一样键的值指针指向的是同一个值对象
- 值对象的引用计数 +1
函数 | 做用 | 备注 |
---|---|---|
createObject | 建立 robj 对象 | robj createObject(int type, void ptr) |
createRawStringObject | 建立 raw 编码的字符串对象 | robj createRawStringObject(char ptr, size_t len) |
createEmbeddedStringObject | 建立 emb 压缩算法压缩的字符串对象 | robj createRawStringObject(char ptr, size_t len) |
createStringObject | 建立字符串对象, raw 或 emb 编码 | robj createStringObject(char ptr, size_t len) |
createStringObjectFromLongLong | 建立 int 编码的字符串对象 | robj *createStringObjectFromLongLong(long long value) |
createStringObjectFromLongDouble | 建立浮点数的字符串对象, raw 或 emb 编码 | robj *createStringObjectFromLongDouble(long double value) |
dupStringObject | 复制字符串对象 | robj dupStringObject(robj o) |
createListObject | 建立 linkedlist 编码的列表对象 | robj *createListObject(void) |
createZiplistObject | 建立 ziplist 编码的列表对象 | robj *createZiplistObject(void) |
createSetObject | 建立 ht 编码的集合对象 | robj *createSetObject(void) |
createIntsetObject | 建立 intset 编码的集合对象 | robj *createIntsetObject(void) |
createHashObject | 建立 ziplist 编码的哈希对象 | robj *createHashObject(void) |
createZsetObject | 建立 skiplist 编码的有序集合对象 | robj *createZsetObject(void) |
createZsetZiplistObject | 建立 ziplist 编码的有序集合对象 | robj *createZsetZiplistObject(void) |
freeStringObject | 释放 raw 编码的字符串对象 | void freeStringObject(robj *o) |
freeListObject | 释放链表对象 | void freeListObject(robj *o) |
freeSetObject | 释放集合对象 | void freeSetObject(robj *o) |
freeZsetObject | 释放有序集合对象 | void freeZsetObject(robj *o) |
freeHashObject | 释放哈希对象 | void freeHashObject(robj *o) |
incrRefCount | 为对象的引用加 1 | void incrRefCount(robj *o) |
decrRefCount | 为对象的引用减 1 | void decrRefCount(robj *o) |
decrRefCountVoid | 对象的引用减 1 | void decrRefCountVoid(void *o) |
resetRefCount | 重置对象的 refcount 为 0 | robj resetRefCount(robj obj) |
checkType | 检查对象 o 的类型是否符合 type, 并返回客户端信息 | int checkType(redisClient c, robj o, int type) |
isObjectRepresentableAsLongLong | 对象 o 是否能够表示为 long long 类型, 将转换后的值存入到 llval | int isObjectRepresentableAsLongLong(robj o, long long llval) |
tryObjectEncoding | 尝试对字符串对象进行编码, 以节约内存 | robj tryObjectEncoding(robj o) |
getDecodedObject | 将传入的 robj 对象解析为字符串对象 | robj getDecodedObject(robj o) |
compareStringObjectsWithFlags | 比较字符串大小, 根据传入的 flag 使用不一样的函数比较 | int compareStringObjectsWithFlags(robj a, robj b, int flags) |
compareStringObjects | 以二进制安全方式比较字符串大小 | int compareStringObjects(robj a, robj b) |
collateStringObjects | 以 strcoll 函数比较字符串大小 | int collateStringObjects(robj a, robj b) |
equalStringObjects | 判断两个字符串是否相等 | int equalStringObjects(robj a, robj b) |
stringObjectLen | 获取字符串对象中字符串长度 | size_t stringObjectLen(robj *o) |
getDoubleFromObject | 从字符串对象 o 中获取 double 值, 存入 target | int getDoubleFromObject(robj o, double target) |
getDoubleFromObjectOrReply | 从对象 o 中获取 double 值, 存入 target; 若失败返回给定信息 | int getDoubleFromObjectOrReply(redisClient c, robj o, double target, const char msg) |
getLongDoubleFromObject | 从对象 o 中获取 long double 值, 存入 target | int getLongDoubleFromObject(robj o, long double target) |
getLongDoubleFromObjectOrReply | 从对象 o 中获取 long double 值, 存入 target; 若失败返回给定信息 | int getLongDoubleFromObjectOrReply(redisClient c, robj o, long double target, const char msg) |
getLongLongFromObject | 从对象 o 中获取 long long 值, 存入 target | int getLongLongFromObject(robj o, long long target) |
getLongLongFromObjectOrReply | 从对象 o 中获取 long long 值, 存入 target; 若失败返回给定信息 | int getLongLongFromObjectOrReply(redisClient c, robj o, long long target, const char msg) |
getLongFromObjectOrReply | 从对象 o 中获取 long 值, 存入 target; 若失败返回给定信息 | int getLongFromObjectOrReply(redisClient c, robj o, long target, const char msg) |
strEncoding | 返回给定 encoding 编码的字符串表示 | char *strEncoding(int encoding) |
estimateObjectIdleTime | 计算给定对象的空闲时长 | unsigned long long estimateObjectIdleTime(robj *o) |
objectCommandLookup | OBJECT 命令获取指定 key 的值对象, 不修改 lru | robj objectCommandLookup(redisClient c, robj *key) |
objectCommandLookupOrReply | OBJECT 命令获取指定 key 的之对象, 不修改 lru; 若失败返回给定信息 | robj objectCommandLookupOrReply(redisClient c, robj key, robj reply) |
objectCommand | OBJECT 命令 | void objectCommand(redisClient *c) |
函数 | 做用 | 备注 |
---|---|---|
listTypeTryConversion | 根据插入的 value 值, 看是否须要将链表 subject 从 ziplist 转为 linkedlist | void listTypeTryConversion(robj subject, robj value) |
listTypePush | 向链表 subject 头部或尾部添加值 value | void listTypePush(robj subject, robj value, int where) |
listTypePop | 从链表 subject 头部或尾部弹出节点 | robj listTypePop(robj subject, int where) |
listTypeLength | 获取链表 subject 的长度 | unsigned long listTypeLength(robj *subject) |
listTypeInitIterator | 初始化链表迭代器 | listTypeIterator listTypeInitIterator(robj subject, long index, unsigned char direction) |
listTypeReleaseIterator | 释放链表迭代器 li | void listTypeReleaseIterator(listTypeIterator *li) |
listTypeNext | 获取迭代器当前指向的节点, 将其存入 entry | int listTypeNext(listTypeIterator li, listTypeEntry entry) |
listTypeGet | 返回 entry 结构保存的节点, 转为 robj 结构 | robj listTypeGet(listTypeEntry entry) |
listTypeInsert | 将 value 插入 entry 所在链表节点的前面或后面 | void listTypeInsert(listTypeEntry entry, robj value, int where) |
listTypeEqual | 判断对象 o 与 entry 节点值是否相等 | int listTypeEqual(listTypeEntry entry, robj o) |
listTypeDelete | 删除 entry 节点 | void listTypeDelete(listTypeEntry *entry) |
listTypeConvert | 将 subject 编码转换为 linkedlist | void listTypeConvert(robj *subject, int enc) |
函数 | 做用 | 备注 |
---|---|---|
setTypeCreate | 根据 value 值建立集合 | robj setTypeCreate(robj value) |
setTypeAdd | 将 value 值添加到 subject 集合中 | int setTypeAdd(robj subject, robj value) |
setTypeRemove | 将 value 值从 setobj 集合中删除 | int setTypeRemove(robj setobj, robj value) |
setTypeIsMember | 判断 value 值是否在集合 subject 中 | int setTypeIsMember(robj subject, robj value) |
setTypeInitIterator | 建立指定 subject 集合的迭代器 | setTypeIterator setTypeInitIterator(robj subject) |
setTypeReleaseIterator | 释放集合迭代器 | void setTypeReleaseIterator(setTypeIterator *si) |
setTypeNext | 获取迭代器当前指向的节点, 存入 objele, 返回的对象没有增长引用计数, 对 copy-on-write 友好 | int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) |
setTypeNextObject | 获取迭代器当前指向的节点, 老是返回一个新增的, 或者是引用计数增长的对象, 对 copy-on-write 非友好 | robj setTypeNextObject(setTypeIterator si) |
setTypeRandomElement | 从集合中随机获取一个元素, 若集合编码是 intset, 存到 llele; 若编码是 ht, 存到 objele | int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) |
setTypeSize | 获取集合的元素数量 | unsigned long setTypeSize(robj *subject) |
setTypeConvert | 将集合 setobj 的编码转换为给定的 ht 编码 | void setTypeConvert(robj *setobj, int enc) |
qsortCompareSetsByCardinality | 计算集合 s1 与集合 s2 的元素数量之差 | int qsortCompareSetsByCardinality(const void s1, const void s2) |
qsortCompareSetsByRevCardinality | 计算集合 s2 与集合 s1 的元素数量之差 | int qsortCompareSetsByRevCardinality(const void s1, const void s2) |
函数 | 做用 | 备注 |
---|---|---|
hashTypeTryConversion | 尝试将 ziplist 编码的 hash 对象转换成 ht 编码 | void hashTypeTryConversion(robj *o, robj ** argv, int start, int end) |
hashTypeTryObjectEncoding | 尝试将 ht 编码的键值对字符串对象分别进行编码, 以节省内存 | void hashTypeTryObjectEncoding(robj *subject, robj o1, robj o2) |
hashTypeGetFromZiplist | 从 ziplist 编码的 hash 中获取指定 field 相对应的值 | int hashTypeGetFromZiplist(robj o, robj field, unsigned char ** vstr, unsigned int vlen, long long vll) |
hashTypeGetFromHashTable | 从 ht 编码的 hash 中获取指定 field 相对应的值 | int hashTypeGetFromZiplist(robj o, robj field, unsigned char **vstr, unsigned int vlen, long long vll) |
hashTypeGetObject | 从 hash 中获取指定 field 的值, 返回 robj 对象 | robj hashTypeGetObject(robj o, robj *field) |
hashTypeExists | 判断 hash 中指定 filed 是否存在 | int hashTypeExists(robj o, robj field) |
hashTypeSet | 将键值对添加到 hash 中, 若键已存在, 则使用新值替换旧值 | int hashTypeSet(robj o, robj field, robj *value) |
hashTypeDelete | 从 hash 中删除指定 field 的键值对 | int hashTypeDelete(robj o, robj field) |
hashTypeLength | 返回 hash 中键值对数量 | unsigned long hashTypeLength(robj *o) |
hashTypeInitIterator | 建立 hash 迭代器 | hashTypeIterator hashTypeInitIterator(robj subject) |
hashTypeReleaseIterator | 释放 hash 迭代器 | void hashTypeReleaseIterator(hashTypeIterator *hi) |
hashTypeNext | 获取迭代器下个节点, 存入迭代器 | int hashTypeNext(hashTypeIterator *hi) |
hashTypeCurrentFromZiplist | 从 ziplist 编码的 hash 中, 获取迭代器当前指向的节点的 field 或 value 对象 | void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int vlen, long long vll) |
hashTypeCurrentFromHashTable | 从 ht 编码的 hash 中, 获取迭代器当前指向的节点的 field 或 value 对象 | void hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) |
hashTypeCurrentObject | 获取迭代器当前节点的 field 或 value 对象 | robj hashTypeCurrentObject(hashTypeIterator hi, int what) |
hashTypeLookupWriteOrCreate | 按 key 在数据库中查找并返回相应 hash 对象, 若对象不存在, 建立新 hash 对象并返回 | robj hashTypeLookupWriteOrCreate(redisClient c, robj *key) |
hashTypeConvertZiplist | 将 ziplist 编码的 hash 对象转换成 ht 编码 | void hashTypeConvertZiplist(robj *o, int enc) |
hashTypeConvert | 将 hash 对象 o 转换成指定 enc 编码, 目前仅支持将 ziplist 编码转换为 ht 编码 | void hashTypeConvert(robj *o, int enc) |