Redis中的事务

2023-11-02

1.Redis事务的定义

Redis事务是一个单独的隔离操作,事务中的所有命令都会序列化,然后按照顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
其实就是一个缓存队列,将所有任务放入,然后再某一个阶段,将其中的任务拿出来依次执行。
Redis事务和关系型数据库的事务不太一样,它不保证原子性,也没有隔离级别的概念,放入缓存队列中的任务并不会被执行,更不会返回什么。
在Redis中单条命令是原子性的但是事务并不是原子性的,事务其中一条命令执行失败其他的任务照常执行,更不存在回滚。

(1) 关于队列

:队列(Queue)简称队,是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队,删除元素称为出队或离队。其操作特性为先进先出(First In First Out,FIFO),并且只允许在队尾进,队头出。

(2) 关于原子性

:原子性(Atomicity)在场景中理解就是一个不会被中断的线程操作,这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。在单线程中,能够在单条指令中完成的操作都可以认为是"原子操作(atomic operation)",因为中断只能发生于指令之间。在多线程中,不能被其它进程x线程打断的操作就叫原子操作。
Redis单命令的原子性主要得益于Redis的单线程。

2. Multi / Exec / discard

Multi / Exec / discard 三条命令都是Redis操作事务使用的命令,从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。

(1) Redis事务的三个阶段

  1. 第一阶段: 开启事务
  2. 第二阶段: 命令入队
  3. 第三阶段: 执行事务

小编画了一张图,希望有助于大家的理解,下面的内容也将按照这张图片来讲解。
在这里插入图片描述

(2) Multi

执行 Multi 命令,标识一个事务的开启,Redis进入组队阶段,接下来执行的命令都将放入一个缓存队列,注意命令并不会被执行只是加入缓存队列,只有执行Exec命令队列里面的命令才会按照顺序一一开始执行,从而Redis的事务就不会导致数据脏读,不可重复读,幻读的问题,因此就没有隔离级别。

本地Redis:0>Multi // 开启事务
"OK"
本地Redis:0>set j1 1 // 命令组队
"QUEUED" // 组队成功
本地Redis:0>set j2 2
"QUEUED"
本地Redis:0>Exec // 开始执行事务中的命令
1) "OK"
2) "OK"

注意: 在开始事务后组队阶段某个命令出现了报告错误,整个队列都将会被取消,缓存队列中的命令全部作废取消

本地Redis:0>Multi // 开启事务
"OK" // 开启成功
本地Redis:0>set k1 1 // 命令入队
"QUEUED" // 入队成功
本地Redis:0>set k2 // 没有设置value 命令错误
"ERR wrong number of arguments for 'set' command" // 发生报错 整个队列取消
本地Redis:0>

(3) discard

在命令组队阶段可以执行 discard 命令,放弃在组队的事务,对于已经存在缓存队列中的命令不会执行全部作废,关闭当前事务

本地Redis:0>Multi // 开启事务
"OK" // 开启成功
本地Redis:0>set j1 1 // 命令入队
"QUEUED" // 入队成功
本地Redis:0>set j2 2
"QUEUED"
本地Redis:0>discard  // 中断关闭事务
"OK" // 关闭成功
本地Redis:0>

(4) Exec

在开始事务后,要入队的命令全部入队后,可以执行Exec命令,缓存队列中的命令将会按照顺序一一执行

本地Redis:0>Multi // 开启事务
"OK"
本地Redis:0>set j1 1 // 命令组队
"QUEUED" // 组队成功
本地Redis:0>set j2 2
"QUEUED"
本地Redis:0>Exec // 开始执行事务中的命令
1) "OK"
2) "OK"

注意: 在执行完Exec命令队列里执行的命令发生错误的话,只有发生错误的命令不执行,其他没有报错的命令照常执行

本地Redis:0>Multi // 开启事务
"OK"
本地Redis:0>set v1 v1 // 命令入队
"QUEUED"
本地Redis:0>incr v1 // 字符串值无法加一 这条命令是有问题的
"QUEUED" // 命令入队成功
本地Redis:0>Exec // 开始执行命令
1) "OK"
2) "OK" // 其他命令照常执行
3) "OK"
4) "ERR value is not an integer or out of range" // 其中一条命令发生错误
5) "OK"
本地Redis:0>

3.悲观锁和乐观锁

接下来要讲解的WATCHUNWATCH两条命令会设置到悲观锁和乐观锁两个概念,所以小编在这里简单介绍一下悲观锁和乐观锁。

(1) 悲观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
但是在效率方面,处理加锁的机制会产生额外的开销,还有增加产生死锁的机会。另外还会降低并行性,如果已经锁定了一个线程A,其他线程就必须等待该线程A处理完才可以处理。

(2) 乐观锁

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本(version)或者是时间戳来实现,不过使用版本记录是最常用的。
乐观控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。

(3) 总结

悲观锁阻塞事务,乐观锁回滚重试:它们各有优缺点,不要认为一种一定好于另一种。像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行重试,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

4. WATCH / UNWATCH

(1) WATCH key [ key…]

在执行multi命令之前,可以先执行WATCH key (监视一个或者多个key),如果在事务执行之前watch监视的一个或者多个key被其他命令改动,那么事务将会被打断,有点类似我们上面介绍的乐观锁

(2) UNWATCH

取消 WATCH 命令对所有 key 的监视,如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。

5.Redis中的三个特性

(1) 单独隔离操作

事务中的所有命令都会序列化,按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

(2) 没有隔离级别的概念

队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行

(3) 不保证原子性

事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

6. 结尾

那么关于Redis中的事务小编就介绍到这里了,如果有错误的地方或者需要补充的地方,各位读者朋友可以在评论区提出来,后续专栏将会推出更多优质内容,期待的小伙伴可以给小编点个关注。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis中的事务 的相关文章

  • 为什么我不能让单个 Redis 客户端在同一连接中充当 PUB 和 Sub ?

    我的思维模型是 聊天 我订阅了某个频道 并且可以向该频道发布消息 由于 pub sub 是异步的 因此发布的消息可能随时出现 包括当您期望命令响应时 尽管 Redis 是单线程的 通常会阻止此类事情 但网络延迟可能会导致一些有趣的影响 根据
  • 在节点中使用redis获取hash key的所有字段和值

    红色是使用哈希 我需要存储具有多个字段和值的哈希键 我尝试如下 client hmset Table1 Id 9324324 ReqNo 23432 redis print client hmset Table1 Id 9324325 Re
  • 如何在实时添加对象时从 Redis 中弹出对象?

    我想让 Node js 进程运行 因为它正在检查 Redis 服务器是否有任何新的弹出内容 另一个进程将偶尔进行推送 而 Node 进程将尝试弹出任何进来的内容 Node 进程将保持运行 有人能给我指出一个好的方向吗 我正在尝试找出如何监听
  • 是否可以使用带有 FUSE 文件系统的 Linux VFS 缓存?

    默认情况下 Linux VFS 缓存似乎不适用于 FUSE 文件系统 例如 read 调用似乎被系统地转发到 FUSE 文件系统 我在 FUSE 特定的远程文件系统上工作 我需要一个非常积极的缓存 我需要实现自己的页面缓存吗 或者是否可以为
  • 连接到 localhost:6379 时出现错误 99。无法分配请求的地址

    设置 我有一个虚拟机 并在虚拟机中运行三个容器 一个 nginx 代理 一个非常简约的 Flask 应用程序和 redis Flask 应在端口 5000 上提供服务 而 redis 应在 6379 上提供服务 这些容器中的每一个都可以作为
  • Redis 写入 .ssh/authorized_keys

    当前设置 2 个主服务器 12 个工作服务器 工作人员通过 ssh copy id 连接到主设备 主设备和工作人员正在主设备上的 redis 队列中写入数据 过去一周我遇到的问题是 Redis 正在将数据写入authorized keys
  • 找不到模块“socket.io/node_modules/redis”

    当尝试做的时候 var redis require socket io node modules redis 我收到错误 找不到模块 socket io node modules redis 我不明白为什么 我正在运行 Windows 并运
  • 使用 EVAL、SCAN 和 DEL 的 Redis 通配符删除脚本返回“非确定性命令后不允许写入命令”

    因此 我正在寻求构建一个 lua 脚本 该脚本使用 SCAN 根据模式查找键并删除它们 原子地 我首先准备了以下脚本 local keys local done false local cursor 0 repeat local resul
  • Laravel - 缓存 Eloquent 并频繁更新

    是否可以对经常修改的对象使用缓存 例如 假设我们有一个 BlogPost 对象 并且有一个经常更改的 num of views 列 以及其他列 是否可以更新缓存和数据库中的 num of views 字段 而不破坏缓存对象并重新创建它 我可
  • 库存管理系统的 SQL 与 NoSQL

    我正在开发一个基于 JAVA 的网络应用程序 主要目的是拥有在多个称为渠道的网站上销售的产品的库存 我们将担任所有这些渠道的管理者 我们需要的是 用于管理每个渠道的库存更新的队列 库存表 其中包含每个通道上分配的正确快照 将会话 ID 和其
  • 如果另一个键中的计数器低于零,则从集合中原子删除一个项目?

    雷迪斯2 0 3 在我的 Redis DB 中 我有一组项目 每个项目都有一个与其关联的计数器 MULTI SADD items set foo INCRBY items foo 10000 EXEC 新项目会以随机间隔添加到集合中 当用户
  • Redis INCRBY 有限制

    我想知道是否有一种方法可以通过我的应用程序的单次往返在 Redis 中执行此操作 对于给定的键K 其可能值V是范围内的任意整数 A B 基本上 它有上限和下限 When an INCRBY or DECRBY发出命令 例如INCRBY ke
  • 从redis中检索大数据集

    一台服务器上的应用程序查询另一台服务器上运行的 Redis 查询的结果数据集约为 250kzrangebyscore objects locations inf inf这在应用程序服务器上似乎需要 40 秒 当使用命令执行时redis cl
  • 如何将node.js管道传输到redis?

    我有很多数据要插入 SET INCR 到redis DB 所以我正在寻找pipeline http redis io topics pipelining 质量插入 http redis io topics mass insert通过node
  • 有没有办法在 ruby​​ 中重新定义 []=+

    我正在尝试编写一个简单的 DSL 针对 Redis 并且我想自己定义 I have def key val redis zadd name val key end 我想定义 def key val redis zincrby name va
  • Node Js:Redis 作业在完成其任务后未完成

    希望你们做得很好 我在我的 Nodejs 项目中实现了 BullMQ Bull 的下一个主要版本 来安排发送电子邮件的作业 例如 发送忘记密码请求的电子邮件 所以 我编写了如下所示的代码 用户服务 await resetPasswordJo
  • Java 将字节转换为二进制安全字符串

    我有一些以字节为单位的数据 我想将它们放入Redis中 但是Redis只接受二进制安全字符串 而我的数据有一些二进制非安全字节 那么如何将这些字节转换为二进制安全字符串以便将它们保存到 Redis 中呢 Base64 对我有用 但它使数据更
  • 有没有办法用Lettuce自动发现Redis集群中新的集群节点IP

    我有一个Redis集群 3主3从 运行在一个库伯内斯簇 该集群通过Kubernetes 服务 Kube 服务 我将我的应用程序服务器连接到 Redis 集群 使用Kube 服务作为 URI 通过 Redis 的 Lettuce java 客
  • Redis是如何实现高吞吐量和高性能的?

    我知道这是一个非常普遍的问题 但是 我想了解允许 Redis 或 MemCached Cassandra 等缓存 以惊人的性能极限工作的主要架构决策是什么 如何维持连接 连接是 TCP 还是 HTTP 我知道它完全是用C写的 内存是如何管理
  • StackExchange.Redis Get 函数抛出 TimeoutException

    我在用着StackExchange Redis与 C 和StackExchangeRedisCacheClient Get函数抛出以下异常 myCacheClient Database StringGet txtKey Text myCac

随机推荐

  • R语言实战-第八章回归

    第八章 回归 简单线性回归 用到基础包中的women数据集 研究身高与体重的关系 head women fit lt lm weight height data women summary fit fitted fit 列出拟合模型的预测值
  • 【深度学习】RetinaFace人脸检测简要介绍

    介绍 Insight Face在2019年提出的最新人脸检测模型 原模型使用了deformable convolution和dense regression loss 当时在 WiderFace 数据集上达到SOTA 基网络有三种结构 基于
  • Java使用base64格式上传图片

    使用蚂蚁金服ui直接返回的是base64格式的图片 通过post方式进行请求 然后在控制器中以字符串的形式进行接收 接收之后进行转图片存储处理 只保存路径到数据库中 base64字节转图片代码 package com utils impor
  • qsort函数实现对任意数据的排序

    学会使用qsort函数排序 qsort介绍 compare函数介绍 不同的数据类型相应的比较函数定义 对数组元素为数字的 数组元素为字符时比较函数定义 结构体数据比较函数定义 qsort介绍 qsort函数是一个库函数 它的作用是对数据进行
  • 14.1 矩阵幂级数

    文章目录 矩阵的幂 矩阵幂的极限 谱半径与范数 矩阵幂级数 矩阵的幂 现在讨论下矩阵的n次方的问题 比如下面的矩阵 A 1
  • 医学图像配准MATLAB实现

    医学图像配准MATLAB实现 医学图像处理在临床诊断 肿瘤治疗和医学信息融合等领域中起着至关重要的作用 医学图像配准作为医学图像处理中的重要研究方向之一 其目的是将通过不同机器或技术获取到的不同角度或时间段的医学图像进行对比 以便医生或研究
  • 七种排序算法

    排序算法主要分为三大类 分别是插入排序 选择排序和交换排序 其中插入排序包括直接插入排序和希尔排序 选择排序包括直接选择排序和堆排序 交换排序包括冒泡排序 快速排序和归并排序 各种排序算法的时间复杂度和空间复杂度如下 一 插入排序 1 直接
  • android之fragment与fragment、activity与activity、fragment与activity之间的通信

    Broadcast广播接受者可以实现所有通信 activity与activity之间的通信 当下一个activity关闭时传值给上一个activity 主要用得到startActivityForResult和onActivityResult
  • epoll_create和epoll_create1

    名字 epoll create epoll create1 创建epoll文件描述符 摘要 include
  • 创建自定义类的对象数组

    源代码 public class Student static int number 0 静态变量的访问可以不用创建类的实例就可就可使用 lt 类名 属性 gt 的方法访问 String name 学生姓名 Student 无参构造函数 S
  • 【HDLBits 刷题 12】Circuits(8)Finite State Manchines 27-34

    目录 写在前面 Finite State Manchines 2014 q3c m2014 q6b m2014 q6c m2014 q6 2012 q2fsm 2012 q2b 2013 q2afsm 2013 q2bfsm 写在前面 HD
  • C++中的虚函数表和虚函数在内存中的位置

    目录 结论 今天在看别人面经的时候发现了这个问题 一时间发现自己也说不清楚 还想当然的以为 虚函数表既然是类对象公有的 那么应该在静态存储区 想当然终究只是想当然 经过试验得知 这种想法是错误的 由于不同的编译器在虚函数表上的实现可能不同
  • Jdk1.8新特性 - 方法引用

    一 说明 方法引用使用一对冒号 标识 通过方法的名字来指向一个方法 是函数式接口的另一种书写方式 通过方法引用 可以将方法的引用赋值给一个Function变量 Lambda表达式一般用于自己提供方法体 而方法引用一般直接引用现成的方法 二
  • Linux系统shell脚本之根分区监控

    Linux系统shell脚本之根分区监控 一 脚本要求 二 脚本分析 三 执行脚本 查看执行输出文件 一 脚本要求 1 编写一个shell脚本 脚本名为disk per sh 2 脚本检测根分区使用率 如果根分区超过80 则显示使用率 且提
  • ES6笔记(解构)

    1 解构 解构通俗点说 就是通过一种特定格式 快捷的读取对象 数组中的数据的方法 基本用法 如果右边是对象 左边也要用对象的格式 解构出来就是变量了 再也不是属性了 解构对象 var oUser name aaa age 20 es5读数据
  • [QT_001]解决Ubuntu下Qt无法连接MySQL数据库[Linux环境]

    准备 简要思路 编译Qt下MySQL项目 使其重新生成libqsqlmysql so这个动态库 而后进行替换 编译项目过程中 项目配置文件需要引入使用到MySQL的头文件和库 所以需要安装MySQL 我的环境 1 Ubuntu 18 04
  • 对受控组件和非受控组件的理解,以及应用场景?

    一 受控组件 受控组件 简单来讲 就是受我们控制的组件 组件的状态全程响应外部数据 举个简单的例子 class TestComponent extends React Component constructor props super pr
  • 笨人可以学计算机吗,为什么有的笨人一旦开窍,其人生就像开了挂似的呢?

    前言 真正的天才 是懂得在别人面前装糊涂的人 但如果他受到某种刺激 装糊涂也就没有必要了 低调惯了的人 总有一天 会一鸣惊人 韬盛和夫 大自然有一种特有的现象 自作聪明的动物往往不会活的太长 而那些看起不起眼的动物 往往隐藏着巨大的力量 笨
  • Bootstarp入门教程(6) 表格

    基本案例 为任意 table 标签添加 table可以为其赋予基本的样式 少量的内补 padding 和水平方向的分隔线 table
  • Redis中的事务

    1 Redis事务的定义 Redis事务是一个单独的隔离操作 事务中的所有命令都会序列化 然后按照顺序地执行 事务在执行的过程中 不会被其他客户端发送来的命令请求所打断 其实就是一个缓存队列 将所有任务放入 然后再某一个阶段 将其中的任务拿