加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Redis系列(四):数据结构String类型中基本操作命令和源码解析

发布时间:2020-12-16 04:37:49 所属栏目:安全 来源:网络整理
导读:1.介绍 string类型本质上是char[]数组的封装? 中文网: http://www.redis.cn/commands.html#string ? 2.常用命令 set /get set命令的时间复杂度是O(1) 将键 key 设定为指定的“字符串”值。 如果 key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并

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
get myKey
"
set userId 1 userId
object encoding userId
int encoding myKey
embstrset myKey World NX
(nil)
 myKey World XX
OK
6379>

?

NX :应用场景分布式锁:通过myKey的赋值来判断是否获取到了一个分布式锁? 如果OK说明获取到了锁 如果nil说明没有获取到了锁

如果存放到string中的value是int,那么在内部还是int ,可以从encoding

redisObject中有一个type属性和encoding属性

?

源码解析

redisCommand存放着所有的命令

    {set",setCommand,-3,write use-memory @string0,NULL,1,1)">0},

?

?

?setCommand源码

setCommand传入一个client结构体?

最后调用setGenericCommand函数来处理set

/* SET key value [NX] [XX] [KEEPTTL] [EX <seconds>] [PX <milliseconds>] */
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[N') &&
            (a[1] == xX') && a[2] == ' &&
            !(flags & OBJ_SET_XX))
        {
            flags |= OBJ_SET_NX;
        } else 
                   (a[' &&
                   !(flags & OBJ_SET_NX))
        {
            flags |= OBJ_SET_XX;
        } if (!strcasecmp(c->argv[j]->ptr,1)">KEEPTTL") &&
                   !(flags & OBJ_SET_EX) && !(flags & OBJ_SET_PX))
        {
            flags |= OBJ_SET_KEEPTTL;
        } eE' &&
                   !(flags & OBJ_SET_KEEPTTL) &&
                   !(flags & OBJ_SET_PX) && next)
        {
            flags |= OBJ_SET_EX;
            unit = UNIT_SECONDS;
            expire = next;
            j++;
        } pP' &&
                   !(flags & OBJ_SET_KEEPTTL) &&
                   !(flags & OBJ_SET_EX) && OBJ_SET_PX;
            unit = UNIT_MILLISECONDS;
            expire =else {
            addReply(c,shared.syntaxerr);
            return;
        }
    }

    c->argv[2] = tryObjectEncoding(c->argv[2]);
    setGenericCommand(c,flags,c->argv[1],c->argv[],expire,unit,NULL);
}

?

?

?

?

?

?setGenericCommand源码

可以看出调用了genericSetKey函数

?

void setGenericCommand(client *c,int flags,robj *key,robj *val,robj *expire,1)">int unit,robj *ok_reply,robj *abort_reply) {
    long long milliseconds = 0;  initialized to avoid any harmness warning */

    if (expire) {
        if (getLongLongFromObjectOrReply(c,&milliseconds,NULL) != C_OK)
            ;
        if (milliseconds <= 0) {
            addReplyErrorFormat(c,1)">invalid expire time in %sname);
            ;
        }
        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]);
        ;
    }
    genericSetKey(c,c->db,key,val,flags & OBJ_SET_KEEPTTL,1)">);
    server.dirty++;
    if (expire) setExpire(c,c->db,mstime()+milliseconds);
    notifyKeyspaceEvent(NOTIFY_STRING,c->db->id);
     (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,1)">expireid);
    addReply(c,ok_reply ? ok_reply : shared.ok);
}

?

?

?genericSetKey源码

void genericSetKey(client *c,redisDb *db,1)">int keepttl,1)"> signal) {
    if (lookupKeyWrite(db,1)"> NULL) {
        dbAdd(db,val);
    }  {
        dbOverwrite(db,val);
    }
    incrRefCount(val);
    if (!keepttl) removeExpire(db,key);
     (signal) signalModifiedKey(c,db,key);
}

?

?

?dbOverwrite源码

可以看出最后存入dict中

void dbOverwrite(redisDb *db,1)">val) {
    dictEntry *de = dictFind(db->dict,key->ptr);

    serverAssertWithInfo(NULL,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);

     (server.lazyfree_lazy_server_del) {
        freeObjAsync(old);
        dictSetVal(db->dict,&auxentry,NULL);
    }

    dictFreeVal(db->dict,1)">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?操作。

6379> APPEND myKey !
(integer) 6
World!6379>

setex/psetex

设置过期时间,

setex表示设置多少秒过期

psetex表示设置多少毫秒过期

6379> psetex mykey 10000 6379> pttl mykey
(integer) 3430
 ttl mykey
(integer) -2
 pttl mykey
(integer) -6379> setex mykey 10  ttl mykey
(integer) 5
2

strlen

返回key的string类型value的长度。如果key对应的非string类型,就返回错误。

strlen取的是sds中的len属性,所以时间复杂度是O(1)

时间复杂度:O(1)

set mykey HelloWorld! strlen mykey
(integer) 11
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

6379> getrange mykey 0 4
6379> setrange mykey 5 redis
(integer)  mykey
Helloredis!6379>

源码解析

size_t stringObjectLen(robj *o) {
    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
     (sdsEncodedObject(o)) {
        return sdslen(o->ptr);
    }  {
        return sdigits10((long)o->ptr);
    }
}

?

static inline size_t sdslen(const sds s) {
    unsigned char flags = s[-];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:d
             SDS_TYPE_5_LEN(flags);
         SDS_TYPE_8:
            return SDS_HDR(8,s)->len;
         SDS_TYPE_16:
            16,1)"> SDS_TYPE_32:
            32,1)"> SDS_TYPE_64:
            64,1)">len;
    }
    return ;
}

?

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 。

6379> setbit mykey 7 0
6379> getbit mykey 71
6379> setbit num1 6379> setbit num2 6  bitop and nums num1 num2
(integer)  nums
x006379>

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读