此篇为对redis五大数据类型中list的分析,希望能有所帮助
List API
listTypePush函数
cpp
void listTypePush(robj *subject, robj *value, int where) {
/* 检查编码类型是否为 quicklist (快速列表) */
if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
/* 根据参数 where 选择插入位置,由 pos 保存插入位置信息 */
int pos = (where == LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;
/* value 为整数编码 */
if (value->encoding == OBJ_ENCODING_INT) {
/* 将 value 先转换为字符串 */
char buf[32];
ll2string(buf, 32, (long)value->ptr);
/* 将元素插入列表 */
quicklistPush(subject->ptr, buf, strlen(buf), pos);
/* value 为字符串编码 */
} else {
quicklistPush(subject->ptr, value->ptr, sdslen(value->ptr), pos);
}
} else {
serverPanic("Unknown list encoding");
}
}
分析:
该函数将一个元素插入到指定的列表对象 'subject', 插入位置由 'where' 决定是在列表头部还是尾部插入,调用者不需要自己来增加 'value' 的 refcount,该函数会负责处理。
作用:
实现命令函数pushGenericCommand中会用到
cpp
/* 读取输入的元素,并向列表插入元素 */
for (j = 2; j < c->argc; j++) {
listTypePush(lobj,c->argv[j],where);
/* 脏数据 + 1,即缓存中未保存到本地的数据 */
server.dirty++;
}
实现 lmove 命令中向目的列表(destination)插入元素中会用到
cpp
/* 向列表中插入元素 */
listTypePush(dstobj,value,where);
listPopSaver函数(用于给列表弹出的元素创建副本的函数)
cpp
void *listPopSaver(unsigned char *data, size_t sz) {
return createStringObject((char*)data,sz);
}
示例:
给quicklistPopCustom传参用到
cpp
/* 从列表中弹出元素 */
if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value,
NULL, &vlong, listPopSaver))
listTypePop函数(弹出列表元素的通用实现函数)
cpp
robj *listTypePop(robj *subject, int where) {
long long vlong;
robj *value = NULL;
/* 根据参数 where 选择插入位置,由 ql_where 保存插入位置信息 */
int ql_where = where == LIST_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL;
/* 检查编码类型是否为 quicklist (快速列表) */
if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
/* 从列表中弹出元素 */
if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value,
NULL, &vlong, listPopSaver)) {
/* 如果 value 为 NULL ,则弹出元素被保存在 vlong 中(若不为 NULL 则说明保存在 value 中)
* 则用 vlong 创建一个字符串对象并让 value 对其引用 */
if (!value)
value = createStringObjectFromLongLong(vlong);
}
} else {
serverPanic("Unknown list encoding");
}
return value;
}
示例:
popGenericCommand函数,弹出一个元素
cpp
/* 弹出一个元素,这是 pop 的原始操作,会向客户端回复一个字符串 */
value = listTypePop(o,where);
listTypeLength函数(获取列表长度(元素总数))
cpp
unsigned long listTypeLength(const robj *subject) {
if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
return quicklistCount(subject->ptr);
} else {
serverPanic("Unknown list encoding");
}
}
listTypeInitIterator函数(在指定的索引处初始化一个列表迭代器)
cpp
listTypeIterator *listTypeInitIterator(robj *subject, long index,
unsigned char direction) {
/* 给列表迭代器分配内存空间 */
listTypeIterator *li = zmalloc(sizeof(listTypeIterator));
/* 初始化列表迭代器 */
li->subject = subject;
li->encoding = subject->encoding;
li->direction = direction;
li->iter = NULL;
/* LIST_HEAD means start at TAIL and move *towards* head.
* LIST_TAIL means start at HEAD and move *towards* tail. */
/* LIST_HEAD 意味着从列表尾部开始,并向头部移动。
* LIST_TAIL 表示从列表头部开始,并向尾部移动 */
int iter_direction =
direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD;
/* 检查编码类型是否为 quicklist (快速列表) */
if (li->encoding == OBJ_ENCODING_QUICKLIST) {
/* 初始化一个 quicklist 节点的迭代器,并且该迭代器指向列表的第 index 个元素,
* 虽然用词是"指向"但是迭代器并不是一个指针,而是个结构体,它记录的元素信息是列表第 index 个元素 */
li->iter = quicklistGetIteratorAtIdx(li->subject->ptr,
iter_direction, index);
} else {
serverPanic("Unknown list encoding");
}
return li;
}
用处:
linsertCommand函数中用于遍历查找,初始化迭代器;在addListRangeReply函数中初始化范围起点位置的迭代器
listTypeSetIteratorDirection函数(设置迭代器的方向 )
cpp
void listTypeSetIteratorDirection(listTypeIterator *li, unsigned char direction) {
li->direction = direction;
int dir = direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD;
quicklistSetDirection(li->iter, dir);
}
listTypeReleaseIterator函数(释放迭代器)
cpp
void listTypeReleaseIterator(listTypeIterator *li) {
quicklistReleaseIterator(li->iter);
zfree(li);//redis定义的一个函数,在free函数的基础上有改变
}
基本上跟在listTypeInitIterator函数后面,释放迭代器
listTypeNext函数
cpp
int listTypeNext(listTypeIterator *li, listTypeEntry *entry) {
/* Protect from converting when iterating */
/* 保护迭代时编码不被转换,则需要迭代器记录的编码类型和迭代器指向的列表对象编码一致 */
serverAssert(li->subject->encoding == li->encoding);
/* 将参数 li 赋值给参数 entry 的迭代器成员 */
entry->li = li;
/* 检查编码类型是否为 quicklist (快速列表) */
if (li->encoding == OBJ_ENCODING_QUICKLIST) {
/* 调用 quicklistNext 函数获取下一个元素,
* 元素 (quicklistEntry) 保存在 entry->entry 中,并推进迭代器位置
* 如果 下一个元素存在,该函数返回1,否则返回0 */
return quicklistNext(li->iter, &entry->entry);
} else {
serverPanic("Unknown list encoding");
}
return 0;
}
分析:
获取迭代器指向元素的下一个元素,并推进迭代器的位置(推进方向由迭代器成员 direction 决定)。 如果 entry(下一个元素) 存在,返回1,否则返回0
用处:
linsertCommand函数中用于遍历查找,在addListRangeReply函数中设置迭代器的范围起点位置
总结:
本篇分析了listpush,popsaver,poplength函数,对列表弹出元素和计算列表长度的功能进行分析,同时还分析了关于迭代器初始化,设置方向,释放和获取迭代器下一个元素的函数,对迭代器的功能进行分析。