结合redis设计与实现的redis源码学习-23-排序(sort.c)

来源:互联网 发布:unity 软件位置 编辑:程序博客网 时间:2024/06/08 19:33

Redis的Sort命令可以对列表键,集合键或者有序集合键值进行排序。也可以使用SORT alphabet来对字符进行排序。

一、sort命令的实现

sort命令会先创建一个和列表长度相同的数组,数组的每个元素都是一个redisSortObject结构,然后遍历数组,将各个obj指针所指向的列表项转换成一个double浮点数,,保存早结构的score中,根据score的值对数组进行排序,遍历数组,将各个项的值返回给客户端。

typedef struct _redisSortObject {    robj *obj;//被排序键的值    union {        double score;//排序时使用        robj *cmpobj;//排序带有by选项的字符串时使用    } u;} redisSortObject;

二、ALPHA选项的实现

使用ALPHA选项可以对包含字符串值的键进行排序。
执行sort ALPHA命令,会先创建一个redisSortObject结构数组,长度等于集合大小,遍历数组,将obj指向每个元素,根据元素对数组进行排序,然后遍历数组,返回客户端。

三、ASC选项和DESC选项的实现

在默认情况下,SORT命令默认升序排序,SORT key 和SORT key ASC完全相同,使用DESC可以让命令执行降序排序。
这里的排序方法都是使用快速排序,只是对比函数返回不同。

四、BY选项的实现

默认情况下,SORT命令使用被排序键包含的元素作为排序的权重,通过使用by选项,可以指定某些字符串键,或者哈希键所包含的某些域来作为元素的权重,对一个键进行排序。
类似模式匹配, SORT BY *-test,会识别所有-test结尾的键进行排序;

五、带有ALPHA选项的BY选项实现

BY选项默认假设权重键保存的是数字,如果是字符串的话,需要在使用BY时加上ALPHA选项。
SORT *-test ALPHA

六、LIMIT选项的实现

在默认情况下,SORT命令会将排序后的所有元素返回客户端。通过limit可以让SORT命令只返回其中一部分已排序的元素:LIMIT 表示跳过的数量和要返回的数量。
SORT BY *-test ALPHA LIMIT 0 3

七、GET选项的实现

GET选项可以让SORT命令在排序之后,根据被排序的元素,以及GET选项指定的模式,查找并返回某些键的值。
SORT ALPHA GET *-name,会返回所有-name结尾的键。
SORT命令可以携带多个GET选项。

八、STORE选项的实现

默认情况下,SORT命令只向客户端返回结果,而不保存排序结果。
通过STORE选项,可以将排序结果保存在执行的键里面。
SORT ALPHA STORE

九、多个选项的执行顺序

命令执行过程:
-1、排序:命令会使用ALPHA,ASC或DESC,BY这几个选项,对输入键进行排序,得到一个结果集;
-2、限制排序结果集的长度:命令会使用LIMIT选项,对结果集的长度进行限制,只有指定的元素会被保留到结果集中;
-3、获取外部键:使用GET选项,根据结果集中的元素以及GET指定的模式,获取指定键的值,并将其作为新的排序结果集;
-4、保存排序结果集:使用STORE选项,将结果集保存到指定的键上;
-5、返回客户端:遍历结果集,返回集合中的元素。
有多个GET选项的话,GET选项的顺序不同会影响结果集的顺序。

看代码

sort.c

#include "server.h"#include "pqsort.h" /* Partial qsort for SORT+LIMIT */#include <math.h> /* isnan() */zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);//创建排序操作redisSortOperation *createSortOperation(int type, robj *pattern) {    redisSortOperation *so = zmalloc(sizeof(*so));    so->type = type;    so->pattern = pattern;    return so;}//根据模式找keyrobj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {    char *p, *f, *k;    sds spat, ssub;    robj *keyobj, *fieldobj = NULL, *o;    int prefixlen, sublen, postfixlen, fieldlen;    /* If the pattern is "#" return the substitution object itself in order to implement the "SORT ... GET #" feature. 如果模式是#,返回替换对象本身实现的特性*/    spat = pattern->ptr;    if (spat[0] == '#' && spat[1] == '\0') {        incrRefCount(subst);        return subst;    }    /* The substitution object may be specially encoded. If so we create a decoded object on the fly. Otherwise getDecodedObject will just increment the ref count, that we'll decrement later. 替代对象可以被专门编码,如果是这样,我们将创建一个解码的对象。否则,只会增加ref的数量,我们稍后再递减*/    subst = getDecodedObject(subst);    ssub = subst->ptr;    /* If we can't find '*' in the pattern we return NULL as to GET a fixed key does not make sense. 如果我们找不到*,那么返回NULL作为get的一个固定的键*/    p = strchr(spat,'*');    if (!p) {        decrRefCount(subst);        return NULL;    }    /* Find out if we're dealing with a hash dereference. 判断我们是否正在处理hash解引用*/    if ((f = strstr(p+1, "->")) != NULL && *(f+2) != '\0') {        fieldlen = sdslen(spat)-(f-spat)-2;        fieldobj = createStringObject(f+2,fieldlen);    } else {        fieldlen = 0;    }    /* Perform the '*' substitution. 执行*替换*/    prefixlen = p-spat;    sublen = sdslen(ssub);    postfixlen = sdslen(spat)-(prefixlen+1)-(fieldlen ? fieldlen+2 : 0);    keyobj = createStringObject(NULL,prefixlen+sublen+postfixlen);    k = keyobj->ptr;    memcpy(k,spat,prefixlen);    memcpy(k+prefixlen,ssub,sublen);    memcpy(k+prefixlen+sublen,p+1,postfixlen);    decrRefCount(subst); /* Incremented by decodeObject() 减少开始的引用*/    /* Lookup substituted key 查找替换件*/    o = lookupKeyRead(db,keyobj);    if (o == NULL) goto noobj;    if (fieldobj) {        if (o->type != OBJ_HASH) goto noobj;        /* Retrieve value from hash by the field name. This operation already increases the refcount of the returned object. 通过字段名称从hash中检索值,这个对象已经增加了引用计数*/        o = hashTypeGetObject(o, fieldobj);    } else {        if (o->type != OBJ_STRING) goto noobj;        /* Every object that this function returns needs to have its refcount increased. sortCommand decreases it again. 每个这个函数返回的对象都需要增加它的refcount,怕旭命令再次减少*/        incrRefCount(o);    }    decrRefCount(keyobj);    if (fieldobj) decrRefCount(fieldobj);    return o;noobj:    decrRefCount(keyobj);    if (fieldlen) decrRefCount(fieldobj);    return NULL;}/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with the additional parameter is not standard but a BSD-specific we have to pass sorting parameters via the global 'server' structure 这个是键的对比函数,给Qsort注入,鉴于具有附加参数的qsort_r不是标准的,我们必须通过全局服务器结构来传递排序参数*/int sortCompare(const void *s1, const void *s2) {    const redisSortObject *so1 = s1, *so2 = s2;    int cmp;    if (!server.sort_alpha) {        /* Numeric sorting. Here it's trivial as we precomputed scores 数字排序,预先计算分数开销微不足道*/        if (so1->u.score > so2->u.score) {            cmp = 1;        } else if (so1->u.score < so2->u.score) {            cmp = -1;        } else {            /* Objects have the same score, but we don't want the comparison to be undefined, so we compare objects lexicographically. This way the result of SORT is deterministic. 对象具有相同的分数,按字典序比较,可以得到确定结果*/            cmp = compareStringObjects(so1->obj,so2->obj);        }    } else {        /* Alphanumeric sorting 字典排序*/        if (server.sort_bypattern) {            if (!so1->u.cmpobj || !so2->u.cmpobj) {                /* At least one compare object is NULL 至少有一个比较对象是NULL*/                if (so1->u.cmpobj == so2->u.cmpobj)                    cmp = 0;                else if (so1->u.cmpobj == NULL)                    cmp = -1;                else                    cmp = 1;            } else {                /* We have both the objects, compare them. 我们有两个对象,比较他们*/                if (server.sort_store) {                    cmp = compareStringObjects(so1->u.cmpobj,so2->u.cmpobj);                } else {                    /* Here we can use strcoll() directly as we are sure that the objects are decoded string objects. 这里我们可以直接使用strcoll(),因为我们确信这些对象是解码后的字符串对象*/                    cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);                }            }        } else {            /* Compare elements directly. 比较元素*/            if (server.sort_store) {                cmp = compareStringObjects(so1->obj,so2->obj);            } else {                cmp = collateStringObjects(so1->obj,so2->obj);            }        }    }    return server.sort_desc ? -cmp : cmp;}/* The SORT command is the most complex command in Redis. Warning: this code is optimized for speed and a bit less for readability sort命令时redis中最复杂的命令,代码对速度进行了优化,可读性较差  ·!·真长*/void sortCommand(client *c) {    list *operations;    unsigned int outputlen = 0;    int desc = 0, alpha = 0;    long limit_start = 0, limit_count = -1, start, end;    int j, dontsort = 0, vectorlen;    int getop = 0; /* GET operation counter */    int int_convertion_error = 0;    int syntax_error = 0;    robj *sortval, *sortby = NULL, *storekey = NULL;    redisSortObject *vector; /* Resulting vector to sort */    /* Lookup the key to sort. It must be of the right types 查找key排序,必须是正确的类型*/    sortval = lookupKeyRead(c->db,c->argv[1]);    if (sortval && sortval->type != OBJ_SET &&                   sortval->type != OBJ_LIST &&                   sortval->type != OBJ_ZSET)    {        addReply(c,shared.wrongtypeerr);        return;    }    /* Create a list of operations to perform for every sorted element. Operations can be GET 为每个元素创建一个操作列表,可以是GET*/    operations = listCreate();    listSetFreeMethod(operations,zfree);    j = 2; /* options start at argv[2] 选项从第二个开始*/    /* Now we need to protect sortval incrementing its count, in the future SORT may have options able to overwrite/delete keys during the sorting and the sorted key itself may get destroyed 现在我们需要保护sortval增加它的计数器,在将来sort可能有选项能够在排序过程中删除键,排序后的键本身可能会被破坏*/    if (sortval)        incrRefCount(sortval);    else        sortval = createQuicklistObject();    /* The SORT command has an SQL-alike syntax, parse it     sort命令有一个类似sql的语法,解析*/    while(j < c->argc) {        int leftargs = c->argc-j-1;        if (!strcasecmp(c->argv[j]->ptr,"asc")) {            desc = 0;        } else if (!strcasecmp(c->argv[j]->ptr,"desc")) {            desc = 1;        } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) {            alpha = 1;        } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) {            if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL)                 != C_OK) ||                (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL)                 != C_OK))            {                syntax_error++;                break;            }            j+=2;        } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) {            storekey = c->argv[j+1];            j++;        } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) {            sortby = c->argv[j+1];            /* If the BY pattern does not contain '*', i.e. it is constant, we don't need to sort nor to lookup the weight keys. 如果BY模式不包含*,即它是不变的,我们不需要排序,也不要查找权重键*/            if (strchr(c->argv[j+1]->ptr,'*') == NULL) {                dontsort = 1;            } else {                /* If BY is specified with a real patter, we can't accept it in cluster mode. 如果BY使用真实的模式指定的,我们不能再集群模式下接受它*/                if (server.cluster_enabled) {                    addReplyError(c,"BY option of SORT denied in Cluster mode.");                    syntax_error++;                    break;                }            }            j++;        } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {            if (server.cluster_enabled) {                addReplyError(c,"GET option of SORT denied in Cluster mode.");                syntax_error++;                break;            }            listAddNodeTail(operations,createSortOperation(                SORT_OP_GET,c->argv[j+1]));            getop++;            j++;        } else {            addReply(c,shared.syntaxerr);            syntax_error++;            break;        }        j++;    }    /* Handle syntax errors set during options parsing. 处理选项解析期间设置的语法错误*/    if (syntax_error) {        decrRefCount(sortval);        listRelease(operations);        return;    }    /* When sorting a set with no sort specified, we must sort the output so the result is consistent across scripting and replication. The other types (list, sorted set) will retain their native order even if no sort order is requested, so they remain stable across scripting and replication. 当对没有指定排序的集合进行排序时,我们必须对输出进行排序,以便跨脚本和复制的结果是一致的,其他类型即使没有请求排序顺序,也将保持本机顺序,因此他们在脚本和复制之间保持稳定*/    if (dontsort &&        sortval->type == OBJ_SET &&        (storekey || c->flags & CLIENT_LUA))    {        /* Force ALPHA sorting */        dontsort = 0;        alpha = 1;        sortby = NULL;    }    /* Destructively convert encoded sorted sets for SORT. 为SORT破坏性地转换编码的有序集合*/    if (sortval->type == OBJ_ZSET)        zsetConvert(sortval, OBJ_ENCODING_SKIPLIST);    /* Objtain the length of the object to sort. 获取要排序对象的长度*/    switch(sortval->type) {    case OBJ_LIST: vectorlen = listTypeLength(sortval); break;    case OBJ_SET: vectorlen =  setTypeSize(sortval); break;    case OBJ_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;    default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */    }    /* Perform LIMIT start,count sanity checking. 执行LIMIT开始,计数完整性检查*/    start = (limit_start < 0) ? 0 : limit_start;    end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;    if (start >= vectorlen) {        start = vectorlen-1;        end = vectorlen-2;    }    if (end >= vectorlen) end = vectorlen-1;    /* Whenever possible, we load elements into the output array in a more direct way. This is possible if:只要有可能,我们以更直接的方式将元素加载到输出数组中,这是可能的如果:1) The object to sort is a sorted set or a list (internally sorted).排序对象是一个有序集合或一个链表2) There is nothing to sort as dontsort is true (BY <constant string>).没有什么可排的 In this special case, if we have a LIMIT option that actually reduces the number of elements to fetch, we also optimize to just load the range we are interested in and allocating a vector that is big enough for the selected range length. 在这种特殊情况下,如果我们有一个LIMIT选项,实际上减少了要获取的元素的数量,我们也优化为只加载我们感兴趣的范围,并为选定的范围长度分配一个足够大的向量*/    if ((sortval->type == OBJ_ZSET || sortval->type == OBJ_LIST) &&        dontsort &&        (start != 0 || end != vectorlen-1))    {        vectorlen = end-start+1;    }    /* Load the sorting vector with all the objects to sort 开辟一个向量空间存储所有的对象*/    vector = zmalloc(sizeof(redisSortObject)*vectorlen);    j = 0;    if (sortval->type == OBJ_LIST && dontsort) {        /* Special handling for a list, if 'dontsort' is true. This makes sure we return elements in the list original ordering, accordingly to DESC / ASC options.如果 dontsort是真的,对列表键进行特殊处理,确保我们返回列表中的元素原来的顺序。 Note that in this case we also handle LIMIT here in a directway, just getting the required range, as an optimization. 在这种情况下,我们也在这里直接处理LIMIT,知识获得所需的范围,作为优化*/        if (end >= start) {            listTypeIterator *li;            listTypeEntry entry;            li = listTypeInitIterator(sortval,                    desc ? (long)(listTypeLength(sortval) - start - 1) : start,                    desc ? LIST_HEAD : LIST_TAIL);            while(j < vectorlen && listTypeNext(li,&entry)) {                vector[j].obj = listTypeGet(&entry);                vector[j].u.score = 0;                vector[j].u.cmpobj = NULL;                j++;            }            listTypeReleaseIterator(li);            /* Fix start/end: output code is not aware of this optimization. 修复开始/结束:输出代码不知道这个优化*/            end -= start;            start = 0;        }    } else if (sortval->type == OBJ_LIST) {        listTypeIterator *li = listTypeInitIterator(sortval,0,LIST_TAIL);        listTypeEntry entry;        while(listTypeNext(li,&entry)) {            vector[j].obj = listTypeGet(&entry);            vector[j].u.score = 0;            vector[j].u.cmpobj = NULL;            j++;        }        listTypeReleaseIterator(li);    } else if (sortval->type == OBJ_SET) {        setTypeIterator *si = setTypeInitIterator(sortval);        robj *ele;        while((ele = setTypeNextObject(si)) != NULL) {            vector[j].obj = ele;            vector[j].u.score = 0;            vector[j].u.cmpobj = NULL;            j++;        }        setTypeReleaseIterator(si);    } else if (sortval->type == OBJ_ZSET && dontsort) {        /* Special handling for a sorted set, if 'dontsort' is true. This makes sure we return elements in the sorted set original ordering, accordingly to DESC / ASC options.如果dontsorttrue,则对有序集合进行特殊处理,这确保我们返回有序集合原始顺序中的元素。 Note that in this case we also handle LIMIT here in a direct way, just getting the required range, as an optimization. 在这种情况下,我们也直接在这里处理LIMIT,只是获得所需的范围,作为优化*/        zset *zs = sortval->ptr;        zskiplist *zsl = zs->zsl;        zskiplistNode *ln;        robj *ele;        int rangelen = vectorlen;        /* Check if starting point is trivial, before doing log(N) lookup. 在进行日志查找之前,检查起始点是否微不足道*/        if (desc) {            long zsetlen = dictSize(((zset*)sortval->ptr)->dict);            ln = zsl->tail;            if (start > 0)                ln = zslGetElementByRank(zsl,zsetlen-start);        } else {            ln = zsl->header->level[0].forward;            if (start > 0)                ln = zslGetElementByRank(zsl,start+1);        }        while(rangelen--) {            serverAssertWithInfo(c,sortval,ln != NULL);            ele = ln->obj;            vector[j].obj = ele;            vector[j].u.score = 0;            vector[j].u.cmpobj = NULL;            j++;            ln = desc ? ln->backward : ln->level[0].forward;        }        /* Fix start/end: output code is not aware of this optimization. */        end -= start;        start = 0;    } else if (sortval->type == OBJ_ZSET) {        dict *set = ((zset*)sortval->ptr)->dict;        dictIterator *di;        dictEntry *setele;        di = dictGetIterator(set);        while((setele = dictNext(di)) != NULL) {            vector[j].obj = dictGetKey(setele);            vector[j].u.score = 0;            vector[j].u.cmpobj = NULL;            j++;        }        dictReleaseIterator(di);    } else {        serverPanic("Unknown type");    }    serverAssertWithInfo(c,sortval,j == vectorlen);    /* Now it's time to load the right scores in the sorting vector 现在在分类向量中加载正确的分数*/    if (dontsort == 0) {        for (j = 0; j < vectorlen; j++) {            robj *byval;            if (sortby) {                /* lookup value to sort by 查找值在对象中*/                byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);                if (!byval) continue;            } else {                /* use object itself to sort by 使用对象本身来排序*/                byval = vector[j].obj;            }            if (alpha) {                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);            } else {                if (sdsEncodedObject(byval)) {                    char *eptr;                    vector[j].u.score = strtod(byval->ptr,&eptr);                    if (eptr[0] != '\0' || errno == ERANGE ||                        isnan(vector[j].u.score))                    {                        int_convertion_error = 1;                    }                } else if (byval->encoding == OBJ_ENCODING_INT) {                    /* Don't need to decode the object if it's integer-encoded (the only encoding supported) so far. We can just cast it 如果目标是整数编码,不需要解码对象,直接释放*/                    vector[j].u.score = (long)byval->ptr;                } else {                    serverAssertWithInfo(c,sortval,1 != 1);                }            }            /* when the object was retrieved using lookupKeyByPattern, its refcount needs to be decreased. */            if (sortby) {                decrRefCount(byval);            }        }    }    if (dontsort == 0) {        server.sort_desc = desc;        server.sort_alpha = alpha;        server.sort_bypattern = sortby ? 1 : 0;        server.sort_store = storekey ? 1 : 0;        if (sortby && (start != 0 || end != vectorlen-1))            pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);        else            qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);    }    /* Send command output to the output buffer, performing the specified GET/DEL/INCR/DECR operations if any. 发送命令到输出缓冲区,执行指定的GET/DEL/INCR/DECR操作*/    outputlen = getop ? getop*(end-start+1) : end-start+1;    if (int_convertion_error) {        addReplyError(c,"One or more scores can't be converted into double");    } else if (storekey == NULL) {        /* STORE option not specified, sent the sorting result to client 没有指定STORE选项,将结果发送给客户端*/        addReplyMultiBulkLen(c,outputlen);        for (j = start; j <= end; j++) {            listNode *ln;            listIter li;            if (!getop) addReplyBulk(c,vector[j].obj);            listRewind(operations,&li);            while((ln = listNext(&li))) {                redisSortOperation *sop = ln->value;                robj *val = lookupKeyByPattern(c->db,sop->pattern,                    vector[j].obj);                if (sop->type == SORT_OP_GET) {                    if (!val) {                        addReply(c,shared.nullbulk);                    } else {                        addReplyBulk(c,val);                        decrRefCount(val);                    }                } else {                    /* Always fails */                    serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET);                }            }        }    } else {        robj *sobj = createQuicklistObject();        /* STORE option specified, set the sorting result as a List object 将结果存入链表中*/        for (j = start; j <= end; j++) {            listNode *ln;            listIter li;            if (!getop) {                listTypePush(sobj,vector[j].obj,LIST_TAIL);            } else {                listRewind(operations,&li);                while((ln = listNext(&li))) {                    redisSortOperation *sop = ln->value;                    robj *val = lookupKeyByPattern(c->db,sop->pattern,                        vector[j].obj);                    if (sop->type == SORT_OP_GET) {                        if (!val) val = createStringObject("",0);                        /* listTypePush does an incrRefCount, so we should take care care of the incremented refcount caused by either lookupKeyByPattern or createStringObject("",0) */                        listTypePush(sobj,val,LIST_TAIL);                        decrRefCount(val);                    } else {                        /* Always fails */                        serverAssertWithInfo(c,sortval,sop->type == SORT_OP_GET);                    }                }            }        }        if (outputlen) {            setKey(c->db,storekey,sobj);            notifyKeyspaceEvent(NOTIFY_LIST,"sortstore",storekey,                                c->db->id);            server.dirty += outputlen;        } else if (dbDelete(c->db,storekey)) {            signalModifiedKey(c->db,storekey);            notifyKeyspaceEvent(NOTIFY_GENERIC,"del",storekey,c->db->id);            server.dirty++;        }        decrRefCount(sobj);        addReplyLongLong(c,outputlen);    }    /* Cleanup */    if (sortval->type == OBJ_LIST || sortval->type == OBJ_SET)        for (j = 0; j < vectorlen; j++)            decrRefCount(vector[j].obj);    decrRefCount(sortval);    listRelease(operations);    for (j = 0; j < vectorlen; j++) {        if (alpha && vector[j].u.cmpobj)            decrRefCount(vector[j].u.cmpobj);    }    zfree(vector);}
阅读全文
0 0
原创粉丝点击