事务相关命令有哪些?
- MULTI:事务正式开始,之后除了事务相关的命令都会被存储到事务队列里。
- EXEC:提交事务,执行存储在事务队列中的所有命令并返回所有结果。
- WATCH:对某一个键进行监视,如果被监视的键被其他客户端修改,那么当该客户端执行事务的时候就会被服务器拒绝。
事务的底层结构?
答:队列。Redis 为每一个客户端都维护着一个事务状态属性,这个属性里包含了一个事务队列,事务队列就是一个数组,数组里存放的就是命令相关的信息,包括命令参数、参数数量、命令函数指针等。
typedef struct client {
multiState mstate; /* 事务状态属性 */
}
typedef struct multiState {
multiCmd *commands; /* 事务队列数组 */
int count; /* 事务队列数组的大小 */
} multiState;
typedef struct multiCmd {
robj **argv; /* 参数 */
int argc; /* 参数数量 */
struct redisCommand *cmd; /* 命令指针 */
} multiCmd;
MULTI 命令的实现原理?
答:执行 MULTI 命令后,服务器会打开客户端状态的 flags 属性的CLIENT_MULTI
标识,来将该客户端切换至事务状态。切换到事务状态后,服务器会将客户端发送过来的非事务相关的命令放到事务队列里。
/* Client flags */
#define CLIENT_MULTI (1<<3) /* This client is in a MULTI context */
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 命令就会被拒绝。
typedef struct redisDb {
dict *watched_keys; /* 正在被 WATCH 命令监视的键 */
}
/* Client flags */
#define CLIENT_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */
/* "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 开发事务回滚功能。