Redis源码阅读之set命令
June 19, 2024
redis版本:7.0.2
函数setGenericCommand,对应文件是t_string.c,源码如下:
/* The setGenericCommand() function implements the SET operation with different
* options and variants. This function is called in order to implement the
* following commands: SET, SETEX, PSETEX, SETNX, GETSET.
* setGenericCommand()函数实现了SET操作,它有多个不同的选项和变体。调用这个函数可以实现下面几个命令:SET, SETEX, PSETEX, SETNX, GETSET.
*
* 'flags' changes the behavior of the command (NX, XX or GET, see below).
* 'flags'影响命令(NX,XX或GET,详情见下面)的操作。
*
* 'expire' represents an expire to set in form of a Redis object as passed
* by the user. It is interpreted according to the specified 'unit'.
* 'expire'代表了一个由用户传递的过期时间,它是Redis对象形式。会根据不同的单位对其进行解析。
*
* 'ok_reply' and 'abort_reply' is what the function will reply to the client
* if the operation is performed, or when it is not because of NX or
* XX flags.
* 'ok_reply' 和 'abort_reply' 是函数在操作成功或者因为NX或XX标志而没有执行操作时,会回复给客户端的内容。
*
* If ok_reply is NULL "+OK" is used.
* If abort_reply is NULL, "$-1" is used.
* 如果ok_reply为NULL,会使用"+OK"。
* 如果abort_reply为NULL,会使用"$-1"。
*
* */
#define OBJ_NO_FLAGS 0
#define OBJ_SET_NX (1 << 0) /* Set if key not exists. */
#define OBJ_SET_XX (1 << 1) /* Set if key exists. */
#define OBJ_EX (1 << 2) /* Set if time in seconds is given */
#define OBJ_PX (1 << 3) /* Set if time in ms in given */
#define OBJ_KEEPTTL (1 << 4) /* Set and keep the ttl */
#define OBJ_SET_GET (1 << 5) /* Set if want to get key before set */
#define OBJ_EXAT (1 << 6) /* Set if timestamp in second is given */
#define OBJ_PXAT (1 << 7) /* Set if timestamp in ms is given */
#define OBJ_PERSIST (1 << 8) /* Set if we need to remove the ttl */
/* Forward declaration */
static int getExpireMillisecondsOrReply(client *c, robj *expire, int flags, int unit, long long *milliseconds);
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 */
int found = 0;
int setkey_flags = 0;
if (expire && getExpireMillisecondsOrReply(c, expire, flags, unit, &milliseconds) != C_OK)
{
return;
}
if (flags & OBJ_SET_GET)
{
if (getGenericCommand(c) == C_ERR)
return;
}
found = (lookupKeyWrite(c->db, key) != NULL);
if ((flags & OBJ_SET_NX && found) ||
(flags & OBJ_SET_XX && !found))
{
if (!(flags & OBJ_SET_GET))
{
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
}
return;
}
setkey_flags |= (flags & OBJ_KEEPTTL) ? SETKEY_KEEPTTL : 0;
setkey_flags |= found ? SETKEY_ALREADY_EXIST : SETKEY_DOESNT_EXIST;
setKey(c, c->db, key, val, setkey_flags);
server.dirty++;
notifyKeyspaceEvent(NOTIFY_STRING, "set", key, c->db->id);
if (expire)
{
setExpire(c, c->db, key, milliseconds);
/* Propagate as SET Key Value PXAT millisecond-timestamp if there is
* EX/PX/EXAT/PXAT flag. */
robj *milliseconds_obj = createStringObjectFromLongLong(milliseconds);
rewriteClientCommandVector(c, 5, shared.set, key, val, shared.pxat, milliseconds_obj);
decrRefCount(milliseconds_obj);
notifyKeyspaceEvent(NOTIFY_GENERIC, "expire", key, c->db->id);
}
if (!(flags & OBJ_SET_GET))
{
addReply(c, ok_reply ? ok_reply : shared.ok);
}
/* Propagate without the GET argument (Isn't needed if we had expire since in that case we completely re-written the command argv) */
if ((flags & OBJ_SET_GET) && !expire)
{
int argc = 0;
int j;
robj **argv = zmalloc((c->argc - 1) * sizeof(robj *));
for (j = 0; j < c->argc; j++)
{
char *a = c->argv[j]->ptr;
/* Skip GET which may be repeated multiple times. */
if (j >= 3 &&
(a[0] == 'g' || a[0] == 'G') &&
(a[1] == 'e' || a[1] == 'E') &&
(a[2] == 't' || a[2] == 'T') && a[3] == '\0')
continue;
argv[argc++] = c->argv[j];
incrRefCount(c->argv[j]);
}
replaceClientCommandVector(c, argc, argv);
}
}
主要逻辑为:
...