redis-3.2.100 源码层面事务原子性与非原子性分析

145 阅读3分钟

  前言:最近在看redis,看迷糊了,有说redis是非原子性的,有说redis是原子性的??当时很懵逼,然后花了点时间查了查资料,配合大佬的文章,结合自己使用redis写了这个。

1. 先说结论

redis-3.2.100(windows版本)具有原子性一面。

redis-3.2.100(windows版本)不支持事务回滚,也有非原子性的一面

2. 源码分析

我们知道,输入mutli时,会开始事务,输入的命令不是 EXEC、DISCARD、MULTI 或 WATCH 命令,则将命令加入事务队列。

redis-3.2.100 命令执行函数processCommand(server.c文件下)主要是对 Redis 命令的处理进行了多方面的检查和处理,包括错误检查、身份验证、集群重定向、内存管理、持久化问题、从节点状态、Pub/Sub 上下文、加载数据状态、Lua 脚本执行时间等。根据不同的条件和配置,决定是否执行命令,返回相应的回复或错误。

部分代码:

int processCommand(client* c){
  /* Now lookup the command and check ASAP about trivial error conditions
     * such as wrong arity, bad command name and so forth. */
    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
    if (!c->cmd) {
        flagTransaction(c);
        addReplyErrorFormat(c,"unknown command '%s'",
            (char*)c->argv[0]->ptr);
        return C_OK;
    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
               (c->argc < -c->cmd->arity)) {
        flagTransaction(c);
        addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
            c->cmd->name);
        return C_OK;
    }


  /* 事务代码 */
    if (c->flags & CLIENT_MULTI &&
        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
    {
        queueMultiCommand(c); //入队函数
        addReply(c,shared.queued);
    } else {
        call(c,CMD_CALL_FULL);
        c->woff = server.master_repl_offset;
        if (listLength(server.ready_keys))
            handleClientsBlockedOnLists();
    }
    return C_OK;
}

当前输入的命令通过lookupCommand能找得到,并核查参数数量无误后,执行的命令且不是 EXEC、DISCARD、MULTI 或 WATCH 命令,则将该命令加入事务队列,并返回已入队的回复。

所以当我们每输入一条命令都会进行一次语法检测此时我们开始事务,如果出现语法错误即通不过检测,则无法回滚,直接终止事务。返回nil在这里表示键不存在。这里就体现出原子性。

好,假设我们输入的命令没有任何问题,就进入事务队列了,queueMultiCommand函数(multi.c文件下): ​ 那这时候我们来这么一段,为什么会这样呢?

确实,我们输入的没有语法问题,进入了事务队列,但是我们细看事务执行的代码execCommand(multi.c文件下),命令运行时有问题——运行错误

void execCommand(client *c) {

    if (!(c->flags & CLIENT_MULTI)) {
        addReplyError(c,"EXEC without MULTI");
        return;
    }

    if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC)) {
        addReply(c, c->flags & CLIENT_DIRTY_EXEC ? shared.execaborterr :
                                                  shared.nullmultibulk);
        discardTransaction(c);
        goto handle_monitor;
    }

    /* Exec all the queued commands */
    ...
}

意思就是,我们调用exec的时候,运行事务队列里面的命令时,会对命令进行一定的检测,如果这时候出错了,前面后面的命令都会执行,不会发生事务回滚,表现出非原子性的一面

(ps:这里有点偷懒了= = ,没有去深挖.........)

参考链接:

高频Redis面试题解析:Redis 事务是否具备原子性? - 知乎

Redis的事务满足原子性吗? - 掘金

源码下载:Release 3.2.100 · microsoftarchive/redis · GitHub