哈希对象的底层实现有两种, 一种是dict, 一种是ziplist. 分别对应编码HT与ZIPLIST. 而之前介绍的zipmap这种结构, 虽然也是一种轻量级的字典结构, 且纵使在源代码中有相应的编码宏值, 但遗憾的是, 至Redis 4.0.10, 目前哈希对象的底层编码仍然只有ziplist与dict两种
dict自不必说, 本身就是字典类型, 存储键值对的. 用ziplist作为底层数据结构时, 是将键值对以<key1><value1><key2><value2>...<keyn><valuen>这样的形式存储在ziplist中的. 两种编码内存布局分别如下:
上图中不严谨的地方有:
ziplist中每个entry, 除了键与值本身的二进制数据, 还包括其它字段, 图中没有画出来
dict底层可能持有两个dictht实例
没有画出dict的哈希冲突
需要注意的是: 当采用HT编码, 即使用dict作为哈希对象的底层数据结构时, 键与值均是以sds的形式存储的.
哈希对象的相关接口如下:
分类 API名 功能创建接口 robj *createHashObject(void) 创建一个空哈希对象
底层编码使用ZIPLIST, 即底层使用ziplist
释放接口 void freeHashObject(robj *o) 释放哈希对象
若哈希对象底层使用的是dict, 则调用dictRelease释放这个dict
若哈希对象底层使用的是ziplist, 则直接释放掉这个ziplist占用的连续内存空间
编码转换接口 void hashTypeConvertZiplist(robj *o, int enc) 将哈希对象的编码从ZIPLIST转换为HT, 即底层实现从ziplist转为dict
-- void hashTypeConvert(robj *o, int enc) 转换哈希对象的编码.
虽然接口设计的好像可以在底层编码之间互相转换, 但实际上这个接口的实现, 目前仅支持从ZIPLIST转向HT
-- void hashTypeTryConversion(robj *o,robj **argv,int start,int end) o是一个哈希对象. argv是其它对象的数组.(最好是字符串对象, 且为SDS实现)
这个函数会检查argv数组中, 从start到end之间的所有对象, 如果这些对象中, 但凡有一个对象是字符串对象, 且长度超过了用ziplist实现哈希对象时, ziplist的限长
那么o这个哈希对象的编码就会从ZIPLIST转为HT
读写接口 int hashTypeSet(robj *o,sds field,sds value,int flags) 向哈希对象写入一个键值对.
在底层编码为HT时, flag将影响插入键值对时的具体行为. flag可有标志位 HASH_SET_TAKE_VALUE与HASH_SET_TAKE_FIELD, 若对应位置1, 代表键与值直接引用参数值. 否则代表要调用sdsdup接口拷贝键与值.
在底层编码为ZIPLIST时, 键与值必然会被拷贝
-- int hashTypeExists(robj *o, sds field) 查询指定键在哈希对象中是否存在
-- unsigned long hashTypeLength(const robj *o) 查询哈希对象中的键值对总数
-- int hashTypeGetFromZiplist(robj *o, sds field,unsigned char **vstr,unsigned int *vlen,long long *vll) 从编码为ZIPLIST的哈希对象中, 取出一个键对应的值.
键从field传入, 当值为数值类型时, 值以*vll传出, 当值为二进制类型时, 值以*vstr与*vlen传出
-- sds hashTypeGetFromHashTable(robj *o, sds field) 从编码为HT的哈希对象中, 取出一个键对应的值.
键从field传入, 值以返回值传出. 若值不存在, 返回NULL"
-- "int hashTypeGetValue(robj *o,sds field,unsigned char **vstr,unsigned int *vlen,long long *vll) 取出哈希对象中指定键对应的值. 若值是数值类型, 则以*vll传出, 否则以*vstr与*vlen传出
-- robj *hashTypeGetValueObject(robj *o, sds field) 取出哈希对象中指定键对应的值, 并包装成RedisObject返回. 返回的对象为字符串对象
-- size_t hashTypeGetValueLength(robj *o, sds field) 取出哈希对象中指定键对应的值的长度
-- int hashTypeDelete(robj *o, sds field) 删除哈希对象中的一个键值对. 键不存在时返回0, 成功删除返回1
迭代器接口 hashTypeIterator *hashTypeInitIterator(robj *subject) 在指定哈希对象上创建一个迭代器
-- void hashTypeReleaseIterator(hashTypeIterator *hi) 释放哈希对象的迭代器
-- int hashTypeNext(hashTypeIterator *hi) 让哈希迭代器步进一步
-- void hashTypeCurrentFromZiplist(hashTypeIterator *hi,int what,unsigned char **vstr,unsigned int *vlen,long long *vll) 取出哈希对象迭代器当前指向的键 或值. 当what传入OBJ_HASH_KEY时, 取的是键, 否则取的是值.
注意, 该函数仅在哈希对象的编码为ZIPLIST时才能正确运行
-- sds hashTypeCurrentFromHashTable(hashTypeIterator *hi,int what) 取出哈希对象迭代器当前指向的键 或值. 当what传入OBJ_HASH_KEY时, 取的是键, 否则取的是值.
注意, 该函数仅在哈希对象的编码为HT时才能正确运行
-- void hashTypeCurrentObject(hashTypeIterator *hi,int what,unsigned char **vstr,unsigned int *vlen,long long *vll) 取出哈希对象迭代器当前指向的键或值. 当what传入OBJ_HASH_KEY时, 取的是键, 否则取的是值.
-- sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi,int what) 取出哈希对象迭代器当前指向的键或值. 且把键或值以一个全新的SDS字符串返回. 当what传入OBJ_HASH_KEY时, 取的是键, 否则取的是值.
3.3 列表对象