前言:最近在看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 事务是否具备原子性? - 知乎
源码下载:Release 3.2.100 · microsoftarchive/redis · GitHub