【Redis源码】append命令

0be0ff4c7bbfa411876c6ec75ec28c8f.jpg

简介

数据库已经有了key,它的值为value。当我们发现value值需要追加字符串却又不想直接用set命令覆盖原值时,可以用append命令来实现。

命令格式:

append key value
  • 说明: 将value追加到原值的末尾,如果key不存在,此命令等同于set key value命令。

append 实现

现在介绍在key已经存在的情况下进行的操作。我们知道,只有value为字符串时才可以追加字符串,数字是不可以追加的,所以当key存在时,首先判断下value的类型是否为string类型。如果不为string类型时会报错。

if (checkType(c,o,OBJ_STRING))
  return;

在追加字符串时,需要判断追加后的字符串长度必须小于512MB,否则会报错。

append = c->argv[2];
totlen = stringObjectLen(o)+sdslen(append->ptr);//检查长度
if (checkStringLength(c,totlen) != C_OK)

checkStringLength函数原型如下:

static int checkStringLength(client *c, long long size) {
    if (size > 512*1024*1024) {
        addReplyError(c,"string exceeds maximum allowed size (512MB)");
        return C_ERR;
    }
    return C_OK;
}

这里我们不禁要问,为什么在追加字符串时才考虑追加后的长度不能大于512 MB,那么在set命令时为什么没有限制最大长度呢?在networking.c中找到如下代码:

ok = string2ll(c->querybuf+1+c->qb_pos,newline-(c->querybuf+1+c->qb_pos),&ll);
if (!ok || ll > 1024*1024) {
     addReplyError(c,"Protocol error: invalid multibulk length");
     setProtocolError("invalid mbulk count",c);
     return C_ERR;
}

由此可见,在服务端接收到命令的时候,就已经判断了命令的最大长度不能大于1 MB,所以set命令不需要再次判断了。

字符串追加会修改原字符串的值,所以必须保证字符串是非共享的。如果字符串是共享的,则需要解除共享,新创建一个值对象。实现代码为:

robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
    serverAssert(o->type == OBJ_STRING);
    if (o->refcount != 1 || o->encoding != OBJ_ENCODING_RAW) {
        // 如果是共享的,则需要解除共享,创建新的字符串
        robj *decoded = getDecodedObject(o);
        o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));
        decrRefCount(decoded);
        dbOverwrite(db,key,o);
    }
    return o;
}

值对象创建好之后,将新字符串追加到原字符串末尾。

o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));

这样就完成了字符串的append操作。


标 题:《【Redis源码】append命令
作 者:zeekling
提 示:转载请注明文章转载自个人博客:小令童鞋

评论

取消