redis zincrby命令如何做到键不存在时不add

来源:互联网 发布:阿里云合作公司有哪些 编辑:程序博客网 时间:2024/06/04 22:51

最近在用redis对一个固定集合A里面的键进行计数排序,计划使用redis的zset有序集合实现。

一开始想着把A里面的元素全部初始化时全部加入zset,然后读取原始输入数据,直接对于每条输入数据使用zincrby命令进行计数;

本以为zset里面原来没有的键执行这样zincrby不会实际计数,只有原先zset里面已有的键才会计数;

可惜

Increments the score of member in the sorted set stored at key by increment.

If member does not exist in the sorted set, it is added withincrement as its score (as if its previous score was 0.0).

If key does not exist, a new sorted set with the specifiedmember as its sole member is created.

这就悲催了,没法实现啊

可以每次计数之前都判断集合中是否存在

但是zset不像set,没有直接判断元素是否存在的命令SISMEMBER,不过可以用ZSCORE实现,如果不存在则返回nil

太费劲了,而且没法批量操作,性能估计也不好,

然后想想redis代码也不多,就看看代码怎么实现的吧,肯定有判断是否存在的地方,不行就改改,如果不存在就不add了。

分析了一下代码,发现好像redis里面是有这种考虑的,如果不存在则不操作,而且有option可以控制,可是不知道为啥没有对外提供接口,或者是我没找到?

下面贴代码

首先看zset的代码t_zset.c中zincrby的实现

void zincrbyCommand(client *c) {    zaddGenericCommand(c,ZADD_INCR);}
/* This generic command implements both ZADD and ZINCRBY. */#define ZADD_NONE 0#define ZADD_INCR (1<<0)    /* Increment the score instead of setting it. */#define ZADD_NX (1<<1)      /* Don't touch elements not already existing. */#define ZADD_XX (1<<2)      /* Only touch elements already exisitng. */#define ZADD_CH (1<<3)      /* Return num of elements added or updated. */void zaddGenericCommand(client *c, int flags) {    static char *nanerr = "resulting score is not a number (NaN)";    robj *key = c->argv[1];    robj *ele;    robj *zobj;    robj *curobj;    double score = 0, *scores = NULL, curscore = 0.0;    int j, elements;    int scoreidx = 0;    /* The following vars are used in order to track what the command actually     * did during the execution, to reply to the client and to trigger the     * notification of keyspace change. */    int added = 0;      /* Number of new elements added. */    int updated = 0;    /* Number of elements with updated score. */    int processed = 0;  /* Number of elements processed, may remain zero with                           options like XX. */    /* Parse options. At the end 'scoreidx' is set to the argument position     * of the score of the first score-element pair. */    scoreidx = 2;    while(scoreidx < c->argc) {        char *opt = c->argv[scoreidx]->ptr;        if (!strcasecmp(opt,"nx")) flags |= ZADD_NX;        else if (!strcasecmp(opt,"xx")) flags |= ZADD_XX;        else if (!strcasecmp(opt,"ch")) flags |= ZADD_CH;        else if (!strcasecmp(opt,"incr")) flags |= ZADD_INCR;        else break;        scoreidx++;    }    /* Turn options into simple to check vars. */    int incr = (flags & ZADD_INCR) != 0;    int nx = (flags & ZADD_NX) != 0;    int xx = (flags & ZADD_XX) != 0;    int ch = (flags & ZADD_CH) != 0;    /* After the options, we expect to have an even number of args, since     * we expect any number of score-element pairs. */    elements = c->argc-scoreidx;    if (elements % 2 || !elements) {        addReply(c,shared.syntaxerr);        return;    }    elements /= 2; /* Now this holds the number of score-element pairs. */    /* Check for incompatible options. */    if (nx && xx) {        addReplyError(c,            "XX and NX options at the same time are not compatible");        return;    }    if (incr && elements > 1) {        addReplyError(c,            "INCR option supports a single increment-element pair");        return;    }    /* Start parsing all the scores, we need to emit any syntax error     * before executing additions to the sorted set, as the command should     * either execute fully or nothing at all. */    scores = zmalloc(sizeof(double)*elements);    for (j = 0; j < elements; j++) {        if (getDoubleFromObjectOrReply(c,c->argv[scoreidx+j*2],&scores[j],NULL)            != C_OK) goto cleanup;    }    /* Lookup the key and create the sorted set if does not exist. */    zobj = lookupKeyWrite(c->db,key);    if (zobj == NULL) {        if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */        if (server.zset_max_ziplist_entries == 0 ||            server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr))        {            zobj = createZsetObject();        } else {            zobj = createZsetZiplistObject();        }        dbAdd(c->db,key,zobj);    } else {        if (zobj->type != OBJ_ZSET) {            addReply(c,shared.wrongtypeerr);            goto cleanup;        }    }    for (j = 0; j < elements; j++) {        score = scores[j];        if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {            unsigned char *eptr;            /* Prefer non-encoded element when dealing with ziplists. */            ele = c->argv[scoreidx+1+j*2];            if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {                if (nx) continue;                if (incr) {                    score += curscore;                    if (isnan(score)) {                        addReplyError(c,nanerr);                        goto cleanup;                    }                }                /* Remove and re-insert when score changed. */                if (score != curscore) {                    zobj->ptr = zzlDelete(zobj->ptr,eptr);                    zobj->ptr = zzlInsert(zobj->ptr,ele,score);                    server.dirty++;                    updated++;                }                processed++;            } else if (!xx) {                /* Optimize: check if the element is too large or the list                 * becomes too long *before* executing zzlInsert. */                zobj->ptr = zzlInsert(zobj->ptr,ele,score);                if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)                    zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);                if (sdslen(ele->ptr) > server.zset_max_ziplist_value)                    zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);                server.dirty++;                added++;                processed++;            }        } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {            zset *zs = zobj->ptr;            zskiplistNode *znode;            dictEntry *de;            ele = c->argv[scoreidx+1+j*2] =                tryObjectEncoding(c->argv[scoreidx+1+j*2]);            de = dictFind(zs->dict,ele);            if (de != NULL) {                if (nx) continue;                curobj = dictGetKey(de);                curscore = *(double*)dictGetVal(de);                if (incr) {                    score += curscore;                    if (isnan(score)) {                        addReplyError(c,nanerr);                        /* Don't need to check if the sorted set is empty                         * because we know it has at least one element. */                        goto cleanup;                    }                }                /* Remove and re-insert when score changed. We can safely                 * delete the key object from the skiplist, since the                 * dictionary still has a reference to it. */                if (score != curscore) {                    serverAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj));                    znode = zslInsert(zs->zsl,score,curobj);                    incrRefCount(curobj); /* Re-inserted in skiplist. */                    dictGetVal(de) = &znode->score; /* Update score ptr. */                    server.dirty++;                    updated++;                }                processed++;            } else if (!xx) {                znode = zslInsert(zs->zsl,score,ele);                incrRefCount(ele); /* Inserted in skiplist. */                serverAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);                incrRefCount(ele); /* Added to dictionary. */                server.dirty++;                added++;                processed++;            }        } else {            serverPanic("Unknown sorted set encoding");        }    }reply_to_client:    if (incr) { /* ZINCRBY or INCR option. */        if (processed)            addReplyDouble(c,score);        else            addReply(c,shared.nullbulk);    } else { /* ZADD. */        addReplyLongLong(c,ch ? added+updated : added);    }cleanup:    zfree(scores);    if (added || updated) {        signalModifiedKey(c->db,key);        notifyKeyspaceEvent(NOTIFY_ZSET,            incr ? "zincr" : "zadd", key, c->db->id);    }}

zincrbyCommand中调用了zaddGenericCommand,从zaddGenericCommand的注释“/* This generic command implements both ZADD and ZINCRBY. */”可以看出ZINCRBY和ZADD命令用的相同的函数,但是从开始的几个宏定义

#define ZADD_INCR (1<<0)    /* Increment the score instead of setting it. */
#define ZADD_NX (1<<1)      /* Don't touch elements not already existing. */
#define ZADD_XX (1<<2)      /* Only touch elements already exisitng. */

可以看出应该是可以对incry的行为进行控制的,如果不存在则不处理

往下分析可以看到

/* Lookup the key and create the sorted set if does not exist. */    zobj = lookupKeyWrite(c->db,key);    if (zobj == NULL) {        if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */
就是说如果xx被设置了,那么当key不存在的时候则nothing to do

aha 就是这里,找找怎么设置xx

可以轻松发现是通过client里面argv里的参数进行控制的

char *opt = c->argv[scoreidx]->ptr;
else if (!strcasecmp(opt,"xx")) flags |= ZADD_XX;
int xx = (flags & ZADD_XX) != 0;
那么问题就是找到c->argv了

跟踪发现argv是server.c中readQueryFromClient函数读取的客户端传来的数据中解析得到的

nread = read(fd, c->querybuf+qblen, readlen);

函数调用关系是

readQueryFromClient->processInputBuffer->processInlineBuffer-> || querylen = newline-(c->querybuf);
                                                                              |                                            || aux = sdsnewlen(c->querybuf,querylen);
                                                                              |                                           || argv = sdssplitargs(aux,&argc);

                                                                              |--->processCommand->call->"c->cmd->proc(c)"

最后的proc就是对应命令的执行函数,也就是zincrbyCommand

也就是说这个“xx”是客户端传过来的选项,但是zincrby命令明确要求只能有三个参数啊,输多了会报错wrong number of arguments,gdb attach redis-server进程看了一个argv[0]就是命令zincrby,后面依次三个参数,也不能多了啊

看代码

    scoreidx = 2;    while(scoreidx < c->argc) {        char *opt = c->argv[scoreidx]->ptr;        if (!strcasecmp(opt,"nx")) flags |= ZADD_NX;        else if (!strcasecmp(opt,"xx")) flags |= ZADD_XX;        else if (!strcasecmp(opt,"ch")) flags |= ZADD_CH;        else if (!strcasecmp(opt,"incr")) flags |= ZADD_INCR;        else break;        scoreidx++;    }

scoreidx=2,从argv[2]开始判断,如果argv[2]不等于nx,xx,ch,或者incr就直接break了,所以xx只能放在argv[2]

zincrby key incr value 命令中argv[2]是incr,所以我需要把xx放到incr前面,但是又要突破参数个数的限制

搜了一下wrong number of arguments错误提示在processCommand函数里面

} else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||               (c->argc < -c->cmd->arity)) {        flagTransaction(c);        addReplyErrorFormat(c,"wrong number of arguments for '%s' command",            c->cmd->name);        return C_OK;    }

这里c->cmd->arity是关键,起到限制作用,arity中文含义就是参数数量,c->cmd结构体是 struct redisCommand

分析发现所以命令都存在server.c文件开头

struct redisCommand redisCommandTable[] = {    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},    {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},    {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},    {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},    {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0},    {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},    {"strlen",strlenCommand,2,"rF",0,NULL,1,1,1,0,0},    {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},    {"exists",existsCommand,-2,"rF",0,NULL,1,-1,1,0,0},    {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},    {"getbit",getbitCommand,3,"rF",0,NULL,1,1,1,0,0},    {"bitfield",bitfieldCommand,-2,"wm",0,NULL,1,1,1,0,0},    {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},    {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},    {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},    {"incr",incrCommand,2,"wmF",0,NULL,1,1,1,0,0},    {"decr",decrCommand,2,"wmF",0,NULL,1,1,1,0,0},    {"mget",mgetCommand,-2,"r",0,NULL,1,-1,1,0,0},    {"rpush",rpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},    {"lpush",lpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},    {"rpushx",rpushxCommand,3,"wmF",0,NULL,1,1,1,0,0},    {"lpushx",lpushxCommand,3,"wmF",0,NULL,1,1,1,0,0},    {"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},    {"rpop",rpopCommand,2,"wF",0,NULL,1,1,1,0,0},    {"lpop",lpopCommand,2,"wF",0,NULL,1,1,1,0,0},    {"brpop",brpopCommand,-3,"ws",0,NULL,1,1,1,0,0},    {"brpoplpush",brpoplpushCommand,4,"wms",0,NULL,1,2,1,0,0},    {"blpop",blpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},    {"llen",llenCommand,2,"rF",0,NULL,1,1,1,0,0},    {"lindex",lindexCommand,3,"r",0,NULL,1,1,1,0,0},    {"lset",lsetCommand,4,"wm",0,NULL,1,1,1,0,0},    {"lrange",lrangeCommand,4,"r",0,NULL,1,1,1,0,0},    {"ltrim",ltrimCommand,4,"w",0,NULL,1,1,1,0,0},    {"lrem",lremCommand,4,"w",0,NULL,1,1,1,0,0},    {"rpoplpush",rpoplpushCommand,3,"wm",0,NULL,1,2,1,0,0},    {"sadd",saddCommand,-3,"wmF",0,NULL,1,1,1,0,0},    {"srem",sremCommand,-3,"wF",0,NULL,1,1,1,0,0},    {"smove",smoveCommand,4,"wF",0,NULL,1,2,1,0,0},    {"sismember",sismemberCommand,3,"rF",0,NULL,1,1,1,0,0},    {"scard",scardCommand,2,"rF",0,NULL,1,1,1,0,0},    {"spop",spopCommand,-2,"wRF",0,NULL,1,1,1,0,0},    {"srandmember",srandmemberCommand,-2,"rR",0,NULL,1,1,1,0,0},    {"sinter",sinterCommand,-2,"rS",0,NULL,1,-1,1,0,0},    {"sinterstore",sinterstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},    {"sunion",sunionCommand,-2,"rS",0,NULL,1,-1,1,0,0},    {"sunionstore",sunionstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},    {"sdiff",sdiffCommand,-2,"rS",0,NULL,1,-1,1,0,0},    {"sdiffstore",sdiffstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},    {"smembers",sinterCommand,2,"rS",0,NULL,1,1,1,0,0},    {"sscan",sscanCommand,-3,"rR",0,NULL,1,1,1,0,0},    {"zadd",zaddCommand,-4,"wmF",0,NULL,1,1,1,0,0},    {"zincrby",zincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},    {"zrem",zremCommand,-3,"wF",0,NULL,1,1,1,0,0},    {"zremrangebyscore",zremrangebyscoreCommand,4,"w",0,NULL,1,1,1,0,0},    {"zremrangebyrank",zremrangebyrankCommand,4,"w",0,NULL,1,1,1,0,0},    {"zremrangebylex",zremrangebylexCommand,4,"w",0,NULL,1,1,1,0,0},    {"zunionstore",zunionstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},    {"zinterstore",zinterstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},    {"zrange",zrangeCommand,-4,"r",0,NULL,1,1,1,0,0},    {"zrangebyscore",zrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},    {"zrevrangebyscore",zrevrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},    {"zrangebylex",zrangebylexCommand,-4,"r",0,NULL,1,1,1,0,0},    {"zrevrangebylex",zrevrangebylexCommand,-4,"r",0,NULL,1,1,1,0,0},    {"zcount",zcountCommand,4,"rF",0,NULL,1,1,1,0,0},    {"zlexcount",zlexcountCommand,4,"rF",0,NULL,1,1,1,0,0},    {"zrevrange",zrevrangeCommand,-4,"r",0,NULL,1,1,1,0,0},    {"zcard",zcardCommand,2,"rF",0,NULL,1,1,1,0,0},    {"zscore",zscoreCommand,3,"rF",0,NULL,1,1,1,0,0},    {"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0},    {"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0},    {"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0},    {"hset",hsetCommand,4,"wmF",0,NULL,1,1,1,0,0},    {"hsetnx",hsetnxCommand,4,"wmF",0,NULL,1,1,1,0,0},    {"hget",hgetCommand,3,"rF",0,NULL,1,1,1,0,0},    {"hmset",hmsetCommand,-4,"wm",0,NULL,1,1,1,0,0},    {"hmget",hmgetCommand,-3,"r",0,NULL,1,1,1,0,0},    {"hincrby",hincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},    {"hincrbyfloat",hincrbyfloatCommand,4,"wmF",0,NULL,1,1,1,0,0},    {"hdel",hdelCommand,-3,"wF",0,NULL,1,1,1,0,0},    {"hlen",hlenCommand,2,"rF",0,NULL,1,1,1,0,0},    {"hstrlen",hstrlenCommand,3,"rF",0,NULL,1,1,1,0,0},    {"hkeys",hkeysCommand,2,"rS",0,NULL,1,1,1,0,0},    {"hvals",hvalsCommand,2,"rS",0,NULL,1,1,1,0,0},    {"hgetall",hgetallCommand,2,"r",0,NULL,1,1,1,0,0},    {"hexists",hexistsCommand,3,"rF",0,NULL,1,1,1,0,0},    {"hscan",hscanCommand,-3,"rR",0,NULL,1,1,1,0,0},    {"incrby",incrbyCommand,3,"wmF",0,NULL,1,1,1,0,0},    {"decrby",decrbyCommand,3,"wmF",0,NULL,1,1,1,0,0},    {"incrbyfloat",incrbyfloatCommand,3,"wmF",0,NULL,1,1,1,0,0},    {"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},    {"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},    {"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},    {"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},    {"select",selectCommand,2,"lF",0,NULL,0,0,0,0,0},    {"move",moveCommand,3,"wF",0,NULL,1,1,1,0,0},    {"rename",renameCommand,3,"w",0,NULL,1,2,1,0,0},    {"renamenx",renamenxCommand,3,"wF",0,NULL,1,2,1,0,0},    {"expire",expireCommand,3,"wF",0,NULL,1,1,1,0,0},    {"expireat",expireatCommand,3,"wF",0,NULL,1,1,1,0,0},    {"pexpire",pexpireCommand,3,"wF",0,NULL,1,1,1,0,0},    {"pexpireat",pexpireatCommand,3,"wF",0,NULL,1,1,1,0,0},    {"keys",keysCommand,2,"rS",0,NULL,0,0,0,0,0},    {"scan",scanCommand,-2,"rR",0,NULL,0,0,0,0,0},    {"dbsize",dbsizeCommand,1,"rF",0,NULL,0,0,0,0,0},    {"auth",authCommand,2,"sltF",0,NULL,0,0,0,0,0},    {"ping",pingCommand,-1,"tF",0,NULL,0,0,0,0,0},    {"echo",echoCommand,2,"F",0,NULL,0,0,0,0,0},    {"save",saveCommand,1,"as",0,NULL,0,0,0,0,0},    {"bgsave",bgsaveCommand,-1,"a",0,NULL,0,0,0,0,0},    {"bgrewriteaof",bgrewriteaofCommand,1,"a",0,NULL,0,0,0,0,0},    {"shutdown",shutdownCommand,-1,"alt",0,NULL,0,0,0,0,0},    {"lastsave",lastsaveCommand,1,"RF",0,NULL,0,0,0,0,0},    {"type",typeCommand,2,"rF",0,NULL,1,1,1,0,0},    {"multi",multiCommand,1,"sF",0,NULL,0,0,0,0,0},    {"exec",execCommand,1,"sM",0,NULL,0,0,0,0,0},    {"discard",discardCommand,1,"sF",0,NULL,0,0,0,0,0},    {"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},    {"psync",syncCommand,3,"ars",0,NULL,0,0,0,0,0},    {"replconf",replconfCommand,-1,"aslt",0,NULL,0,0,0,0,0},    {"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},    {"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},    {"sort",sortCommand,-2,"wm",0,sortGetKeys,1,1,1,0,0},    {"info",infoCommand,-1,"lt",0,NULL,0,0,0,0,0},    {"monitor",monitorCommand,1,"as",0,NULL,0,0,0,0,0},    {"ttl",ttlCommand,2,"rF",0,NULL,1,1,1,0,0},    {"touch",touchCommand,-2,"rF",0,NULL,1,1,1,0,0},    {"pttl",pttlCommand,2,"rF",0,NULL,1,1,1,0,0},    {"persist",persistCommand,2,"wF",0,NULL,1,1,1,0,0},    {"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0},    {"role",roleCommand,1,"lst",0,NULL,0,0,0,0,0},    {"debug",debugCommand,-1,"as",0,NULL,0,0,0,0,0},    {"config",configCommand,-2,"lat",0,NULL,0,0,0,0,0},    {"subscribe",subscribeCommand,-2,"pslt",0,NULL,0,0,0,0,0},    {"unsubscribe",unsubscribeCommand,-1,"pslt",0,NULL,0,0,0,0,0},    {"psubscribe",psubscribeCommand,-2,"pslt",0,NULL,0,0,0,0,0},    {"punsubscribe",punsubscribeCommand,-1,"pslt",0,NULL,0,0,0,0,0},    {"publish",publishCommand,3,"pltF",0,NULL,0,0,0,0,0},    {"pubsub",pubsubCommand,-2,"pltR",0,NULL,0,0,0,0,0},    {"watch",watchCommand,-2,"sF",0,NULL,1,-1,1,0,0},    {"unwatch",unwatchCommand,1,"sF",0,NULL,0,0,0,0,0},    {"cluster",clusterCommand,-2,"a",0,NULL,0,0,0,0,0},    {"restore",restoreCommand,-4,"wm",0,NULL,1,1,1,0,0},    {"restore-asking",restoreCommand,-4,"wmk",0,NULL,1,1,1,0,0},    {"migrate",migrateCommand,-6,"w",0,migrateGetKeys,0,0,0,0,0},    {"asking",askingCommand,1,"F",0,NULL,0,0,0,0,0},    {"readonly",readonlyCommand,1,"F",0,NULL,0,0,0,0,0},    {"readwrite",readwriteCommand,1,"F",0,NULL,0,0,0,0,0},    {"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0},    {"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},    {"client",clientCommand,-2,"as",0,NULL,0,0,0,0,0},    {"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},    {"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},    {"slowlog",slowlogCommand,-2,"a",0,NULL,0,0,0,0,0},    {"script",scriptCommand,-2,"s",0,NULL,0,0,0,0,0},    {"time",timeCommand,1,"RF",0,NULL,0,0,0,0,0},    {"bitop",bitopCommand,-4,"wm",0,NULL,2,-1,1,0,0},    {"bitcount",bitcountCommand,-2,"r",0,NULL,1,1,1,0,0},    {"bitpos",bitposCommand,-3,"r",0,NULL,1,1,1,0,0},    {"wait",waitCommand,3,"s",0,NULL,0,0,0,0,0},    {"command",commandCommand,0,"lt",0,NULL,0,0,0,0,0},    {"geoadd",geoaddCommand,-5,"wm",0,NULL,1,1,1,0,0},    {"georadius",georadiusCommand,-6,"w",0,NULL,1,1,1,0,0},    {"georadiusbymember",georadiusByMemberCommand,-5,"w",0,NULL,1,1,1,0,0},    {"geohash",geohashCommand,-2,"r",0,NULL,1,1,1,0,0},    {"geopos",geoposCommand,-2,"r",0,NULL,1,1,1,0,0},    {"geodist",geodistCommand,-4,"r",0,NULL,1,1,1,0,0},    {"pfselftest",pfselftestCommand,1,"a",0,NULL,0,0,0,0,0},    {"pfadd",pfaddCommand,-2,"wmF",0,NULL,1,1,1,0,0},    {"pfcount",pfcountCommand,-2,"r",0,NULL,1,-1,1,0,0},    {"pfmerge",pfmergeCommand,-2,"wm",0,NULL,1,-1,1,0,0},    {"pfdebug",pfdebugCommand,-3,"w",0,NULL,0,0,0,0,0},    {"post",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},    {"host:",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},    {"latency",latencyCommand,-2,"aslt",0,NULL,0,0,0,0,0}};
结构体第三个成员就是限定对应命令参数个数的,我们只需把zincrby的4改成5就好了

最后命令zincrby key incr value也要改成zincrby key xx incr value

key原来在集合里,可以正常incr,key1一开始没有,incr返回nil,后来add之后就可以incr了

仓促完成,未经严格测试,如果有其他官方实现方案不吝赐教

阅读全文
0 0