Redis事务深入解析和使用

作为关系型数据库中一项非常重要的基础功能——事务,在 Redis 中是如何处理并使用的?

1.前言

事务指的是提供一种将多个命令打包,一次性按顺序地执行的机制,并且保证服务器只有在执行完事务中的所有命令后,才会继续处理此客户端的其他命令。

事务也是其他关系型数据库,所必备的一项非常重要的能力。以支付的场景为例,正常情况下只有正常消费完成之后,才会减去账户余额。但如果没有事务的保障,可能会发生消费失败了,但依旧会把账户的余额给扣减了,我想这种情况应该任何人都无法接受吧?所以事务是关系型数据库中一项非常重要的基础功能。

2.事务基本使用

事务在其他语言中,一般分为以下三个阶段:

  • 开启事务——Begin Transaction
  • 执行业务代码,提交事务——Common Transaction
  • 业务处理中出现异常,回滚事务——Rollback Transaction

以 Java 中的事务执行为例:

// 开启事务
begin();
try {//......// 提交事务commit();
} catch(Exception e) {// 回滚事务rollback();
}

Redis 中的事务从开始到结束也是要经历三个阶段:

  • 开启事务
  • 命令入列
  • 执行事务/放弃事务

其中,开启事务使用 multi 命令,事务执行使用 exec 命令,放弃事务使用 discard 命令。

1)开启事务

multi 命令用于开启事务,实现代码如下:

> multi
OK

multi 命令可以让客户端从非事务模式状态,变为事务模式状态,如下图所示:
image.png
注意:multi 命令不能嵌套使用,如果已经开启了事务的情况下,再执行 multi 命令,会提示如下错误:

(error) ERR MULTI calls can not be nested

执行效果,如下代码所示:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> multi
(error) ERR MULTI calls can not be nested

当客户端是非事务状态时,使用 multi 命令,客户端会返回结果 OK ,如果客户端已经是事务状态,再执行 multi 命令会 multi 命令不能嵌套的错误,但不会终止客户端为事务的状态,如下图所示:
image.png

2)命令入列

客户端进入事务状态之后,执行的所有常规 Redis 操作命令(非触发事务执行或放弃和导致入列异常的命令)会依次入列,命令入列成功后会返回 QUEUED ,如下代码所示:

> multi
OK
> set k v
QUEUED
> get k
QUEUED

执行流程如下图所示:
image.png
注意:命令会按照先进先出(FIFO)的顺序出入列,也就是说事务会按照命令的入列顺序,从前往后依次执行。

3)执行事务/放弃事务

执行事务的命令是 exec ,放弃事务的命令是 discard
执行事务示例代码如下:

> multi
OK
> set k v2
QUEUED
> exec
1) OK
> get k
"v2"

放弃事务示例代码如下:

> multi
OK
> set k v3
QUEUED
> discard
OK
> get k
"v2"

执行流程如下图所示:
image.png

3.事务错误&回滚

事务执行中的错误分为以下三类:

  • 执行时才会出现的错误(简称:执行时错误);
  • 入列时错误,不会终止整个事务;
  • 入列时错误,会终止整个事务。

1)执行时错误

示例代码如下:

> get k
"v"
> multi
OK
> set k v2
QUEUED
> expire k 10s
QUEUED
> exec
1) OK
2) (error) ERR value is not an integer or out of range
> get k
"v2"

执行命令解释如下图所示:
image.png
从以上结果可以看出,即使事务队列中某个命令在执行期间发生了错误,事务也会继续执行,直到事务队列中所有命令执行完成。

2)入列错误不会导致事务结束

示例代码如下:

> get k
"v"
> multi
OK
> set k v2
QUEUED
> multi
(error) ERR MULTI calls can not be nested
> exec
1) OK
> get k
"v2"

执行命令解释如下图所示:
image.png
可以看出,重复执行 multi 会导致入列错误,但不会终止事务,最终查询的结果是事务执行成功了。除了重复执行 multi 命令,还有在事务状态下执行 watch 也是同样的效果,下文会详细讲解关于 watch 的内容。

3)入列错误会导致事务结束

示例代码如下:

> get k
"v2"
> multi
OK
> set k v3
QUEUED
> set k
(error) ERR wrong number of arguments for 'set' command
> exec
(error) EXECABORT Transaction discarded because of previous errors.
> get k
"v2"

执行命令解释如下图所示:
image.png

4)为什么不支持事务回滚?

Redis 官方文档的解释如下:

If you have a relational databases background, the fact that Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back, may look odd to you.
However there are good opinions for this behavior:

  • Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.
  • Redis is internally simplified and faster because it does not need the ability to roll back.

An argument against Redis point of view is that bugs happen, however it should be noted that in general the roll back does not save you from programming errors. For instance if a query increments a key by 2 instead of 1, or increments the wrong key, there is no way for a rollback mechanism to help. Given that no one can save the programmer from his or her errors, and that the kind of errors required for a Redis command to fail are unlikely to enter in production, we selected the simpler and faster approach of not supporting roll backs on errors.

大概的意思是,作者不支持事务回滚的原因有以下两个:

  • 他认为 Redis 事务的执行时,错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他认为没有必要为 Redis 开发事务回滚功能;
  • 不支持事务回滚是因为这种复杂的功能和 Redis 追求的简单高效的设计主旨不符合。

这里不支持事务回滚,指的是不支持运行时错误的事务回滚。

4.监控

watch 命令用于客户端并发情况下,为事务提供一个乐观锁(CAS,Check And Set),也就是可以用 watch 命令来监控一个或多个变量,如果在事务的过程中,某个监控项被修改了,那么整个事务就会终止执行
watch 基本语法如下:

watch key [key …]

watch 示例代码如下:

> watch k
OK
> multi
OK
> set k v2
QUEUED
> exec
(nil)
> get k
"v"

从以上命令可以看出,如果 exec 返回的结果是 nil 时,表示 watch 监控的对象在事务执行的过程中被修改了。从 get k 的结果也可以看出,在事务中设置的值 set k v2 并未正常执行。
执行流程如下图所示:
image.png
注意watch 命令只能在客户端开启事务之前执行,在事务中执行 watch 命令会引发错误,但不会造成整个事务失败,如下代码所示:

> multi
OK
> set k v3
QUEUED
> watch k
(error) ERR WATCH inside MULTI is not allowed
> exec
1) OK
> get k
"v3"

执行命令解释如下图所示:
image.png
unwatch 命令用于清除所有之前监控的所有对象(键值对)。
unwatch 示例如下所示:

> set k v
OK
> watch k
OK
> multi
OK
> unwatch
QUEUED
> set k v2
QUEUED
> exec
1) OK
2) OK
> get k
"v2"

可以看出,即使在事务的执行过程中,k 值被修改了,因为调用了 unwatch 命令,整个事务依然会顺利执行。

5.事务在程序中使用

以下是事务在 Java 中的使用,代码如下:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;public class TransactionExample {public static void main(String[] args) {// 创建 Redis 连接Jedis jedis = new Jedis("xxx.xxx.xxx.xxx", 6379);// 设置 Redis 密码jedis.auth("xxx");// 设置键值jedis.set("k", "v");// 开启监视 watchjedis.watch("k");// 开始事务Transaction tx = jedis.multi();// 命令入列tx.set("k", "v2");// 执行事务tx.exec();System.out.println(jedis.get("k"));jedis.close();}
}

6.小结

事务为多个命令提供一次性按顺序执行的机制,与 Redis 事务相关的命令有以下五个:

  • multi:开启事务
  • exec:执行事务
  • discard:丢弃事务
  • watch:为事务提供乐观锁实现
  • unwatch:取消监控(取消事务中的乐观锁)

正常情况下 Redis 事务分为三个阶段:开启事务、命令入列、执行事务。Redis 事务并不支持运行时错误的事务回滚,但在某些入列错误,如 set key 或者是 watch 监控项被修改时,提供整个事务回滚的功能。

7.思考题

Redis 事务中如何解决并发修改的问题?Redis 支持事务回滚吗?使用 Redis 事务时会出现哪三种错误?这三种错误对事务有何影响?只有高手才能答对的问题,你能答上来几个?

8.参考&鸣谢

https://redis.io/topics/transactions

https://redisbook.readthedocs.io/en/latest/feature/transaction.html#id3

关注下方二维码,订阅更多精彩内容

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

局域网在线设备扫描工具V2.0软件说明

软件简介: 一个场所的在线设备(例如:监控摄像头或计算机)一般都处在同一个局域网内,当在调试和维护在线设备时,如果不知道在线设备的IP地址,可以使用本工具扫描局域网内在线设备的IP地址和MAC地…

漫画:什么是归并排序?

————— 第二天 —————————————————举个例子,有A、B、C、D、E、F、G、H一共8个武术家参考参加比武大会。第一轮,两两一组,有4名选手胜出(四分之一决赛)第二轮,两两一组,有…

世界上没有技术驱动型公司

点击上方“Java中文社群”,选择“设为星标”终身学习者,以编程为乐!来源:zhihu.com/question/312019918/answer/608965942作者介绍:智煜徽,洛林大学计算机专业研究生,现就职于华为,从…

单片机Proteus7.8仿真和Proteus8.6仿真 LED点阵 温度采集 电子琴 温度报警 电子秤 音乐播放器 PWM 电压表 温度计 交通灯

单片机Proteus7.8仿真: 单片机Proteus8.6仿真: 下载地址: 关注公众号,获取资料 部分仿真图: 下载地址: 关注公众号,获取资料

InnoDB的内存结构和特性

常言说得好,每个成功男人背后都有一个为他默默付出的女人,而对于MySQL来说,这个“人”就是InnoDB存储引擎。MySQL区别于其他数据库的最为重要的特点就是其插件式的表存储引擎。而在众多存储引擎中,InnoDB是最为常用的存储引擎。从…

Maven必知的7个问题,你会吗?

点击上方“Java中文社群”,选择“设为星标”终身学习者,以编程为乐!原文:https://www.jianshu.com/p/20b39ab6a88c在如今的互联网项目开发当中,特别是Java领域,可以说Maven随处可见。Maven的仓库管理、依赖…

Linux 小知识翻译 - 「Linux的吉祥物企鹅叫什么名字?」

这次说说Linux的吉祥物 *企鹅* 都知道Linux的吉祥物是企鹅,但是这个企鹅叫什么名字呢? 它的名字就是「Tux」,注意可不是「Tax」(税收)。 具体参考 wiki:中文解释 英文的解释更为详细一些:英文解释 Linux内核发展到 v2…

WordPress修改WordPress地址和站点地址之后无法打开网站和无法后台登录

WordPress地址一定不要修改。 WordPress修改WordPress地址和站点地址之后无法打开网站和无法后台登录的解决方法: 1、使用ssh登录服务器 xshell、SecureCRT等工具均可。 2、登录MySQL数据库 使用如下指令后,输入密码,打开mysql数据库&am…

从JVM入手,聊聊Java的学习和复习!

我们在学习的时候,经常会遇到这样一个问题:「学完就忘」这个问题大部分人都会遇到,那么我们今天就来聊聊,为啥会学了就忘呢?我根据自己的学习经验,大致总结以下三点原因:1、知识没有用起来2、没…

Activity和Service交互

2019独角兽企业重金招聘Python工程师标准>>> As discussed in the Services document, you can create a service that is both started and bound. That is, the service can be started by calling startService(), which allows the service to run indefinitely…

Redis从入门到精通|干货篇

点击上方“Java中文社群”,选择“设为星标”终身学习者,以编程为乐!来源:http://rrd.me/eDY9S常用的 SQL 数据库的数据都是存在磁盘中的,虽然在数据库底层也做了对应的缓存来减少数据库的 IO 压力。由于数据库的缓存一…

最重要的7个Drupal内核模板文件

Drupal内核拥有40多个模板文件&#xff0c;其中最重要的有7个。它们组成了页面的主要部分&#xff0c;在定制Drupal主题时经常要用到。因此列表如下供大家参考。 模板名原始位置用途html.tpl.phpmodules/system打印HTML文档的结构。包括了<head>标记中的内容&#xff0c;…

Java中的13个原子操作类

点击上方“Java中文社群”&#xff0c;选择“设为星标”终身学习者&#xff0c;以编程为乐&#xff01;来源&#xff1a;https://www.iteye.com/blog/xiaoheng-2509522atomic 包中的 13 个类&#xff0c;属于 4 中类型的原子更新方式.原子更新基本类型原子更新数组原子更新引用…

Redis中的键值过期操作

1.过期设置 Redis 中设置过期时间主要通过以下四种方式&#xff1a; expire key seconds&#xff1a;设置 key 在 n 秒后过期&#xff1b;pexpire key milliseconds&#xff1a;设置 key 在 n 毫秒后过期&#xff1b;expireat key timestamp&#xff1a;设置 key 在某个时间戳…

天意PE维护系统 V2021.7.16版

天意PE U盘维护系统是一款用于系统维护和装机的U盘PE系统&#xff08;U盘启动盘&#xff09;&#xff0c;采用双PE模式,支持UEFI双启动&#xff0c;集成常用工具的维护软件&#xff0c;集成SRS驱动&#xff0c;新版更加上了NVME硬盘的支持&#xff0c;天意U盘维护系统纯净、稳定…

Redis持久化的几种方式——深入解析RDB

Redis 的读写都是在内存中&#xff0c;所以它的性能较高&#xff0c;但在内存中的数据会随着服务器的重启而丢失&#xff0c;为了保证数据不丢失&#xff0c;我们需要将内存中的数据存储到磁盘&#xff0c;以便 Redis 重启时能够从磁盘中恢复原有的数据&#xff0c;而整个过程就…

【LeetCode】148. Sort List

Sort List Sort a linked list in O(n log n) time using constant space complexity. 要求时间复杂度为O(nlogn)&#xff0c;那么不能用quickSort了&#xff08;最坏O(n^2)&#xff09;&#xff0c;所以使用mergeSort. 通常写排序算法都是基于数组的&#xff0c;这题要求基于链…

WordPress网站搬家教程(根目录程序+ MySQL数据库)

WordPress博客网站怎么搬家到新的服务器上&#xff1f;WordPress搬家无非是两点&#xff0c;即WP程序文件和数据库&#xff0c;具体什么系统不用考虑&#xff1a; 3分钟详解WordPress搬家教程 本文新手站长网关于WordPress搬家教程是帮大家缕清思路&#xff08;参考&#xff1…

Redis持久化的几种方式——RDB深入解析

点击上方“Java中文社群”&#xff0c;选择“设为星标”终身学习者&#xff0c;以编程为乐&#xff01;Redis 的读写都是在内存中&#xff0c;所以它的性能较高&#xff0c;但在内存中的数据会随着服务器的重启而丢失&#xff0c;为了保证数据不丢失&#xff0c;我们需要将内存…

配置Debian11服务器允许root用户远程SSH登录

全新安装系统后&#xff0c;默认情况下将禁用Debian Linux上的root登录。当您尝试以root用户身份登录Debian11 Linux服务器时&#xff0c;访问将被拒绝。要在Debian Linux系统上为root用户启用SSH登录&#xff0c;您需要首先配置SSH服务器。 0.首先安装SSH 注意&#xff1a;如…