广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >Redis事务为什么不支持回滚
  • 633
分享到

Redis事务为什么不支持回滚

2024-04-02 19:04:59 633人浏览 安东尼
摘要

目录前言Redis 有事务吗Redis 事务实现原理Redis 事务 ACID 特性A - 原子性 C - 一致性I - 隔离性D - 持久性watch 命令watch 命

前言

事务是关系型数据库的特征之一,那么作为 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 中每个客户端都有记录当前客户端的事务状态 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 事务 ACID 特性

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

A - 原子性

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

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


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

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

在这里插入图片描述

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


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

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

在这里插入图片描述

Redis 中的事务为什么不会滚

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

在这里插入图片描述

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

  • Redis 作者认为发生事务回滚的原因大部分都是程序错误导致,这种情况一般发生在开发测试阶段,而生产环境很少出现。
  • 对于逻辑性错误,比如本来应该把一个数加 1 ,但是程序逻辑写成了加 2,那么这种错误也是无法通过事务回滚来进行解决的。
  • Redis 追求的是简单高效,而传统事务的实现相对比较复杂,这和 Redis 的设计思想相违背。

 C - 一致性

一致性指的就是事务执行前后的数据符合数据库的定义和要求。这一点 Redis 中的事务是符合要求的,上面讲述原子性的时候已经提到,不论是发生语法错误还是运行时错误,错误的命令均不会被执行。

I - 隔离性

事务中的所有命令都会按顺序执行,在执行 Redis 事务的过程中,另一个客户端发出的请求不可能被服务,这保证了命令是作为单独的独立操作执行的。所以 Redis 当中的事务是符合隔离性要求的。

D - 持久性

如果 Redis 当中没有被开启持久化,那么就是纯内存运行的,一旦重启,所有数据都会丢失,此时可以认为 Redis 不具备事务的持久性;而如果 Redis 开启了持久化,那么可以认为 Redis 在特定条件下是具备持久性的。

watch 命令

上面我们讲述 Redis 中事务时,提到的的常用命令还有一个 watch 命令,这个又是做什么用的呢?我们还是先来看一个例子。

首先打开一个客户端一,依次执行以下命令:


flushall  //清空数据库
multi     //开启事务
get name  //获取 name,此时正常返回 nil
set name lonely_wolf //设置 name
get name //获取 name,此时正常应该返回 lonely_wolf

得到如下效果图:

在这里插入图片描述

这时候我们先不执行事务,打开另一个客户端二,来执行一个命令 set name zhangsan

在这里插入图片描述

客户端二执行成功了,这时候再返回到客户端一执行 exec 命令:

在这里插入图片描述

可以发现,第一句话返回了 zhangsan。也就是说,name 这个 key 值在入队之后到 exec 之前发生了变化,一旦发生这种情况,可能会引起很严重的问题,所以在关系型数据库可以通过来解决这种问题,那么 Redis 当中试如何解决的呢?

是的,在 Redis 当中就是通过 watch 命令来处理这种场景的。

watch 命令的作用

watch 命令可以为 Redis 事务提供 CAS 乐观锁行为,它可以在 exec 命令执行之前,监视任意 key 值的变化,也就是说当多个线程更新同一个 key 值的时候,会跟原值做比较,一旦发现它被修改过,则拒绝执行命令,并且会返回 nil 给客户端。

下面还是让我们通过一个示例来演示一下。

打开一个客户端一,依次执行如下命令:


flushall  //清空数据库
watch name //监视 name
multi     //开启事务
set name lonely_wolf //设置 name
set age 18 // 设置 age
get name   //获取 name
get age    //获取 age

执行之后得到如下效果图:

在这里插入图片描述

这时候再打开一个客户端二,执行 set name zhangsan命令:

在这里插入图片描述

然后再回到客户端一执行 exec命令。这时候会发现直接返回了 nil,也就是事务中所有的命令都没有被执行(即:只要检测到一个 key 值被修改过,那么整个事务都不会被执行):

在这里插入图片描述

watch 原理分析

下面是一个 Redis 服务的数据结构定义:


typedef struct redisDb {
    dict *watched_keys;  //被 watch 命令监视的 key
    int id;           //Database ID
    //...省略了其他属性
} redisDb;

可以看到,redisDb 中的 watched_keys 存储了一个字典,这个字典当中的 key 存的就是被监视的 key ,然后字典的值存的就是客户端 id。然后每个客户端还有一个标记属性 CLIENT_DIRTY_CAS,一旦我们执行了一些如 setsadd 等能修改 key 值对应 value 的命令,那么客户端的 CLIENT_DIRTY_CAS 标记属性将会被修改,后面执行事务提交命令 exec 时发现客户端的标记属性被修改过(乐观锁的体现),则会拒绝执行事务。

总结

本文主要介绍了 Redis 当中的事务机制,在介绍事务实现原理的同时从传统关系型数据库的 ACID 四大特性对比分析了 Redis 当中的事务,并最终了解到了 Redis 的事务似乎并不是那么“完美”。

到此这篇关于Redis事务为什么不支持回滚 的文章就介绍到这了,更多相关Redis事务回滚 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Redis事务为什么不支持回滚

本文链接: https://www.lsjlt.com/news/132187.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Redis事务为什么不支持回滚
    目录前言Redis 有事务吗Redis 事务实现原理Redis 事务 ACID 特性A - 原子性 C - 一致性I - 隔离性D - 持久性watch 命令watch 命...
    99+
    2022-11-12
  • redis不支持事务回滚如何解决
    Redis本身是单线程的,并且它使用了类似于命令队列的机制来处理客户端请求。这导致了Redis的事务是部分支持事务回滚的。在Redi...
    99+
    2023-09-16
    redis
  • redis中能不能支持回滚
    小编给大家分享一下redis中能不能支持回滚,希望大家阅读完这篇文章后大所收获,下面让我们一起去探讨吧!如果你具备关系型数据库的知识背景,你就会发现一个事实:在事务运行期间,虽然Redis命令可能会执行失败...
    99+
    2022-10-18
  • redis中支不支持事务
    小编给大家分享一下redis中支不支持事务,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Redis是一个开源的使用ANSI C语...
    99+
    2022-10-18
  • mongodb为什么不支持事务
    MongoDB不支持事务的主要原因是为了追求高性能和可伸缩性。事务需要锁定数据,并保证数据的一致性,这会限制并发性能和增加系统的复杂...
    99+
    2023-09-04
    mongodb
  • 为什么nodejs不支持import
    这篇文章主要讲解了“为什么nodejs不支持import”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“为什么nodejs不支持import”吧! ...
    99+
    2022-10-19
  • 为什么redis支持多个数据库
    为什么redis支持多个数据库?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。注意:Redis支持多个数据库,并且每个数据库的数据是隔离的不...
    99+
    2022-10-18
  • php事务回滚不了怎么办
    本教程操作环境:windows7系统、PHP8.1版、Dell G3电脑。php事务回滚不了怎么办?问题描述:关于php 事务未执行回滚mysqliphptransactions Transactions not doing the rol...
    99+
    2022-10-20
  • 微软为什么不支持php8
    微软不支持php8的原因:1、战略调整;2、市场竞争;3、技术兼容性;4、社区反馈;5、资源分配;6、技术风险;7、生态系统的考虑;8、法律和授权问题。详细介绍:1、战略调整,微软可能认为PHP在其整体战略中的地位较低,因此没有将PHP8的...
    99+
    2023-12-21
    微软 PHP8
  • Python为什么不支持switch语句
    一、简洁性和可读性 Python的设计哲学之一是代码的简洁性和可读性。引入”switch”语句可能会增加语言的复杂性,并且可以用其他方式更清晰地表达相同的逻辑。Python鼓励使用”if-elif-else”语句来处理多个条件情况,这种方式...
    99+
    2023-10-29
    不支持 语句 Python
  • 为什么Java不支持多继承
    这篇文章主要介绍“为什么Java不支持多继承”,在日常操作中,相信很多人在为什么Java不支持多继承问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”为什么Java不支持多继承”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-15
  • 为什么Python不支持i ++语法
    本篇内容介绍了“为什么Python不支持i ++语法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!正常情况下,当有人问起++原因而不是Pyt...
    99+
    2023-06-16
  • input为什么不支持伪元素
    input 元素是 HTML 表单中的一个表单控件,用于接收用户的输入。它是一个自闭合标签,没有内容,因此不支持伪元素。伪元素是用于在 CSS 中选择和样式化元素的特殊选择器,例如 `::before` 和 `::after`。伪元素可以用...
    99+
    2023-10-21
    input 伪元素
  • 为什么GO不支持循环引用
    目录1、案例演示2、原因分析3、总结学习 Go 语言的开发者越来越多了,很多小伙伴在使用时,就会遇到种种不理解的问题。 其中一点就是包的循环引用的报错: package comm...
    99+
    2022-11-12
  • MySQL为什么不支持中文排序?
    前言 或许都知道,MySQL不支持中文排序,这样的说法可以说对也可以说也不对。接下来我们分析一下: 首先执行命令,查看编码集: SHOW VARIABLES LIKE "character_set%"; 可以看到字符集是utf8mb4,...
    99+
    2020-10-28
    MySQL为什么不支持中文排序?
  • python为什么不支持函数重载
    python中不支持函数重载的原因函数重载主要是为了解决可变参数类型和可变参数个数两个问题。而python中可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数,从而...
    99+
    2022-10-06
  • Kafka为什么不支持读写分离
    本篇文章为大家展示了Kafka为什么不支持读写分离,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实...
    99+
    2023-06-16
  • 为什么说php不支持unicode编码
    本篇内容介绍了“为什么说php不支持unicode编码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!php不支持unicode是指PHP字符...
    99+
    2023-06-20
  • PHP7.3为什么不支持curl?怎么解决?
    PHP是一种著名的互联网编程语言,由于其强大的功能和易用性,广泛应用于Web开发,并且在世界各地的网站中得到了广泛的使用。然而,在PHP7.3中,我们发现curl之类的一些常用工具无法正常使用。那么,PHP7.3为什么不支持curl?有什么...
    99+
    2023-05-14
    php curl
  • mysql事务回滚的原理是什么
    MySQL中的事务回滚是通过将数据库恢复到事务开始之前的状态来实现的。事务回滚的原理如下:1. MySQL使用日志来记录所有对数据库...
    99+
    2023-10-09
    mysql
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作