redis代码解析-事务

redis 的事务相关的几个命令分别为 watch multi exec.

watch 可以监控一个变量在事务开始执行之前是否有被修改.使用方式为: WATCH key [key ...]

在redis内部的实现是每个db有一个名为watched_keys的dict,这个dict的key为监控的key,value为所有监控该key的client组成的链表.

所有的涉及到修改数据库的命令执行成功之后都会执行signalModifiedKey->touchWatchedKey,将修改的db中的watched_keys里面记录的所有监控了本次修改的key的client设置c->flags |= CLIENT_DIRTY_CAS;

multi 表示开始一次事务,使用方式为: MULTI

实现为只是对client标记上 c->flags |= CLIENT_MULTI;它会先检查一下是否已经标记了 CLIENT_MULTI,如果标记了本次multi命令会失败,因为redis不允许事务嵌套.

在multi开始之后所有的命令(除了multi,exec,discard,watch)都会进入multicommand queue.

我们知道一条命令进入server的流程为,

第一步,底层的io多路复用处理器会上报client可读事件,调用注册的处理可读事件的函数readQueryFromClient将命令读入client的querybuf,

第二步,进入到processInputBuffer处理读入的buffer,分解出argv,argc;

第三步,再进入processCommand,先根据argv[1]在命令表的dict中找到对应的command,找到command后会检查本次的command是否合法(名字是否合法,参数个数是否合法),如果command不合法在返回错误的同时会检查是否处于事务状态中,如果命令

不合法CLIENT_DIRTY_EXEC标志将导致事务执行失败.

/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.
 * Should be called every time there is an error while queueing a command. */
void flagTransaction(client *c) {
    if (c->flags & CLIENT_MULTI)
        c->flags |= CLIENT_DIRTY_EXEC;
}

然后(略过一些与事务无关的步骤),检查c->flags & CLIENT_MULTI,来决定是否直接执行命令还是将命令进入queueMultiCommand

第四步,在queueMultiCommand中,将本次命令command,argc,argv加入到c->mstate.commands队列中.

/* Client MULTI/EXEC state */
typedef struct multiCmd {
    robj **argv;
    int argc;
    struct redisCommand *cmd;
} multiCmd;

typedef struct multiState {
    multiCmd *commands;     /* Array of MULTI commands */
    int count;              /* Total number of MULTI commands */
    int minreplicas;        /* MINREPLICAS for synchronous replication */
    time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
} multiState;
typedef struct client {
 ...
 multiState mstate;      /* MULTI/EXEC state */
 ...
} client;

exec 命令开始执行事务队列中的全部命令,使用方式为:EXEC

exec会检查当前是否处于事务状态

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

然后检查该条命令来自的client监控的key是否有被touch

/* Check if we need to abort the EXEC because:
     * 1) Some WATCHed key was touched.
     * 2) There was a previous error while queueing commands.
     * A failed EXEC in the first case returns a multi bulk nil object
     * (technically it is not an error but a special behavior), while
     * in the second an EXECABORT error is returned. */
    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;
    }

最后是遍历c->mstate.commands执行所有事务命令队列中的命令

/* Exec all the queued commands */
    unwatchAllKeys(c); /* Unwatch ASAP otherwise we‘ll waste CPU cycles */
    orig_argv = c->argv;
    orig_argc = c->argc;
    orig_cmd = c->cmd;
    addReplyMultiBulkLen(c,c->mstate.count);
    for (j = 0; j < c->mstate.count; j++) {
        c->argc = c->mstate.commands[j].argc;
        c->argv = c->mstate.commands[j].argv;
        c->cmd = c->mstate.commands[j].cmd;

        /* Propagate a MULTI request once we encounter the first command which
         * is not readonly nor an administrative one.
         * This way we‘ll deliver the MULTI/..../EXEC block as a whole and
         * both the AOF and the replication link will have the same consistency
         * and atomicity guarantees. */
        if (!must_propagate && !(c->cmd->flags & (CMD_READONLY|CMD_ADMIN))) {
            execCommandPropagateMulti(c);
            must_propagate = 1;
        }

        call(c,CMD_CALL_FULL);

        /* Commands may alter argc/argv, restore mstate. */
        c->mstate.commands[j].argc = c->argc;
        c->mstate.commands[j].argv = c->argv;
        c->mstate.commands[j].cmd = c->cmd;
    }
    c->argv = orig_argv;
    c->argc = orig_argc;
    c->cmd = orig_cmd;
    discardTransaction(c);
时间: 2024-10-10 21:24:19

redis代码解析-事务的相关文章

04: redis 消费模式 和 事务 - 扩展slow日志

Redis发布消息模式 生产消费模型 Redis发布消息通常有两种模式: 1:队列模式(queuing) 2:发布-订阅模式(publish-subscribe) 任务队列:顾名思义,就是“传递消息的队列”.与任务队列进行交互的实体有两类,一类是生产者(producer),另一类则是消费者(consumer).生产者将需要处理的任务放入任务队列中,而消费者则不断地从任务独立中读入任务信息并执行. 任务队列的好处: 松耦合. 生产者和消费者只需按照约定的任务描述格式,进行编写代码. 易于扩展. 多

Redis中的事务(多命令)操作

作为一个nosql数据库,事务是必要功能.但是redis我们是可以理解为它不支持事务操作的,因为它的特征完全不满足我们对事物的正常理解 ps:我不知道是谁一开始提出redis支持事务的,但是我更倾向于这是redis的多命令功能 multi这个命令单词意思已经说明了一切,我只能理解为中文文档一厢情愿了 事务的使用 1. 开启事务 命令:multi 127.0.0.1:6379> multi OK 执行该命令后,连接会进入事务模式 2.执行操作 可以执行任意的redis数据操作命令,那么执行操作会进

ffmpeg代码解析

void avdevice_register_all(void){    static int initialized;    if (initialized)        return;    initialized = 1;    /* devices */    REGISTER_INOUTDEV(ALSA,             alsa);    REGISTER_INDEV   (AVFOUNDATION,     avfoundation);    REGISTER_INDEV

[nRF51822] 10、基础实验代码解析大全 &#183; 实验15 - RTC

一.实验内容: 配置NRF51822 的RTC0 的TICK 频率为8Hz,COMPARE0 匹配事件触发周期为3 秒,并使能了TICK 和COMPARE0 中断. TICK 中断中驱动指示灯D1 翻转状态, 即指示灯D1 以8Hz 的速率翻转状态 COMPARE0 中断中点亮指示灯D2 二.nRF51822的内部RTC结构: NRF51822 有两个RTC 时钟:RTC0,RTC1.两个RTC 均为24 位,使用LFCLK 低频时钟,并带有12 位分频器,可产生TICK.compare 和溢出

(转)Java二进制指令代码解析

转自http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html Java二进制指令代码解析 Java源码在运行之前都要编译成为字节码格式(如.class文件),然后由ClassLoader将字节码载入运行.在字节码文件中,指令代码只是其中的一部分,里面还记录了字节码文件的编译版本.常量池.访问权限.所有成员变量和成员方法等信息(详见Java字节码格式详解).本文主要简单介绍不同Java指令的功能以及在代码中如何解析二进制指令. Ja

Storm中的LocalState 代码解析

官方的解释这个类为: /** * A simple, durable, atomic K/V database. *Very inefficient*, should only be * used for occasional reads/writes. Every read/write hits disk. */ 简单来理解就是这个类每次读写都会将一个Map<Object, Object>的对象序列化存储到磁盘中,读的时候将其反序列化. 构造函数指定的参数就是你在磁盘中存储的目录,同时也作为

Redis 中的事务

Redis支持简单的事务 Redis与mysql事务的对比 Mysql Redis 开启 start transaction muitl 语句 普通sql 普通命令 失败 rollback 回滚 discard 取消 成功 commit exec 注: rollback与discard 的区别 如果已经成功执行了2条语句, 第3条语句出错. Rollback后,前2条的语句影响消失. Discard只是结束本次事务,前2条语句造成的影响仍然还在 注: 在mutil后面的语句中, 语句出错可能有2

Java二进制指令代码解析

http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html http://blog.csdn.net/sum_rain/article/details/39892219 http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html Java二进制指令代码解析 小注:去年在看<深入解析JVM>书的时候做的一些记录,同时参考了<Java虚拟机规范>.只是对指令的

[nRF51822] 12、基础实验代码解析大全 &#183; 实验19 - PWM

一.PWM概述: PWM(Pulse Width Modulation):脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形. PWM 的几个基本概念: 1) 占空比:占空比是指在一个周期内,信号处于高电平的时间占整个信号周期的百分比,方波的占空比是50%. 2) 调制频率:周期的倒数. 3) 脉冲宽度:信号处于高电平的时间. 二.nRF51822的PWM产生: NRF51822 通过Timer.PPI 和GPIOTE 的配合实现PWM 的功能,由Timer 产生一个事件,