redis五大类型分析--list(1)

此篇为对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函数,对列表弹出元素和计算列表长度的功能进行分析,同时还分析了关于迭代器初始化,设置方向,释放和获取迭代器下一个元素的函数,对迭代器的功能进行分析。

相关推荐
ketil271 分钟前
Ubuntu 安装 redis
redis
王佑辉1 小时前
【redis】redis缓存和数据库保证一致性的方案
redis·面试
Karoku0662 小时前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
gorgor在码农2 小时前
Redis 热key总结
java·redis·热key
想进大厂的小王2 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构
Java 第一深情2 小时前
高性能分布式缓存Redis-数据管理与性能提升之道
redis·分布式·缓存
minihuabei7 小时前
linux centos 安装redis
linux·redis·centos
monkey_meng9 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
hlsd#10 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
奶糖趣多多12 小时前
Redis知识点
数据库·redis·缓存