1.介绍
string类型本质上是char[]数组的封装
中文网:http://www.redis.cn/commands.html#string
2.常用命令
set /get
set命令的时间复杂度是O(1)
将键key
设定为指定的“字符串”值。
如果 key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。
当set
命令执行成功之后,之前设置的过期时间都将失效
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX
seconds – 设置键key的过期时间,单位时秒
PX
milliseconds – 设置键key的过期时间,单位时毫秒
NX
– 只有键key不存在的时候才会设置key的值
XX
– 只有键key存在的时候才会设置key的值
127.0.0.1:6379> set myKey "Hello" OK 127.0.0.1:6379> get myKey "Hello" 127.0.0.1:6379> set userId "1" OK 127.0.0.1:6379> get userId "1" 127.0.0.1:6379> object encoding userId "int" 127.0.0.1:6379> object encoding myKey "embstr" 127.0.0.1:6379> set myKey World NX (nil) 127.0.0.1:6379> set myKey World XX OK 127.0.0.1:6379>
NX :应用场景分布式锁:通过myKey的赋值来判断是否获取到了一个分布式锁 如果OK说明获取到了锁 如果nil说明没有获取到了锁
如果存放到string中的value是int,那么在内部还是int ,可以从encoding
redisObject中有一个type属性和encoding属性
源码解析
redisCommand存放着所有的命令
{"set",setCommand,-3, "write use-memory @string", 0,NULL,1,1,1,0,0,0},
setCommand源码
setCommand传入一个client结构体
最后调用setGenericCommand函数来处理set
/* SET key value [NX] [XX] [KEEPTTL] [EX] [PX */ void setCommand(client *c) { int j; robj *expire = NULL; int unit = UNIT_SECONDS; int flags = OBJ_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 & OBJ_SET_XX)) { flags |= OBJ_SET_NX; } else if ((a[0] == 'x' || a[0] == 'X') && (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && !(flags & OBJ_SET_NX)) { flags |= OBJ_SET_XX; } else if (!strcasecmp(c->argv[j]->ptr,"KEEPTTL") && !(flags & OBJ_SET_EX) && !(flags & OBJ_SET_PX)) { flags |= OBJ_SET_KEEPTTL; } else if ((a[0] == 'e' || a[0] == 'E') && (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && !(flags & OBJ_SET_KEEPTTL) && !(flags & OBJ_SET_PX) && next) { flags |= OBJ_SET_EX; unit = UNIT_SECONDS; expire = next; j++; } else if ((a[0] == 'p' || a[0] == 'P') && (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && !(flags & OBJ_SET_KEEPTTL) && !(flags & OBJ_SET_EX) && next) { flags |= OBJ_SET_PX; 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); }]
setGenericCommand源码
可以看出调用了genericSetKey函数
void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) { long long milliseconds = 0; /* initialized to avoid any harmness warning */ if (expire) { if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK) return; if (milliseconds <= 0) { addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name); return; } if (unit == UNIT_SECONDS) milliseconds *= 1000; } if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) || (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL)) { addReply(c, abort_reply ? abort_reply : shared.null[c->resp]); return; } genericSetKey(c,c->db,key,val,flags & OBJ_SET_KEEPTTL,1); server.dirty++; if (expire) setExpire(c,c->db,key,mstime()+milliseconds); notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id); if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC, "expire",key,c->db->id); addReply(c, ok_reply ? ok_reply : shared.ok); }
genericSetKey源码
void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) { if (lookupKeyWrite(db,key) == NULL) { dbAdd(db,key,val); } else { dbOverwrite(db,key,val); } incrRefCount(val); if (!keepttl) removeExpire(db,key); if (signal) signalModifiedKey(c,db,key); }
dbOverwrite源码
可以看出最后存入dict中
void dbOverwrite(redisDb *db, robj *key, robj *val) { dictEntry *de = dictFind(db->dict,key->ptr); serverAssertWithInfo(NULL,key,de != NULL); dictEntry auxentry = *de; robj *old = dictGetVal(de); if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { val->lru = old->lru; } dictSetVal(db->dict, de, val); if (server.lazyfree_lazy_server_del) { freeObjAsync(old); dictSetVal(db->dict, &auxentry, NULL); } dictFreeVal(db->dict, &auxentry); }
incr/incrby/decr/decrby命令
incr/decr 自增或者自减1
incrby/decrby:自增或者自减指定数
127.0.0.1:6379> INCR userId
(integer) 2
127.0.0.1:6379> INCR userId
(integer) 3
127.0.0.1:6379> get userId
"3"
127.0.0.1:6379> INCRBY userId 10
(integer) 13
127.0.0.1:6379> DECR userId
(integer) 12
127.0.0.1:6379> DECRBY userId 10 (integer) 2
append
如果 key
已经存在,并且值为字符串,那么这个命令会把 value
追加到原来值(value)的结尾。 如果 key
不存在,那么它将首先创建一个空字符串的key
,再执行追加操作,这种情况 APPEND 将类似于 SET 操作。
127.0.0.1:6379> APPEND myKey "!" (integer) 6 127.0.0.1:6379> get myKey "World!" 127.0.0.1:6379>
setex/psetex
设置过期时间,
setex表示设置多少秒过期
psetex表示设置多少毫秒过期
127.0.0.1:6379> psetex mykey 10000 "Hello" OK 127.0.0.1:6379> pttl mykey (integer) 3430 127.0.0.1:6379> ttl mykey (integer) -2 127.0.0.1:6379> pttl mykey (integer) -2 127.0.0.1:6379> setex mykey 10 "Hello" OK 127.0.0.1:6379> ttl mykey (integer) 5 127.0.0.1:6379> ttl mykey (integer) -2
strlen
返回key的string类型value的长度。如果key对应的非string类型,就返回错误。
strlen取的是sds中的len属性,所以时间复杂度是O(1)
时间复杂度:O(1)
127.0.0.1:6379> set mykey "HelloWorld!" OK 127.0.0.1:6379> strlen mykey (integer) 11 127.0.0.1:6379>
genrange/setrange
genrange: 返回key对应的字符串value的子串,这个子串是由start和end位移决定的(两者都在string内)。 相当于C#中的substr
setrange:这个命令的作用是覆盖key对应的string的一部分,从指定的offset处开始,覆盖value的长度。如果offset比当前key对应string还要长,那这个string后面就补0以达到offset。不存在的keys被认为是空字符串,所以这个命令可以确保key有一个足够大的字符串,能在offset处设置value。 相当于C#中的replace
127.0.0.1:6379> getrange mykey 0 4 "Hello" 127.0.0.1:6379> setrange mykey 5 redis (integer) 11 127.0.0.1:6379> get mykey "Helloredis!" 127.0.0.1:6379>
源码解析
size_t stringObjectLen(robj *o) { serverAssertWithInfo(NULL,o,o->type == OBJ_STRING); if (sdsEncodedObject(o)) { return sdslen(o->ptr); } else { return sdigits10((long)o->ptr); } }
static inline size_t sdslen(const sds s) { unsigned char flags = s[-1]; switch(flags&SDS_TYPE_MASK) { case SDS_TYPE_5:d return SDS_TYPE_5_LEN(flags); case SDS_TYPE_8: return SDS_HDR(8,s)->len; case SDS_TYPE_16: return SDS_HDR(16,s)->len; case SDS_TYPE_32: return SDS_HDR(32,s)->len; case SDS_TYPE_64: return SDS_HDR(64,s)->len; } return 0; }
setbit/getbit/bitop
setbit:设置或者清空key的value(字符串)在offset处的bit值。
gitbit:返回key对应的string在offset处的bit值 当offset超出了字符串长度的时候,这个字符串就被假定为由0比特填充的连续空间。当key不存在的时候,它就认为是一个空字符串,所以offset总是超出范围,然后value也被认为是由0比特填充的连续空间。到内存分配。
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
BITOP
命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数:
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
BITOP NOT destkey srckey
,对给定 key 求逻辑非,并将结果保存到 destkey 。
127.0.0.1:6379> setbit mykey 7 1 (integer) 0 127.0.0.1:6379> getbit mykey 7 (integer) 1 127.0.0.1:6379> getbit mykey 0 (integer) 0 127.0.0.1:6379> setbit mykey 7 1 (integer) 1 127.0.0.1:6379> setbit num1 7 1 (integer) 0 127.0.0.1:6379> setbit num2 6 1 (integer) 0 127.0.0.1:6379> bitop and nums num1 num2 (integer) 1 127.0.0.1:6379> get nums "\x00" 127.0.0.1:6379>