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 doaha 就是这里,找找怎么设置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了
仓促完成,未经严格测试,如果有其他官方实现方案不吝赐教
- redis zincrby命令如何做到键不存在时不add
- redis源码修改之zincrby,hincrby命令
- add-apt-repository命令不存在
- Redis如何做到全局缓存
- 关于MongoDB中如何做到对已有数据插入时更新字段,不存在时则插入新的Document
- 转屏时如何做到activity不重新创建
- linux make命令不存在如何安装?
- 解决ecshop订单管理退货时,如何做到不退邮费
- Dynamics CRM 导入解决方案时如何做到不覆盖目标系统的站点地图
- redis如何做到和mysql数据库的同步
- 改版如何做到不丢失权重与收录
- 如何做到通过Struts2完成Submit但是画面不跳转
- 如何做到js绑定事件的同时不执行事件
- 技术变化那么快,程序员如何做到不被淘汰?
- 技术变化那么快,程序员如何做到不被淘汰?
- 技术变化那么快,程序员如何做到不被淘汰?
- 技术变化那么快,程序员如何做到不被淘汰?
- 【mysql】如何做到表内存在同名字段就更改记录,不存在就新增记录(replace的详细使用)
- javaScript 继承
- 深度学习_资料汇总链接(目标检测/可视化/目标识别/自然语言处理/OCR)
- 花旗前端面试
- 无人车技术基础——课程3:Particle Filters
- Spark集群搭建与并验证环境是否搭建成功(三台机器)
- redis zincrby命令如何做到键不存在时不add
- HtmlAgilityPack——解析html和采集网页的神兵利器
- log4j.properties参数
- 2.Kotlin项目创建
- redis的持久化方式
- 三 流程控制与数组与数组
- 1008. 数组元素循环右移问题 (20)
- ES个人学习
- Springmvc中,在业务控制方法中写入包装类收集参数