还不懂Redis事务?看看我的总结应该就懂了

Redis事务原理分析



前言

最近一直在看书,但是看书自己也依然是个菜鸡(我好难),我也想成为大佬。

在这里插入图片描述


# 一、事务

事务是一种特性,可以理解为将多个命令打包为一个事务执行的情况,在mysql中就存在事务,我们了解的MySQL中的事务有四大特性:原子性、一致性、隔离性、持久性。而在redis中也存在着事务,也具有相同的四大特性,现在就来了解redis是如何实现事务的。

二、事务的分析

在Redis中通过MULTI、EXEC、WATCH等命令来实现事务功能。Redis中的事务功能也是讲多个命令打包,然后一次性、按顺序的执行多个命令,并且事务执行期间,Redis服务器不会中断事务改去执行其它客户端的命令请求。Redis会将事务中的所有命令执行完毕,然后才去处理其它客户端的命令请求。

举例1:
在这里插入图片描述
可以看到在以上例子中通过MULTI来开始一个事务,然后执行四个命令,最后通过EXEC来将事务提交给服务器执行,最后得到执行结果。

三、事务的实现

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

1)事务开始

2)命令入队

3)事务执行

3.1、事务的开始

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

localhost:0>MULTI
"OK"

MULTI命令可以将执行该命令的客户端从非事务状态切换至事务状态,这一切换是通过在客户端状态的flags属性中打开REDIS_MULTI标识来完成的,MULTI命令的实现可以通过以下伪代码表示:

def MULTI():
  #打开事务标识
  client.flags |= REDIS_MULTI
  #返回OK回复
  replyOK()

3.2、命令入队

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

如下:

localhost:0>SET "name" "test"
"OK"
localhost:0>GET "name"
"OK"
localhost:0>SET "author" "testAuthor"
"OK"
localhost:0>GET "author"
"OK"

当一个客户端切换到事务状态时,服务器会根据这个客户端发送的不同命令执行不同的操作:

如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令中的其中一个,那么服务器会立即执行这个命令。

与此相反,如果客户端发送的不是这四个命令的一个,那么服务器并不会立即执行这个命令,而是将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复。

通过一个流程图可以理解:
在这里插入图片描述

3.3、事务队列

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

typedef struct redisClient{
//......
//事务状态
multiState mstate;
//.....
}redisClient;

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

typedef struct multiState{
//事务队列,FIFO顺序
multiCmd  *commands;
//已入队命令计数器
int count;
}multiState;

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

typedef struct multiCmd{
//参数
robj **argv;
//参数数量
int argc;
//命令指针
struct redisCommand *cmd;
}multiCmd;

事务队列以先进先出(FIFO)的方式保存入队的命令,在事务执行时,最先放入事务队列中的命令最先被执行。

3.4、执行事务

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

因此在举例1中可以知道,redis服务器会先执行SET “name” “test”,然后再执行GET “name”,之后执行SET “author” “testAuthor”,最后执行GET “author”。按照输入的顺序执行,最后的结果也会按照执行顺序返回给客户端。

四、WATCH命令的实现

WATCH命令是一个乐观锁,它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果被修改了,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复。

举例2:
在这里插入图片描述在这里插入图片描述

上图中图一为redis客户端1,图二为redis客户端2,在客户端1中先执行WATCH命令监控name键,然后执行MULTI开始事务,执行命令set “name” “tom”,到此为止在客户端2中执行set “name” “chentom”,最后回到客户端1中执行EXEC执行事务,最后返回空,并没有返回"OK"。

这是因为在客户端1中执行了修改"name"键的 命令,然后客户端2中也执行修改"name"键的命令,这样客户端1中的"name"键值并不是自己所要设定的值了,所以为了避免这种情况,redis服务就会拒绝执行客户端1的事务,并向客户端1返回空回复。

4.1、使用WATCH命令监视数据库键

每个Redis数据库都保存着一个watched_keys字典,这个字典的键是被WATCH命令监视的数据库键,而字典的值则是一个链表,这个链表中记录了所有监视相应数据库的客户端。

typedef struct redisDb{
//.....
//正在被WATCH命令监视的键
dict *watched_keys;
//.....
}redisDb;

通过watched_keys字典,服务器可以清楚的知道哪些键正在被监视,以及这些键正在被哪些客户端使用。

从举例2中可以知道WATCH监视键"name",而"name"正在被客户端1和客户端2使用。

4.2、监视机制的触发

所有对数据库进行修改的命令,如SET、LPUSH、SADD、ZREM、DEL、FLUSHDB等等,在执行之后都会调用multi.c/touchWatchKey函数对watched_keys进行检查,查看是否有客户端执行了MULTI命令监视当前操作的键,如果有,那么touchWatchKey函数会将正在监视当前操作键的客户端的REDIS_DIRTY_CAS标识打开,标识该客户端的事务安全被破坏。

以举例2来说:

在举例2中客户端1正在监视键"name",然后客户端2执行了对键"nam"的SET命令,那么touchWatchKey函数就会将客户端1的REDIS_DIRTY_CAS打开,客户端1的事务安全就被破坏。

4.3、判断事务是否安全

当服务器收到一个客户端发送的EXEC命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行该客户端提交的事务:

如果REDIS_DIRTY_CAS关闭,那么说明客户端监视的键没有被其它客户端所修改,事务是安全的,服务器将执行客户端提交的事务操作。

如果REDIS_DIRTY_CAS打开,那么说明客户端监视的所有键中至少有一个键已经被其它客户端修改过了,因此事务是不安全的,所以服务器就会拒绝执行该客户端提交的事务。
在这里插入图片描述


五、总结

5.1、错误命令问题

当执行multi命令之后,在后续输入错误命令时,那么在2.6.5之前也是会将该错误命令加入事务队列中,最后执行exec时,会报出异常错误。
在这里插入图片描述

而在2.6.5之后Redis不会将这些错误命令加入到事务队列中,那么事务就会执行正确的命令。

5.2、执行过程中发生错误问题

事务在执行过程中,如果命令操作某个键的类型不对,那么就会出现执行过程中发生错误的问题。

示例如下:
在这里插入图片描述
可以看到第二条命令执行时,msg的类型错误,那么最后提交事务执行时,第二条命令的执行会报错,但是整个事务会执行成功,不会对事务的执行造成影响。

5.3、服务器停机问题

当事务在执行时,如果服务器出现停机,那么根据服务器使用的持久化机制,会出现以下几种情况:

1)如果服务器运行在无持久化的服务器上,那么重启之后数据库将是空白的。

2)如果是服务器运行在RDB持久化模式下,事务中途停机不会导致不一致性,这是因为服务器会根据现有的RDB文件来恢复数据,最终都会恢复到一致的状态。

3)如果是服务器运行在AOF持久化模式下,那么事务中途停机也不会导致不一致性,因为服务器会根据AOF文件来恢复数据,最终也是会恢复到一个一致状态。

所以Redis不论在哪种持久化模式下,事务执行过程中发生停机都不会影响数据库一致性。

最后希望看到文章的各位大佬如果本篇文章有不对的点能给予指正。万分感谢!当然了觉得可以给个关注就更好了!!!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值