Redis 事务实现

世界以痛吻我,我仍报之以歌。

事务的三个阶段

一个事务从开始到结束通常会经历以下三个阶段:

  1. 事务开始
  2. 命令入队
  3. 事务执行

事务开始

MULTI 命令的执行标志着事务的开始:

1
redis > MULTI

命令入队

  • 当一个客户端处于非事务状态时,这个客户端发送的命令会立即被服务器执行。

  • 当一个客户端切换到事务状态之后,服务器会根据这个客户端发来的不同命令执行不同的操作: EXEC、DISCARD、WATCH、MULTI 这四种命令服务器会立即执行,以外的其他命令,服务器并不立即执行,而是将命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复。

事务队列

每个 Redis 客户端都有自己的事务状态,这个事务状态保存在客户端状态的 mstate 属性里面:

1
2
3
4
5
6
typedef struct redisClient {
    // ...
    //事务状态
    multiState mstate;    /* MULTI/EXEC state */
    // ...
} redisClient;

事务状态包含一个事务队列,以及一个已入队命令的计数器(也就是事务队列的长度):

1
2
3
4
5
6
typedef struct multiState {
    //事务队列,FIFO顺序
    multiCmd *commands;
    //已入队命令计数
    int count;
} multiState;

事务队列是一个 multiCmd 类型的数组,数组中的每个 multiCmd 结构都保存了一个已入队命令的相关信息,包括指向命令实现函数的指针、命令的参数,以及参数的数量:

1
2
3
4
5
6
7
8
typedef struct multiCmd {
    //参数
    robj **argv;
    //参数数量
    int argc;
    //命令指针
    struct redisCommand *cmd;
} multiCmd;

事务队列以先进先出(FIFO)的方式保存入队的命令,较先入队的命令会被放到数组的前面,而较后入队的命令则会被放到数组的后面。

执行事务

当一个处于事务状态的客户端向服务器发送 EXEC 命令时,这个 EXEC 命令将立即被服务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得的结果全部返回给客户端。