不能回滚的Redis事务还能用吗

事务是关系型数据库的特征之一,那么作为 Nosql 的代表 Redis 中有事务吗?如果有,那么 Redis 当中的事务又是否具备关系型数据库的 ACID 四大特性呢?

Redis 有事务吗

这个答案可能会令很多人感到意外,Redis 当中是存在“事务”的。这里我把 Redis 的事务带了引号,原因在后面分析。

Redis 当中的单个命令都是原子操作,但是如果我们需要把多个命令组合操作又需要保证数据的一致性时,就可以考试使用 Redis 提供的事务(或者使用前面介绍的 Lua 脚本)。

Redis 当中,通过下面 4 个命令来实现事务:

multi:开启事务

exec:执行事务

discard:取消事务

watch:监视

Redis 的事务主要分为以下 3 步:

执行命令 multi 开启一个事务。

开启事务之后执行的命令都会被放入一个队列,如果成功之后会固定返回 QUEUED。

执行命令 exec 提交事务之后,Redis 会依次执行队列里面的命令,并依次返回所有命令结果(如果想要放弃事务,可以执行 discard 命令)。

接下来让我们依次执行以下命令来体会一下 Redis 当中的事务:

multi //开启事务 set name lonely_wolf //设置 name,此时 Redis 会将命令放入队列 set age 18 //设值 age,此时 Redis 会将命令放入队列 get name //获取 name,此时 Redis 会将命令放入队列 exec //提交事务,此时会依次执行队列里的命令,并依次返回结果

执行完成之后得到如下效果:

不能回滚的Redis事务还能用吗

Redis 事务实现原理

Redis 中每个客户端都有记录当前客户端的事务状态 multiState ,下面就是一个客户端 client 的数据结构定义:

typedef struct client { uint64_t id;//客户端唯一 id multiState mstate; //MULTI 和 EXEC 状态(即事务状态) //...省略其他属性 } client;

multiState 数据结构定义如下:

typedef struct multiState { multiCmd *commands;//存储命令的 FIFO 队列 int count;//命令总数 //...省略了其他属性 } multiState;

multiCmd 是一个队列,用来接收并存储开启事务之后发送的命令,其数据结构定义如下:

typedef struct multiCmd { robj **argv;//用来存储参数的数组 int argc;//参数的数量 struct redisCommand *cmd;//命令指针 } multiCmd;

我们以上面事务的示例截图中事务为例,可以得到如下所示的一个简图:

不能回滚的Redis事务还能用吗

Redis 事务 ACID 特性

传统的关系型数据库中,一个事务一般都具有 ACID 特性。那么现在就让我们来分析一下 Redis 是否也满足这 ACID 四大特性。

A - 原子性

在讨论事务的原子性之前,我们先来看 2 个例子。

模拟事务在执行命令前发生异常。依次执行以下命令:

multi //开启事务 set name lonely_wolf //设置 name,此时 Redis 会将命令放入队列 get //执行一个不完成的命令,此时会报错 exec //在发生异常后提交事务

最终得到了如下图所示的结果,我们可以看到,当命令入队的时候报错时,事务已经被取消了:

不能回滚的Redis事务还能用吗

模拟事务在执行命令前发生异常。依次执行以下命令:

flushall //为了防止影响,先清空数据库 multi //开启事务 set name lonely_wolf //设置 name,此时 Redis 会将命令放入队列 incr name //这个命令只能用于 value 为整数的字符串对象,此时执行会报错 exec //提交事务,此时在执行第一条命令成功,执行第二条命令失败 get name //获取 name 的值

最终得到了如下图所示的结果,我们可以看到,当执行事务报错的时候,之前已经成功的命令并没有被回滚,也就是说在执行事务的时候某一个命令失败了,并不会影响其他命令的执行,即 Redis 的事务并不会回滚

不能回滚的Redis事务还能用吗

Redis 中的事务为什么不会滚

这个问题的答案在 Redis 官网中给出了明确的解释:

不能回滚的Redis事务还能用吗

总结起来主要就是 3 个原因:

Redis 作者认为发生事务回滚的原因大部分都是程序错误导致,这种情况一般发生在开发和测试阶段,而生产环境很少出现。

对于逻辑性错误,比如本来应该把一个数加 1 ,但是程序逻辑写成了加 2,那么这种错误也是无法通过事务回滚来进行解决的。

Redis 追求的是简单高效,而传统事务的实现相对比较复杂,这和 Redis 的设计思想相违背。

C - 一致性

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppsdw.html