Redis 事务实现和乐观锁
1. 事务的介绍
Redis
事务(transaction)提供了以下五个命令,用于用户操作事务功能,其分别是:
命令 | 功能 |
---|---|
MULTI | 标记一个事务块的开始 |
DISCARD | 放弃执行事务 |
EXEC | 执行事务中的所有命令 |
WATCH | 监视一个或多个key,如果至少有一个key在EXEC之前被修改,则放弃执行事务 |
UNWATCH | 取消WATCH命令对所有键的监视 |
- 事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务的执行期间,服务器不会打断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去执行其他客户端的命令请求。
- Redis
中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis
中的最小执行单位,一个事务中的命令要么都执行,要么都不执行。
事务命令的使用方法请看:Redis 事务命令 。
Redis 事务源码详细注释
2. 事务的实现
执行事务的过程分为以下的几个阶段:
- 开始事务
- 命令入队
- 执行事务
2.1 开始事务
在客户端执行一个MULTI
命令,标记一个事务块的开始。该命令会被封装成Redis
协议的格式发送给服务器,服务器接收到该命令会调用multiCommand()
函数来执行。函数源码如下:
void multiCommand(client *c) {
// 客户端已经处于事务状态,回复错误后返回
if (c->flags & CLIENT_MULTI) {
addReplyError(c,"MULTI calls can not be nested");
return;
}
// 打开客户的的事务状态标识
c->flags |= CLIENT_MULTI;
// 回复OK
addReply(c,shared.ok);
}
该函数首先先会判断当前客户端是否处于事务状态(CLIENT_MULTI),如果没有处于事务状态,那么会打开客户端的事务状态标识,并且回复客户端一个OK
。
执行完MULTI
命令,表示着一个事务的开始。
2.2 命令入队
由于事务的命令是一次性、按顺序的执行,因此,需要将客户端中的所有事务命令事前保存起来。Redis 事务源码详细注释
在每个描述客户端状态的结构struct client
中,保存着有关事务状态的成员变量,他的定义如下:
typedef struct client {
// 事物状态
multiState mstate;
} client;
multiState
是一个结构体,定义如下:
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;
重点关注前两个属性。
commands
成员是一个指针,保存的是一个成员类型为multiCmd
的数组首地址,每一个成员保存的都是事务命令。count
成员保存的是以commands
为地址的数组的成员个数。