Skip to content

事务相关命令有哪些?

  • MULTI:事务正式开始,之后除了事务相关的命令都会被存储到事务队列里。
  • EXEC:提交事务,执行存储在事务队列中的所有命令并返回所有结果。
  • WATCH:对某一个键进行监视,如果被监视的键被其他客户端修改,那么当该客户端执行事务的时候就会被服务器拒绝。

事务的底层结构?

答:队列。Redis 为每一个客户端都维护着一个事务状态属性,这个属性里包含了一个事务队列,事务队列就是一个数组,数组里存放的就是命令相关的信息,包括命令参数、参数数量、命令函数指针等。

c
typedef struct client {
	multiState mstate;      /* 事务状态属性 */
}
c
typedef struct multiState {
    multiCmd *commands;     /* 事务队列数组 */
    int count;              /* 事务队列数组的大小 */
} multiState;
c
typedef struct multiCmd {
    robj **argv;	/* 参数 */
    int argc;	/* 参数数量 */
    struct redisCommand *cmd;	/* 命令指针 */
} multiCmd;

MULTI 命令的实现原理?

答:执行 MULTI 命令后,服务器会打开客户端状态的 flags 属性的CLIENT_MULTI标识,来将该客户端切换至事务状态。切换到事务状态后,服务器会将客户端发送过来的非事务相关的命令放到事务队列里。

c
/* Client flags */
#define CLIENT_MULTI (1<<3)   /* This client is in a MULTI context */
c
typedef struct client {
    uint64_t flags;		/* Client flags: CLIENT_* macros. */
}

打开标识:client.flags |= CLIENT_MULTI


EXEC 命令的实现原理?

答:执行 EXEC 命令后,服务器会遍历该客户端的事务队列,执行队列中保存的所有命令,并将命令所执行的所有结果返回。同时,清空事务队列,移除CLIENT_MULTI事务标识。

移除标识:client.flags &= ~CLIENT_MULTI


WATCH 命令的实现原理?

答:每个 Redis 数据库都维护着一个存储被监视键的字典,字典的键是被监视的数据库键,值是一个链表,存储监视这个键的客户端。执行 WATCH 命令后,服务器会将监视该键的客户端添加到对应监视链表的末尾。在每次有其他客户端对数据库执行修改命令后,服务器会调用函数touchWatchedKey()检查被修改的键是否存在于字典里,如果存在,就遍历该键对应的客户端链表,打开每个客户端的CLIENT_DIRTY_CAS标识,表示该客户端的事务安全性已经遭到破坏,之后执行 EXEC 命令就会被拒绝。

c
typedef struct redisDb {
    dict *watched_keys;         /* 正在被 WATCH 命令监视的键 */
}
c
/* Client flags */
#define CLIENT_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */
c
/* "Touch" a key, so that if this key is being WATCHed by some client the
 * next EXEC will fail. */
void touchWatchedKey(redisDb *db, robj *key) {}

Redis 事务具备 ACID 吗?

答:Redis 事务具备 ACID 中的原子性、一致性、隔离性,当服务器处于 AOF 持久化模式下,并且采取每次执行命令后都同步策略(某个选项设置为 always),才具备耐久性。


Redis 支持事务回滚吗?

答:不支持。因为作者认为:事务回滚复杂度太高,与 Redis 追求的简单高效的主旨违背。其次,Redis 事务所产生的错误都是编程错误,而且只会出现在开发环境中,不会出现在生产环境中。所以没有必要为 Redis 开发事务回滚功能。


Early Access