基础的string相关的命令
get——getCommand 示例: GET KEY_NAME
返回 key 的值,如果 key 不存在时,返回 nil。 如果 key 不是字符串类型,那么返回一个错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void getCommand (redisClient *c) { getGenericCommand(c); } int getGenericCommand (redisClient *c) { robj *o; if ((o = lookupKeyReadOrReply(c,c->argv[1 ],shared.nullbulk)) == NULL ) return REDIS_OK; if (o->type != REDIS_STRING) { addReply(c,shared.wrongtypeerr); return REDIS_ERR; } else { addReplyBulk(c,o); return REDIS_OK; } }
说明:先判断db的expires字典中查找key并判断是否已过期,如果已过期,则返回空;如果未过期,则继续在db的dict中查找key,找到则返回,若未找到则返回null。
set——setCommand 示例: SET key value [NX] [XX] [EX ] [PX ]
EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX keymillisecond value 。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 void setCommand (redisClient *c) { int j; robj *expire = NULL ; int unit = UNIT_SECONDS; int flags = REDIS_SET_NO_FLAGS; for (j = 3 ; j < c->argc; j++) { char *a = c->argv[j]->ptr; robj *next = (j == c->argc-1 ) ? NULL : c->argv[j+1 ]; if ((a[0 ] == 'n' || a[0 ] == 'N' ) && (a[1 ] == 'x' || a[1 ] == 'X' ) && a[2 ] == '\0' ) { flags |= REDIS_SET_NX; } else if ((a[0 ] == 'x' || a[0 ] == 'X' ) && (a[1 ] == 'x' || a[1 ] == 'X' ) && a[2 ] == '\0' ) { flags |= REDIS_SET_XX; } else if ((a[0 ] == 'e' || a[0 ] == 'E' ) && (a[1 ] == 'x' || a[1 ] == 'X' ) && a[2 ] == '\0' && next) { unit = UNIT_SECONDS; expire = next; j++; } else if ((a[0 ] == 'p' || a[0 ] == 'P' ) && (a[1 ] == 'x' || a[1 ] == 'X' ) && a[2 ] == '\0' && next) { unit = UNIT_MILLISECONDS; expire = next; j++; } else { addReply(c,shared.syntaxerr); return ; } } c->argv[2 ] = tryObjectEncoding(c->argv[2 ]); setGenericCommand(c,flags,c->argv[1 ],c->argv[2 ],expire,unit,NULL ,NULL ); } void setGenericCommand (redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) { long long milliseconds = 0 ; if (expire) { if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL ) != REDIS_OK) return ; if (milliseconds <= 0 ) { addReplyError(c,"invalid expire time in SETEX" ); return ; } if (unit == UNIT_SECONDS) milliseconds *= 1000 ; } if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL ) || (flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL )) { addReply(c, abort_reply ? abort_reply : shared.nullbulk); return ; } setKey(c->db,key,val); server.dirty++; if (expire) setExpire(c->db,key,mstime()+milliseconds); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set" ,key,c->db->id); if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC, "expire" ,key,c->db->id); addReply(c, ok_reply ? ok_reply : shared.ok); }
说明:
先尝试对value进行编码节省内存(只有编码为RAW或者EMBSTR时尝试编码),EMBSTR 转 INT(字节长度小于21且可以转成整数), RAW 转 EMBSTR(字符串长度小于长度39)
在dict中,如果没有该key,则添加key,value;否则覆盖,其中覆盖后,需要是否原有value
如果EX,需要添加到expires字典中;如果是NX,则判断如果已有key,则不进行第二步的dict添加操作操作
setnx——setnxCommand 示例:SETNX KEY_NAME VALUE
返回 key 的值,如果 key 不存在时,返回 nil。 如果 key 不是字符串类型,那么返回一个错误。
1 2 3 4 5 void setnxCommand (redisClient *c) { c->argv[2 ] = tryObjectEncoding(c->argv[2 ]); setGenericCommand(c,REDIS_SET_NX,c->argv[1 ],c->argv[2 ],NULL ,0 ,shared.cone,shared.czero); }
说明:复用“setGenericCommand”方法,通过flags=REDIS_SET_NX,标识
setex——setexCommand 示例:SETEX KEY_NAME TIMEOUT VALUE
1 2 3 4 5 void setexCommand (redisClient *c) { c->argv[3 ] = tryObjectEncoding(c->argv[3 ]); setGenericCommand(c,REDIS_SET_NO_FLAGS,c->argv[1 ],c->argv[3 ],c->argv[2 ],UNIT_SECONDS,NULL ,NULL ); }
说明:复用“setGenericCommand”方法,通过 expire =UNIT_SECONDS 设置过期时间
psetex——psetexCommand 示例:PSETEX key1 EXPIRY_IN_MILLISECONDS value1
1 2 3 4 5 void psetexCommand (redisClient *c) { c->argv[3 ] = tryObjectEncoding(c->argv[3 ]); setGenericCommand(c,REDIS_SET_NO_FLAGS,c->argv[1 ],c->argv[3 ],c->argv[2 ],UNIT_MILLISECONDS,NULL ,NULL ); }
说明:复用“setGenericCommand”方法,通过 expire =UNIT_MILLISECONDS 设置过期时间
append——appendCommand 示例:APPEND KEY_NAME NEW_VALUE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void appendCommand (redisClient *c) { size_t totlen; robj *o, *append; o = lookupKeyWrite(c->db,c->argv[1 ]); if (o == NULL ) { c->argv[2 ] = tryObjectEncoding(c->argv[2 ]); dbAdd(c->db,c->argv[1 ],c->argv[2 ]); incrRefCount(c->argv[2 ]); totlen = stringObjectLen(c->argv[2 ]); } else { if (checkType(c,o,REDIS_STRING)) return ; append = c->argv[2 ]; totlen = stringObjectLen(o)+sdslen(append->ptr); if (checkStringLength(c,totlen) != REDIS_OK) return ; o = dbUnshareStringValue(c->db,c->argv[1 ],o); o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr)); totlen = sdslen(o->ptr); } signalModifiedKey(c->db,c->argv[1 ]); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"append" ,c->argv[1 ],c->db->id); server.dirty++; addReplyLongLong(c,totlen); }
说明:查找dict中的key是否存在,如果不存在,则直接新增key=>value;如果存在,则追加value。
strlen——strlenCommand 示例:STRLEN KEY_NAME
字符串值的长度。 当 key 不存在时,返回 0。
1 2 3 4 5 6 7 8 9 void strlenCommand (redisClient *c) { robj *o; if ((o = lookupKeyReadOrReply(c,c->argv[1 ],shared.czero)) == NULL || checkType(c,o,REDIS_STRING)) return ; addReplyLongLong(c,stringObjectLen(o)); }
说明:查找dict中的key是否存在,如果存在则获取value,计算value的长度
del——delCommand 示例:DEL key [key …]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void delCommand (redisClient *c) { int deleted = 0 , j; for (j = 1 ; j < c->argc; j++) { expireIfNeeded(c->db,c->argv[j]); if (dbDelete(c->db,c->argv[j])) { signalModifiedKey(c->db,c->argv[j]); notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC, "del" ,c->argv[j],c->db->id); server.dirty++; deleted++; } } addReplyLongLong(c,deleted); }
说明:遍历每个要删除的key,先删除expires的过期key,再删除dict中的key
exists——existsCommand 示例:EXISTS KEY_NAME
1 2 3 4 5 6 7 8 9 10 11 12 void existsCommand (redisClient *c) { expireIfNeeded(c->db,c->argv[1 ]); if (dbExists(c->db,c->argv[1 ])) { addReply(c, shared.cone); } else { addReply(c, shared.czero); } }
说明:先判断expires是否有该key,如果key已过期,则删除该key;查询dict中是否有该key,如果有则返回1,否认返回0
incr——incrCommand 示例:INCR KEY_NAME
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 void incrCommand (redisClient *c) { incrDecrCommand(c,1 ); } void incrDecrCommand (redisClient *c, long long incr) { long long value, oldvalue; robj *o, *new; o = lookupKeyWrite(c->db,c->argv[1 ]); if (o != NULL && checkType(c,o,REDIS_STRING)) return ; if (getLongLongFromObjectOrReply(c,o,&value,NULL ) != REDIS_OK) return ; oldvalue = value; if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) || (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) { addReplyError(c,"increment or decrement would overflow" ); return ; } value += incr; new = createStringObjectFromLongLong(value); if (o) dbOverwrite(c->db,c->argv[1 ],new); else dbAdd(c->db,c->argv[1 ],new); signalModifiedKey(c->db,c->argv[1 ]); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrby" ,c->argv[1 ],c->db->id); server.dirty++; addReply(c,shared.colon); addReply(c,new); addReply(c,shared.crlf); }
说明:如果对象不存在,则初始化为0,获取value后,执行value+1。执行过程符合原子性特点
decr——delCommand
1 2 3 4 void decrCommand (redisClient *c) { incrDecrCommand(c,-1 ); }
说明:同上一个方法,执行value-1
incrby——incrbyCommand 示例:INCRBY KEY_NAME INCR_AMOUNT
1 2 3 4 5 6 void incrbyCommand (redisClient *c) { long long incr; if (getLongLongFromObjectOrReply(c, c->argv[2 ], &incr, NULL ) != REDIS_OK) return ; incrDecrCommand(c,incr); }
说明:同上面的方法,value + incr
decrby——decrbyCommand 示例:DECRBY KEY_NAME DECREMENT_AMOUNT
1 2 3 4 5 6 void decrbyCommand (redisClient *c) { long long incr; if (getLongLongFromObjectOrReply(c, c->argv[2 ], &incr, NULL ) != REDIS_OK) return ; incrDecrCommand(c,-incr); }
说明:同上面的方法,value - incr
incrbyfloat——incrbyfloatCommand 1 示例:INCRBYFLOAT KEY_NAME INCR_AMOUNT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void incrbyfloatCommand (redisClient *c) { long double incr, value; robj *o, *new, *aux; o = lookupKeyWrite(c->db,c->argv[1 ]); if (o != NULL && checkType(c,o,REDIS_STRING)) return ; if (getLongDoubleFromObjectOrReply(c,o,&value,NULL ) != REDIS_OK || getLongDoubleFromObjectOrReply(c,c->argv[2 ],&incr,NULL ) != REDIS_OK) return ; value += incr; if (isnan(value) || isinf(value)) { addReplyError(c,"increment would produce NaN or Infinity" ); return ; } new = createStringObjectFromLongDouble(value); if (o) dbOverwrite(c->db,c->argv[1 ],new); else dbAdd(c->db,c->argv[1 ],new); signalModifiedKey(c->db,c->argv[1 ]); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrbyfloat" ,c->argv[1 ],c->db->id); server.dirty++; addReplyBulk(c,new); aux = createStringObject("SET" ,3 ); rewriteClientCommandArgument(c,0 ,aux); decrRefCount(aux); rewriteClientCommandArgument(c,2 ,new); }
说明:同上类似,int改成float
mget——mgetCommand 示例:MGET KEY1 KEY2 .. KEYN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void mgetCommand (redisClient *c) { int j; addReplyMultiBulkLen(c,c->argc-1 ); for (j = 1 ; j < c->argc; j++) { robj *o = lookupKeyRead(c->db,c->argv[j]); if (o == NULL ) { addReply(c,shared.nullbulk); } else { if (o->type != REDIS_STRING) { addReply(c,shared.nullbulk); } else { addReplyBulk(c,o); } } } }
说明:从dict中查找,如果存在则返回;如果没有则返回空。这里没有调用过期函数。
getset——getsetCommand 示例:GETSET KEY_NAME VALUE
1 2 3 4 5 6 7 8 9 10 11 12 13 void getsetCommand (redisClient *c) { if (getGenericCommand(c) == REDIS_ERR) return ; c->argv[2 ] = tryObjectEncoding(c->argv[2 ]); setKey(c->db,c->argv[1 ],c->argv[2 ]); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set" ,c->argv[1 ],c->db->id); server.dirty++; }
说明:结合了get和set方法,无法解决并发计数问题
mset——msetCommand 示例:MSET key1 value1 key2 value2 .. keyN valueN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 void msetCommand (redisClient *c) { msetGenericCommand(c,0 ); } void msetGenericCommand (redisClient *c, int nx) { int j, busykeys = 0 ; if ((c->argc % 2 ) == 0 ) { addReplyError(c,"wrong number of arguments for MSET" ); return ; } if (nx) { for (j = 1 ; j < c->argc; j += 2 ) { if (lookupKeyWrite(c->db,c->argv[j]) != NULL ) { busykeys++; } } if (busykeys) { addReply(c, shared.czero); return ; } } for (j = 1 ; j < c->argc; j += 2 ) { c->argv[j+1 ] = tryObjectEncoding(c->argv[j+1 ]); setKey(c->db,c->argv[j],c->argv[j+1 ]); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set" ,c->argv[j],c->db->id); } server.dirty += (c->argc-1 )/2 ; addReply(c, nx ? shared.cone : shared.ok); }
说明:结合多个set方法
msetnx——msetnxCommand 示例:MSETNX key1 value1 key2 value2 .. keyN valueN
当所有 key 都成功设置,返回 1 。 如果所有给定 key 都设置失败(至少有一个 key 已经存在),那么返回 0 。
1 2 3 4 void msetnxCommand (redisClient *c) { msetGenericCommand(c,1 ); }
说明:同上
命令
作用
int编码时间复杂度
embstr编码时间复杂度
raw编码实现时间复杂度
SET
设置给定 key 的值
O(1)
O(1)
O(1)
GET
获取给定 key 的值
O(1)
O(1)
O(1)
APPEND
将值附加到key原有的string后面
O(n),n为被添加string的长度
O(n)
O(n)
INCRBYFLOAT
为 key 中所储存的值加上指定的浮点数增量值
O(1)
O(1)
O(1)
INCRBY
为 key 所储存的值加上指定值
O(1)
ERROR (不允许这种操作)
ERROR
DECRBY
为 key 所储存的值减去指定额值
O(1)
ERROR
ERROR
STRLEN
计算字符串的长度
O(1)
O(1)
O(1)
SETRANGE
用value参数覆写给定key所储存的字符串值,从偏移量offset开始
O(n)
O(n)
O(n)
GETRANGE
获取存储在指定 key 中字符串的子字符串
O(n),N
为要返回的字符串的长度
O(n)
O(n)