文章目录
1、什么是事务
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务功能。Redis 的事务提供了一种将多个命令请求打包,按顺序放入事务队列,然后一次性、顺序性、排他性地执行这些命令。即在事务执行期间,服务器是不会中断事务而去处理其他客户端的命令请求的,它会将事务中的所有命令都执行完毕后,再去处理其他客户端的请求。
2、Redis 事务的相关命令
- MULTI:用于标识一个事务的开始,将执行该命令的客户端从非事务状态切换至事务状态,这一切换是通过在客户端状态的 flags 属性中打开 REDIS_MULTI 标识来完成的。
- EXEC:执行事务队列中的所有命令。
- WATCH:用于标记要监视的 key,以便有条件地执行事务。WATCH 命令可以监控一个或多个键,一旦监视的键被修改 / 删除,事务将被拒绝执行。
- UNWATCH:取消 WATCH 命令对所有 key 的监视,如果执行 EXEC 或 DISCARD 命令,则无需再执行 UNWATCH 命令。【因为执行 EXEC 或 DISCARD 命令的同时也取消了对所有 key 的监视】
- DISCARD:取消事务的命令,放弃执行事务队列内的所有命令,客户端状态恢复为非事务状态;如果正在使用 WATCH 命令监视某个(或某些)key,那么取消所有监视,等同于执行命令 UNWATCH。
3、事务的实现
3.1 事务开始
3.2 命令入队
当一个客户端处于非事务状态时,这个客户端发送的命令会被服务器立即执行。当一个客户端切换到事务状态后,服务器会根据这个客户端发来的不同命令执行不同的操作。
3.2.1 事务队列
每个 Redis 客户端都有自己的事务状态,对应的是客户端状态(redisClient)的 mstate 属性。
typedef struct redisClient{
multiState mstate; //事务状态
}
redisClient;
事务状态(mstate)包含一个事务队列(FIFO 队列),以及一个已入队命令的计数器。
typedef struct multiState {
// 事务队列,FIFO 顺序
multiCmd *commands;
/* Array of MULTI commands */
// 已入队命令计数
int count;
//......
} multiState;
事务队列是一个 multiCmd 类型数组,数组中每个 multiCmd 结构都保存了一个入队命令的相关信息:指向命令实现函数的指针,命令的参数,以及参数的数量。
typedef struct multiCmd {
// 参数
robj **argv;
// 参数数量
int argc;
// 命令指针
struct redisCommand *cmd;
} multiCmd;
事务队列以先进先出(FIFO)的方式保存入队的命令,较先入队的命令会被放在数组的前面。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET "name" "Practical Commom Lisp"
QUEUED
127.0.0.1:6379> GET "name"
QUEUED
127.0.0.1:6379> SET "author" "Peter Seibel"
QUEUED
127.0.0.1:6379> GET "author"
QUEUED
如果命令入队出错时,会打开客户端状态 REDIS_DIRTY_EXEC。
3.3 事务执行
客户端发送 EXEC 命令,服务器执行 EXEC 命令逻辑。
- 如果客户端状态的 flags 属性不包含 REDIS_MULTI 标识,或者包含 REDIS_DIRTY_CAS 或者 REDIS_DIRTY_EXEC 标识,那么就直接取消事务的执行。【REDIS_DIRTY_EXEC --> 入队命令出错,REDIS_DIRTY_CAS --> 监视的键被修改 / 删除】
- 否则客户端处于事务状态(flags 有 REDIS_MULTI 标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;
4、WATCH 命令实现
4.1 监视机制的触发
5、Redis 事务的 ACID 性质
https://zhuanlan.zhihu.com/p/144268090
5.1 原子性
5.2 一致性
5.3 隔离性
5.4 持久性
X、小结
Redis 事务总具有 ACID 中的原子性、一致性和隔离性,当服务器运行再 AOF 持久化模式下,并且 appendfsync 选项的值为 always 时,事务也具有持久性,否则不具持久性。