Redis事务详解

目录

    • 一、前言
    • 二、Redis事务 - 基本使用
    • 三、Redis事务 - 错误处理
    • 四、Redis事务 - 事务冲突
      • 1、事务所产生的问题
      • 2、悲观锁&乐观锁
      • 3、watch监听
      • 4、watch的应用场景
    • 五、Redis 事务特性

一、前言

事务是指一个完整的动作,要么全部执行,要么什么也没有做。Redis 事务不是严格意义上的事务,只是用于帮助用户在一个步骤中执行多个命令。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

Redis 事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

什么是原子性?

举个经典的简单例子,银行转账,A 像 B 转账 100 元。转账这个操作其实包含两个离散的步骤:

  • 步骤 1:A 账户减去 100
  • 步骤 2:B 账户增加 100

我们要求转账这个操作是原子性的,也就是说步骤 1 和步骤 2 是顺续执行且不可被打断的,要么全部执行成功、要么执行失败。

Redis 的事务不是原子性,但是Redi执行每一个命令都是原子性的

举例:INCR在redis中是自增,即使多个客户端对同一个密钥发出INCR,也永远不会进入竞争状态。例如,客户机1读取“10”,客户机2同时读取“10”,两者都增加到11,并将新值设置为11,这样的情况永远不会发生。最终的值将始终是12。

这个案例是官网提出来的:https://redis.io/docs/data-types/tutorial/

之所以Redi执行每一个命令都是原子性,因为Redis是单线程执行的。这里我们一直在强调的单线程,只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候肯定是不止一个线程的,这里需要大家明确的注意一下。例如Redis进行持久化的时候会以子进程或者子线程的方式执行。

Mysql当中针对于并发事务会存在脏读、不可重复读、幻读等情况,那么Redis会有这种情况吗?

对于Redis而言根本不需要考虑这个。因为Redis是单线程的,根本不具备并发事务,并且Redis的事务虽然给人的感觉是将所有Redis命令放到了一个事务,本质上执行事务,就是把这个事务当成了一行命令来处理,然后对事务内的命令也是一行一行执行。

事务一般都是为原子性而生,既然Redis事务没有原子性,那他存在的意义是什么?

redis事务的主要作用就是串联多个命令防止 别的命令插队。

官网介绍:https://redis.com.cn/redis-transaction.html

二、Redis事务 - 基本使用

每个事务的操作都有 begin、commit 和 rollback:

  • begin 指示事务的开始
  • commit 指示事务的提交
  • rollback 指示事务的回滚

它大致的形式如下:

begin();
try {// 执行业务相关代码command1();command2();....commit();
} catch(Exception e) {rollback();
}

Redis 在形式上看起来也差不多,MULTI、EXEC、DISCARD这三个指令构成了 redis 事务处理的基础:

  • MULTI:用来组装一个事务,从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,redis会将之前的命令依次执行。
  • EXEC:用来执行一个事务
  • DISCARD:用来取消一个事务

redis事务分2个阶段:组队阶段、执行阶段

  • 组队阶段:只是将所有命令加入命令队列
  • 执行阶段:依次执行队列中的命令,在执行这些命令的过程中,不会被其他客户端发送的请求命令插队或者打断。

案例一:事务被成功执行

127.0.0.1:6379> set user_id 1 # 定义了一个user_id的key,value为1
OK
127.0.0.1:6379> get user_id 
"1"
127.0.0.1:6379> MULTI # 标记事务开始
OK
127.0.0.1:6379> incr user_id # 多条命令按顺序入队,返回值为QUEUED,表示这个命令加入队列了,还没有被执行。
QUEUED
127.0.0.1:6379> incr user_id # incr是自增的命令
QUEUED
127.0.0.1:6379> incr user_id
QUEUED
127.0.0.1:6379> exec # 执行事务过后返回的是事务块内所有命令的返回值,按命令执行的先后顺序排列。
1) (integer) 2
2) (integer) 3
3) (integer) 4
127.0.0.1:6379> get user_id
"4"

上面的指令演示了一个完整的事务过程,所有的指令在 exec 之前不执行,而是缓存在服务器的一个事务队列中,服务器一旦收到 exec 指令,才开执行整个事务队列,执行完毕后一次性返回所有指令的运行结果。因为 Redis 的单线程特性,不用担心自己在执行队列的时候被其它指令打搅,可以保证他们能得到的有顺序的执行

案例二:取消事务,放弃执行事务块内的所有命令。

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET greeting "hello"
QUEUED
127.0.0.1:6379> set kaka aaa
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> keys *
(empty list or set)

三、Redis事务 - 错误处理

情况1:组队中某个命令出现了错误报告,执行时整个队列中所有的命令都会被取消。

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi # 开启一个事务块
OK
127.0.0.1:6379> set name ready
QUEUED
127.0.0.1:6379> set age 30
QUEUED
127.0.0.1:6379> set1 age 60 # 命令有问题,导致加入队列失败
(error) ERR unknown command `set1`, with args beginning with: `age`, `60`,
127.0.0.1:6379> exec # 执行exec的时候,事务中所有命令都被取消
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get name  # 事务当中的命令也全都执行失败了
(nil)
127.0.0.1:6379> keys *
(empty list or set)

情况2:命令组队的过程中没有问题,执行中出现了错误会导致部分成功部分失败。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set books iamastring
QUEUED
127.0.0.1:6379> set poorman iamdesperate
QUEUED
127.0.0.1:6379> incr books  
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # incr是对数字类型的进行自增,而books存的是字母
127.0.0.1:6379> get books # 只有incr books 执行失败了,其他都执行成功了。
"iamastring"
127.0.0.1:6379> get poorman
"iamdesperate"

四、Redis事务 - 事务冲突

1、事务所产生的问题

想象一个场景:你的账户中只有10000,有多个人使用你的账户,同时去参加双十一抢购

  • 一个请求想给金额减8000
  • 一个请求想给金额减5000
  • 一个请求想给金额减1000

3个请求同时来带①,看到的余额都是10000,大于操作金额,都去执行修改余额的操作,最后导致金额变成了-4000,这显然是有问题的。

2、悲观锁&乐观锁

悲观锁:

悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人拿到这个数据就会block直到它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等,都是在做操作之前先上锁。

乐观锁:

乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去那数据的时候都认为别人不会修改,所以不会上锁,但是在修改的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。redis就是使用这种check-and-set机制实现事务的

3、watch监听

  • WATCH:在执行multi之前,先执行watch key1 [key2 …],可以监视一个或者多个key,若在事务的exec命令之前这些key对应的值被其他命令所改动了,那么事务中所有命令都将被打断,即事务所有操作将被取消执行。
  • unwatch:取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。

因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时
也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。

注意:Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。

案例一:监视 key,且事务成功执行

127.0.0.1:6379> set lock aa   # 新增了一个key/value
OK
127.0.0.1:6379> keys *        # 数据库目前就只有lock一个key  
1) "lock"
127.0.0.1:6379> watch lock lock_times # 开始监视key为lock或者lock_times的值。lock_times在数据库不存在也是可以监视的
OK
127.0.0.1:6379> multi        # 开启事务
OK
127.0.0.1:6379> SET lock "huangz"
QUEUED
127.0.0.1:6379> INCR lock_times # INCR是对一个key值进行自增,假如key值没有在数据库当中会进行创建并赋值为1
QUEUED
127.0.0.1:6379> EXEC		# 开始执行事务
1) OK
2) (integer) 1
127.0.0.1:6379> get lock
"huangz"
127.0.0.1:6379> get lock_times
"1"

案例二:监视 key,且事务被打断,这里需要准备两个客户端进行测试

案例三:watch监听key后只对当前客户端第一个事务有效,并不影响其他命令执行

127.0.0.1:6379> watch lock
OK
127.0.0.1:6379> set lock 'cccc'
OK
127.0.0.1:6379> get lock
"cccc"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set lock bbb
QUEUED
127.0.0.1:6379> exec #exec 指令返回一个 null 回复时,事务执行是失败的
(nil)

案例四:取消监听

127.0.0.1:6379>  WATCH lock lock_times
OK
127.0.0.1:6379> UNWATCH
OK

4、watch的应用场景

考虑到一个业务场景,Redis 存储了我们的账户余额数据,它是一个整数。现在有两个并发的客户端要对账户余额进行修改操作,这个修改不是一个简单的 incrby 指令,而是要对余额乘以一个倍数。Redis 可没有提供 multiplyby 这样的指令。我们需要先取出余额然后在内存里乘以倍数,再将结果写回 Redis。

这就会出现并发问题,因为有多个客户端会并发进行操作。我们可以通过 Redis 的分布式锁来避免冲突,这是一个很好的解决方案。分布式锁是一种悲观锁,那是不是可以使用乐观锁的方式来解决冲突呢?

当服务器给 exec 指令返回一个 null 回复时,客户端知道了事务执行是失败的,通常客户端 (redis-py) 都会抛出一个 WatchError 这种错误,不过也有些语言 (jedis) 不会抛出异常,而是通过在 exec 方法里返回一个 null,这样客户端需要检查一下返回结果是否为 null 来确定事务是否执行失败。

使用Java代码来实现这个需求,这里用的客户端是Jedis:

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.1</version>
</dependency>
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;import java.util.List;public class TransactionDemo {public static void main(String[] args) {Jedis jedis = new Jedis();String userId = "abc";// 自定义一个keyString key = keyFor(userId);// setnx 做初始化:如果key不存在,创建key,并且返回1,如果存在不覆盖原来的值,返回0jedis.setnx(key, String.valueOf(5));System.out.println(doubleAccount(jedis, userId));jedis.close();}public static int doubleAccount(Jedis jedis, String userId) {String key = keyFor(userId);while (true) {jedis.watch(key);int value = Integer.parseInt(jedis.get(key));// 加倍value *= 2;// 开启事务Transaction tx = jedis.multi();tx.set(key, String.valueOf(value));List<Object> res = tx.exec();// 直到事务提交成功退出循环if (res != null) {// 成功了break;}}// 重新获取余额return Integer.parseInt(jedis.get(key));}public static String keyFor(String userId) {return String.format("account_{}", userId);}
}

五、Redis 事务特性

  • 单独的隔离操作: 事务中的所有命令都会序列化、按顺序地执行,事务在执行过程中,不会被其他客户端发送来的命令请求所打断。
  • 没有隔离级别的概念: 队列中的命令没有提交(exec)之前,都不会实际被执行,因为事务提交前任何指令都不会被实际执行。
  • 不能保证原子性: 事务中如果有一条命令执行失败,后续的命令仍然会被执行,没有回滚。如果在组队阶段,有1个失败了,后面都不会成功;如果在组队阶段成功了,在执行阶段有那个命令失败就这条失败,其他的命令则正常执行,不保证都成功或都失败。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

python--识别图片中的文字

本篇文章主要参考了 python图像处理之识别图像中的文字 这篇文章&#xff0c;在实现的过程中出现了些偏差&#xff0c;特此记录。因为此时笔者不是第一次安装&#xff0c;所展示的结果会和首次安装的结果有所差别。 1.安装PIL 以管理员的身份打开命令提示符&#xff0c;输入&…

【Python例】利用 python 进行图片文字信息的提取 --- OCR-EasyOCR

【Python例】利用 python 进行图片文字信息的提取 — OCR-EasyOCR 本文主要用于记录&#xff0c;并使用 python 脚本进行图片文字信息的生成。 什么是 OCR&#xff1f; OCR OCR&#xff08;Optical character recognition&#xff0c;光学字符识别&#xff09;是一种将图像中的…

Python提取图片中的文字信息

Python提取图片中的文字信息 使用的Python库 Python tesseract是Python的一个光学字符识别&#xff08;OCR&#xff09;工具。也就是说&#xff0c;它将识别并“读取”嵌入图像中的文本。 Python tesseract是Google tesseract OCR引擎的包装器。它还可用作tesseract的独立调…

Python识别图片中的文字

Python识别图片中的文字 一、前言 不知道大家有没有遇到过这样的问题&#xff0c;就是在某个软件或者某个网页里面有一篇文章&#xff0c;你非常喜欢&#xff0c;但是不能复制。或者像百度文档一样&#xff0c;只能复制一部分&#xff0c;这个时候我们就会选择截图保存。但是…

python提取图片中的文字

python提取图片中的文字 准备工作原图片完整代码结果预览 OCR&#xff0c;全称Optical character recognition&#xff0c;中文译名叫做光学文字识别。它把图像中的字符&#xff0c;转换为机器编码的文本的一种方法。OCR技术在印刷行业应用得非常多&#xff0c;也广泛用于识别图…

python代码提取图片文字

步骤一&#xff1a;安装两个库 需要安装pytesseract和PIL两个库&#xff0c;然后还要安装tesseract-ocr识别引擎 问题一&#xff1a;安装两个库失败 这个博主讲的很详细参考链接lpython3安装pil报错 使用pip命令安装这两个库&#xff08;pip命令无法使用的参考我的这篇文章将…

【Python • 图片识别】pytesseract快速识别提取图片中的文字

提示&#xff1a;本文多图&#xff0c;请手机端注意流量。 文章目录 前言一、配置环境1. 安装python依赖2. 安装识别引擎安装tesseract识别引擎&#xff08;可跳过&#xff09;验证是否安装成功 二、使用步骤1.引入库2.提取图片文字3.运行效果 总结完整代码 前言 利用python做…

MySQL进阶- Linux安装 和 索引

目录 Linux安装索引索引的概述索引的结构索引结构的介绍BtreeBtreeHash 索引的分类索引的语法&#xff08;创建&#xff0c;查看&#xff0c;删除等&#xff09;SQL性能分析SQL的执行频率&#xff08;查看SQL的执行频率&#xff09;慢查询日志show profilesexplain执行计划 索引…

Nginx同时支持Http和Https的配置

现在的网站支持Https几乎是标配功能&#xff0c;Nginx能很好的支持Https功能。下面列举一个配置同时支持Http和Https的功能。 需要注意的是&#xff1a;既然选择使用Https&#xff0c;就是为了保证通信安全&#xff0c;那么就没必要再用Http进行通信了。在URL中还支持Http的方式…

Nginx配置同时支持http和https两种方式访问

http&#xff1a; https: Nginx的ssl模块安装 进入到目录的sbin目录下&#xff0c;输入 #注意这里是大写的V&#xff0c;小写的只显示版本号 ./nginx -V 如果出现 (configure arguments: --with-http_ssl_module), 则已安装&#xff08;下面的步骤可以跳过&#xff0c;直接进…

HTTP 和 HTTPS 的区别(面试常考题)

前言 无论是在校学习还是找工作的时候&#xff0c;老师和面试官都问过同学 HTTP 和 HTTPS 的区别。平时上网的时候也没有关注这个问题&#xff0c;只是知道计算机网络里 HTTP 的概念&#xff0c;所以最近才查资料好好补补这一块。其实这一块的知识延伸很广&#xff0c;如果之前…

网站开发(一)http和https的区别 http是什么?http协议的特性 http与https网站通信协议两者的有什么不同呢?

目录 一、前言 二、HTTP是什么&#xff1f; 1、HTTP发展阶段 2、HTTP工作原理 3、HTTP的工作特性 三、HTTP和HTTPS区别 1、http与https端口不同 2、http和https应用 ? ? ? ? 2.1 http应用 ? ? ? ? 2.2 https应用 3、 http和https区别简易图 一、前言 HTTP是…

HTTP和HTTPS区别

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&#xff0c;因此&…

HTTP和HTTPS的工作原理及区别

一、HTTP和HTTPS的基本概念 HTTP&#xff08;HyperText Transfer Protocol&#xff1a;超文本传输协议&#xff09;&#xff1a;是互联网上应用最为广泛的一种网络协议&#xff0c;是一个客户端和服务器端请求和应答的标准&#xff08;TCP&#xff09;&#xff0c;用于从Web服…

Spring Boot项目同时使用http和https

有些时候项目需要同时监听在不同的端口上&#xff0c;比如同时监听http80和https 443端口&#xff0c;这时我们需要自定义web server。 假设我们需要项目同时使用http和https两种协议提供服务&#xff0c;分别使用端口80和443。 我们知道Spring Boot内置了tomcat,jetty,under…

http和https的区别https的连接方式

首先了解什么是http协议&#xff0c;http协议是超文本传输协议&#xff0c;他存在的目的是为浏览器和服务器之前传输信息。但是http是明文传输的&#xff0c;不管是谁&#xff0c;只要抓取了浏览器传向服务器&#xff0c;或者服务器传向浏览器的信息&#xff0c;就可以获取里面…

http和https有什么区别?

ps&#xff1a;https就是http和TCP之间有一层SSL层&#xff0c;这一层的实际作用是防止钓鱼和加密。防止钓鱼通过网站的证书&#xff0c;网站必须有CA证书&#xff0c;证书类似于一个解密的签名。另外是加密&#xff0c;加密需要一个密钥交换算法&#xff0c;双方通过交换后的密…

HTTP和HTTPS的区别详解

1.HTTP的缺点 &#xff08;点击题目解锁惊喜↑↑&#xff09; 通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听。不验证通信方的身份&#xff0c;因此有可能遭遇伪装。无法证明报文的完整性&#xff0c;所以有可能已遭篡改。某些特定的web服务器和特…

HTTP和HTTPS请求的整个过程详解

HTTP和HTTPS请求的整个过程详解 小橙子 小橘子的日记 4月17日 HTTP和HTTPS的请求流程也是面试的时候一大考点&#xff0c;如果不掌握的话&#xff0c;容易丢失印象分。这两种请求都是基于TCP/IP&#xff0c;不明白的请看上一篇文章。这里总结HTTP/HTTPS请求的整个过程&#xf…

http和https协议有什么区别

1、https协议需要到CA &#xff08;Certificate Authority&#xff0c;证书颁发机构&#xff09;申请证书&#xff0c;一般免费证书较少&#xff0c;因而需要一定费用。(原来网易官网是http&#xff0c;而网易邮箱是https。) 2、http是超文本传输协议&#xff0c;信息是明文传…