Redis(十三) 事务
文章目录
- 前言
- 事务的特性
- Redis事务的执行原理
- Redis中使用事务
- WATCH UNWATCH实现乐观锁
前言
前面我们学习 MySQL 的时候,肯定也学习了事务。事务是什么?给大家举个例子:假如我给朋友微信转账,我给他转了 100 块钱,当我输入完成密码了之后,我的微信余额减了 100,但是这时我的微信突然没信号了,换句话就是微信服务器突然挂了,当微信服务器重新启动之后,我问我那个朋友 100 块钱收到了没,他说没有,我就去看我的微信余额,发现 100 块钱扣掉了,那么这时候就出现问题了,我的微信余额减少了,但是朋友的微信余额没有增加。遇到这种问题该如何解决呢?那就是使用事务,事务中的语句要么全部执行,要么全部不执行。Redis 中也有事务这个概念,但是和 MySQL 的事务是有区别的。
事务的特性
前面学习的 MySQL 事务有四种特性(ACID):
- 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
- 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
- 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
- 持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
上面是 MySQL 事务的特性,Redis 事务对比于 MySQL 事务而言,那真的就是一句话:没有可比性。
原子性是事务中的操作要么全部执行,要么一个都不执行。当事务中的某个操作出现错误的时候,就会将之前的操作进行回滚,回滚到事务开启前的状态,只有操作全部正确执行的时候,才不会发生回滚。但是对于我们的 Redis 来说,如果事务中的操作出现了错误之后,redis 只会报错,然后继续执行事务后面的操作,而不会发生回滚。那么 Redis 的这种操作可以称作是原子性吗?其实也是有的,但是这种性质叫做弱原子性,也可以说 Redis 不具有原子性,大家对于原子性的定义是有歧义的,所以对于 Redis 是否具有原子性也是存在歧义的。
一致性是指在事务开始之前和事务结束以后,数据库的完整性没有被破坏。因为 Redis 的事务不具有回滚的功能,所以也就无法保证事务的一致性,也就是说 Redis 不具有一致性。
持久性是指事务的操作对于数据库的改变是永久的,而由于我们的 Redis 操作数据都是在内存中的,内存中的数据是易失的,不具有持久性,所以 Redis 不具有持久性。
隔离性是指多个事务之间的操作是互不影响的,而我们的 Redis 是单线程模型的服务器程序,所以也就不存在多个事务同时执行的情况,就不存在所谓的隔离性特性。
啊?既然 Redis 的事务 ACID 特性都没有,那么 Redis 的事务是干嘛的啊?其实 Redis 事务的主要功能就是为了“打包”,将一个客户端发送的事务之内的操作放在一起,按照顺序执行,防止被其他客户端发送来的请求命令插队、干扰。
Redis事务的执行原理
Redis 实现事务引入了队列,当客户端开启事务的时候,接下来输入的命令 redis 服务器不会立即执行这些命令,而是会将这些命令放入这个队列中(这个队列是每个客户端都对应的有一个)。只有当遇到执行事务这个命令的时候,队列中的命令才会依次执行,而这个队列中的命令只有全部执行完才会执行其他客户端对应的队列中的命令。
那么为什么 Redis 的事务搞得这么简单,而不是像 MySQL 那样功能齐全呢?MySQL 的事务功能齐全那是需要付出代价的,需要付出更多的空间和时间,而 Redis 又以快著称,因为 redis 操作的对象是内存,内存的价格比硬盘高不少,所以完善 redis 的事务功能就以为需要牺牲速度和空间,那这样还不如直接使用 MySQL。
Redis中使用事务
在 redis 中实现事务需要依赖 MULTI
、EXEC
和DISCARD
命令。
- MULTI:开启事务
- EXEC:执行事务
- DISCARD:丢弃事务
现在一个客户端中开启事务,并且向队列中添加一些命令,但是不执行事务。再开启一个窗口,查看事务中的操作是否执行:
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key1 111
QUEUED
127.0.0.1:6379> set key2 222
QUEUED
127.0.0.1:6379> set key3 333
QUEUED
127.0.0.1:6379>
127.0.0.1:6379> get key1
(nil)
127.0.0.1:6379> get key2
(nil)
127.0.0.1:6379> get key3
(nil)
然后执行事务,再看是否执行操作:
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
使用 EXEC 执行事务之后,返回的是事务中的每个操作的返回值
127.0.0.1:6379> get key1
"111"
127.0.0.1:6379> get key2
"222"
127.0.0.1:6379> get key3
"333"
上面就是一个完整的事务,那么如果事务中的操作出现了错误,会发生什么:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key4 444
QUEUED
127.0.0.1:6379> setget key5 555
(error) ERR unknown command `setget`, with args beginning with: `key5`, `555`,
127.0.0.1:6379> set key5 555
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key4
(nil)
127.0.0.1:6379> get key5
(nil)
127.0.
前面不是说了 redis 事务中的操作出现了问题不是不会发生回滚吗?这里为什么事务中的操作都没有执行呢?其实对于 redis 事务是否回滚取决于错误发生的类型,如果发生错误的类型是编译时异常,也就是命令错误时,就会发生回滚,运行时异常则不会发生回滚:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key4 nihao
QUEUED
127.0.0.1:6379> INCR key4
QUEUED
127.0.0.1:6379> set key5 555
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get key4
"nihao"
127.0.0.1:6379> get key5
"555"
这里 key4 不是 intstr 类型,所以执行 INCR 自增的时候就会发生错误,但是由于这里的错误类型是运行时异常,所以事务就不会发生回滚。
然后看看丢失当前事务的操作:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key6 666
QUEUED
127.0.0.1:6379> set key7 777
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get key6
(nil)
127.0.0.1:6379> get key7
(nil)
这是 DISCARD 丢失事务的实现。如果在开启了事务,并且向服务器发送了若干命令之后,服务器突然重启了。服务器重启也就意味了内存中的数据都丢失了,那么事务对应的队列中的数据也就丢失了,这时候该怎么办呢?其实这时就相当于执行了 DISCARD 丢弃事务的操作。
WATCH UNWATCH实现乐观锁
假设在事务开始的过程,执行之前,其他客户端对数据进行了修改,而当前客户端事务中也会对这个数据进行修改,但是对于这个数据修改多次是不允许的,那么在开启事务之后,执行事务之前,如何判断在这个事务中要使用到的数据已经被修改了呢?
这就需要使用到 WATCH 命令和 UNWATCH 命令了。通过使用 WATCH key
就可以监视这个 key,当监视了这个 key 之后,如果在事务中需要修改这个 key 之前,这个 key 已经被修改了,那么事务就会相当于执行 DISCARD 命令,丢弃当前事务。
先开启事务,并向 redis 服务器发送几个命令,但是不执行,然后再开启一个窗口修改 watch 监视的key:
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 111
OK
127.0.0.1:6379> WATCH key1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key1 333
QUEUED
127.0.0.1:6379> set key1 222
OK
执行事务:
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> get key1
"222"
可以看到 key1 最终的结果是另一个窗口修改的 key1 的值,另一个窗口修改的操作是早于当前窗口事务的修改的操作的,按理来说,key1 的值应该是事务中修改的 key1 的值,结果却相反,说明当前窗口的事务中的操作没有执行,事务被丢弃了。
使用 UNWATCH 可以接触 WATCH 对于 key 的监视。
这个 WATCH 命令就相当于我们前面学习多线程中的加乐观锁的操作,乐观锁假设在大多数情况下,多个线程或事务之间不会发生冲突,因此在读取数据时不会上锁。相反,乐观锁会在更新数据时判断在此期间数据是否被其他线程或事务修改过。
与乐观锁相对应的就是悲观锁,悲观锁总是假设最坏的情况,即认为数据在处理过程中总是会被其他线程或事务修改,因此在数据处理开始之前就先对数据进行锁定。
乐观锁相对于悲观锁更轻量。WATCH 的执行流程大概就是,在事务中,要修改监视的值时,先判断当前key的值是否和使用 WATCH key 监视key的时候的值相同,如果相同就允许修改,不同则 DISCARD。这样虽然可以,但是又会发生 ABA 问题,就是这个值被修改过了,只不过在我判断之前这个值又被改了回来,当出现这种情况的时候也是会出现问题的,所以 redis 的 WATCH 乐观锁的实现就是根据版本号来判断的,当使用 WATCH key 监视 key 的时候就会为这个 key 定义一个版本号,当对这个 key 进行修改的时候,版本号就会加上或者减去一个值(要加每次修改,版本号都是加,减,版本号则是减,不会出现这次修改,版本号变大,下次修改版本号变小的情况),所以通过版本号的操作就很好的解决了 ABA 问题。
WATCH 命令只针对于事务来说
相关文章:

Redis(十三) 事务
文章目录 前言事务的特性Redis事务的执行原理Redis中使用事务WATCH UNWATCH实现乐观锁 前言 前面我们学习 MySQL 的时候,肯定也学习了事务。事务是什么?给大家举个例子:假如我给朋友微信转账,我给他转了 100 块钱,当我…...
RK 11.0 多屏模式下修改鼠标进入方式
要求:主屏在左,副屏在右。这种排列情况下鼠标仅可通过主屏的最右侧移入副屏的最左侧,或从副屏的最左侧移入主屏最右侧。 1.RK默认设计 1.1 RK的代码设计是当sys.mouse.presentation1时,鼠标在屏幕边缘的时候就会移入另一个屏幕 …...

【收录 Hello 算法】10.4 哈希优化策略
目录 10.4 哈希优化策略 10.4.1 线性查找:以时间换空间 10.4.2 哈希查找:以空间换时间 10.4 哈希优化策略 在算法题中,我们常通过将线性查找替换为哈希查找来降低算法的时间复杂度。我们借助一个算法题来加深理解。 Question 给…...
浅析部署架构中的GZone、RZone和CZone
在现代软件开发中,理解和应用各种技术概念是成功的重要因素。本文将详细介绍GZone、RZone和CZone三个概念,解释它们的定义、特点、功能及应用场景,并通过实际案例帮助读者更好地理解这些概念。 一、GZone 1.1 定义 GZone是指“Global Zone…...

【全开源】分类记账小程序系统源码(ThinkPHP+FastAdmin+UniApp)
基于ThinkPHPFastAdminUniAppvk-uView-uiVue3.0开发的一款支持多人协作的记账本小程序,可用于家庭,团队,组织以及个人的日常收支情况记录,支持周月年度统计。 :智能管理您的财务生活 一、引言:财务智能化…...
Android NDK系列(四)NDK的编译
Native工程一般会用到NDK,一般开发者使用的NDK是官方提供的,直接下载即可使用。在工作过程中一般很少要定义NDK,不过对于想了解NDK是怎么生成的,可以继续往下阅读。 Google提供了编译NDK的说明文档,地址为NDK编译&…...

Jenkins--从入门到入土
Jenkins–从入门到入土 文章目录 Jenkins--从入门到入土〇、概念提要--什么是CI/DI?1、CI(Continuous Integration,持续集成)2、DI(DevOps Integration,DevOps 集成)3、解决的问题 一、Jenkins安…...

文心一言 VS 讯飞星火 VS chatgpt (267)-- 算法导论20.2 2题
二、写出 PROTO-vEB-DELETE 的伪代码。通过扫描簇内的相关位,来更新相应的 summary 位。并且你实现的伪代码的最坏情况运行时间是多少?如果要写代码,请用go语言。 文心一言: 首先,让我们回顾一下vEB(Van …...
C 语言设计模式(结构型)
文章目录 代理模式场景示例 门面模式场景示例 桥接模式场景示例 适配器模式场景示例 外观模式场景示例 享元模式场景示例 装饰器模式场景示例 组合模式场景示例 代理模式 C语言中,代理模式通常用于实现对象的间接访问。代理模式是一种结构型设计模式,它…...

【云原生--K8S】K8S python接口研究
文章目录 前言一、搭建ubuntu运行环境1.运行ubuntu容器2.拷贝kubeconfig文件二、python程序获取k8s信息1.获取node信息2.获取svc信息3.常用kubernetes API总结前言 在前面的文章中我们都是通过kubectl命令行来访问操作K8S,但是在实际应用中可能需要提供更方便操作的图形化界面…...
5.26作业
服务器 2 3 #define BUFSIZE 10244 #define login_msg_len 205 6 typedef struct Node{7 char name[login_msg_len];8 struct sockaddr_in addr;9 struct Node *next;10 }Node;11 12 typedef struct Msgtype{13 char type;14 char username[login_msg_len]…...
链接库文件体积优化工具篇:bloaty
笔者之前参与过一个嵌入式智能手表项目,曾经碰到过这样一个问题:手表的flash大小只有2M,这意味着只能在上面烧录2M大小的代码。随着开发不断进行,代码越写越多,编译出来的bin也越来越大。最后bin大小超过了2M, 就没法烧…...

使用pyqt绘制一个爱心!
使用pyqt绘制一个爱心! 介绍效果代码 介绍 使用pyqt绘制一个爱心! 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QPainter, QPen, QBrush, QColor from PyQt5.QtCore import Qt, Q…...
关于 Transformer 的11个常见面试题
Transformer 是如何工作的? Transformer 是一种深度学习算法,特别适用于自然语言处理(NLP)任务,如语言翻译、语言生成和语言理解。它们能够处理长度可变的输入序列并捕捉长距离依赖关系,使其在理解和处理自…...
OS多核多线程锁记录笔记
自旋锁作用 自旋锁的是为了保护两个核上的公共资源,也就是全局变量,只有在一方也就是一个核抢到了自选锁,才能对公共资源进行操作修改,当然还有其他形似的锁如互斥锁,这里不比较两者的区别,以前没有深入的去…...
nginx做TCP代理
要实现TCP代理,可以使用Nginx的stream模块。stream模块允许Nginx作为一个转发代理来处理TCP流量,包括TCP代理、负载均衡和SSL终止等功能。 以下是配置Nginx实现TCP代理的基本步骤: 在Nginx配置文件中添加stream块,并在该块中配置…...
python 异常处理 try
异常 我们常见的代码错误后 会出现此类异常 SyntaxError:语法错误 AttributeError:属性错误 IndexError:索引错误 TypeError:类型错误 NameError:变量名不存在错误 KeyError:映射中不存在的关键字…...
月入10万+管道收益,揭秘旅游卡运营的5个阶段!
网上的项目众多,只要用心,便能发现不少商机。在互联网上运营,关键在于理解项目的底层逻辑。今天,我们来揭秘旅游卡项目,如何做到月入10万。 1、先赚成本 开始项目时,首要任务是回本。不要急于求成&#x…...

android_binder源码分析之_binder驱动使用服务
一,binder驱动源码分析,使用服务过程 uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name) {uint32_t handle;unsigned iodata[512/4];struct binder_io msg, reply;bio_init(&msg, iodata, sizeof(iodata), 4);b…...
【波点音乐看广告】
import uiautomator2 as u2 import time from datetime import datetime import xml.etree.ElementTree as ET import re import os 连接设备 d u2.connect() os.system(‘adb shell chmod 775 /data/local/tmp/atx-agent’) os.system(‘adb shell /data/local/tmp/atx-age…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...