深入理解Redis事务、事务异常、乐观锁、管道
Redis事务与MySQL事务
- 不一样。
- 原子性:MySQL有Undo Log机制,支持强原子性,和回滚。Redis只能保证事务内指令可以不被干扰的在同一批次执行,且没有机制保证全部成功则提交,部分失败则回滚。
- 隔离性:MySQL的隔离性指多个事务可以并发执行,MySQL有MVCC机制。而Redis没有,Redis是事务提交前的指令不会被执行,单线程的环境下,也就不存在事务未提交时,事务内外数据不一致的隔离性问题了。
- 持久性:MySQL事务先写Undo Log,并有Redo Log的两阶段提交机制,可以保证持久性。但是Redis持久化机制只有RDB和AOF持久化策略,若事务成功执行且数据刚好被保存,则可以满足持久性。
- 一致性:MySQL是指数据库从一个合法(指符合业务预期)状态转换成另一个合法状态,这种只要Redis执行不出错,可以保证。
Redis事务
- 官方文档:https://redis.io/docs/latest/develop/interact/transactions/
- 极简概括:将一批要执行的Redis指令,放入Redis的执行队列中,事务执行时(不包含事务未提交时) 使其不被并发过来的任务干扰执行。(无法做到严格意义上的ACID 4特性)。
- 适用场景:
- 性能优化:10条命令传输10次执行10次,与1次批量执行10条命令,性能有差异。
- 乐观锁实现:结合Watch可以实现乐观锁。
- 优点:如上的应用场景就是优点。
- 缺点:无法像MySQL那样保证原子性、持久性。
- 关键字:mutli(开启事务),discard(停止事务)、exec(执行事务)、watch(监视指定key)、unwatch(取消监视所有key)。
事务操作实操
测试multi与exec,常规执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a a
QUEUED
127.0.0.1:6379> set b b
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
测试discard,事务未提交,强行终止,则修改不会生效
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a a1
QUEUED
127.0.0.1:6379> set b b1
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get a
"a"
127.0.0.1:6379> get b
"b"
Redis事务异常(语法错误导致整个事务执行失败,非回滚操作)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a a2
QUEUED
127.0.0.1:6379> sset b b2
(error) ERR unknown command `sset`, with args beginning with: `b`, `b2`,
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get a
"a"
127.0.0.1:6379> get b
"b"
Redis事务异常(非语法错误引起的部分失败,无法保证ACID中的A,无回滚机制)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a aa
QUEUED
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> set b bb
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 a
"aa"
127.0.0.1:6379> get b
"bb"
有Redis事务,为什么又出来了Lua?
- Redis事务和Lua机制并不冲突,并且要比Redis事务更加强大。
- 应对并发安全问题:虽然有了Lua的加持,仍不支持事务回滚或者,强原子性(要么都成功,要么都回滚),但是Lua可以保证当前的操作不被打断(无间隙执行),应对并发(例如超卖)问题,Lua能妥善解决。
- Redis事务不支持流程控制,只支持函数调用:配合Lua用于实现无间隙执行的复杂逻辑,这样的用法非常多。因为高并发下,若单纯利用编程语言多次调Redis,实现判断或循环逻辑,这中间有间隙,会有并发问题发生。
Lua是一门高性能脚本语言,Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译、运行。Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。
关于Redis+Lua是否是原子性执行的争议问题
https://redis.io/docs/latest/develop/interact/programmability/eval-intro/
对Redis官网进行搜索,出现了原子性的字眼。
原话是:
Blocking semantics that ensure the script’s atomic execution.
Lua lets you run part of your application logic inside Redis. Such scripts can perform conditional updates across multiple keys, possibly combining several different data types atomically.
但是我想了想有矛盾的地方:
MySQL使用了undo log来保证原子性,要么成功全部执行,要么失败全部回滚。
众所周知,Redis不支持回滚的,那么ACID的A就没办法全部保证,最多是没有执行期间没有间隙,不被其它过来的请求影响,引起并发问题。
然后我又看了看阿里某架构师对此的剖析,跟我设想的一样:
Redis会把Lua脚本当做一个整体去执行,中间不会被其它的命令插入,但是如果执行过程中出现了错误,事务是不会回滚的。
也就意味着执行Lua脚本的过程不可被拆分,不可被中断,但是遇到错误不会回滚。
Redis乐观锁
- 悲观锁:很悲观,认为数据大概率会有并发一致性问题,首次请求过来时加具有互斥性的锁阻塞其它并发请求,但是Redis是高性能组件,阻塞会带来性能问题,所以不用悲观锁。
- 乐观锁:乐观,认为数据小概率有并发一致性问题,所以读数据时不上锁,但是写数据时,会判断一下这个数据是否被改动,从而在旧值的基础上做修改,如果数据被改动,则失败掉此次执行。
- 注意:redis在事务exec或者discard,都会取消对key的watch操作。
- 解决问题:高并发读多写少场景下Redis数据一致性问题。
- 演变:
假设用户a账户有100元,此时要添加10元
127.0.0.1:6379> set a_money 100
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby a_money 10
QUEUED
127.0.0.1:6379> exec
1) (integer) 110
127.0.0.1:6379> get a_money
"110"
假设用户a账户有110元,此时要添加20元,但是事务未提交期间,已经被其它请求改为了115,然后事务内加了20。
由于是加法,所以值正确,但是事务内的数据一般是不让改的,很多情况下的自增或者自减,是需要以原数据为基础基础为准的(这也是MySQL隔离级别的用意,所以有了当前读和快照读的区分)。
终端1
127.0.0.1:6379> get a_money
"110"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby a_money 20
QUEUED终端2
127.0.0.1:6379> get a_money
"110"
127.0.0.1:6379> incrby a_money 5
(integer) 115终端1
127.0.0.1:6379> get a_money
"110"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby a_money 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 135
127.0.0.1:6379> get a_money
"135"
Redis没有事务的隔离机制怎么办?使用watch加锁。
终端一
127.0.0.1:6379> watch a_money
OK
127.0.0.1:6379> get a_money
"135"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby a_money 20
QUEUED终端二模拟其它并发用户
127.0.0.1:6379> incrby a_money 5
(integer) 140终端1
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get a_money
"140"
事务没有成功被执行,因为watch监控了a_money的值,一旦事务执行期间,被事务外的请求锁修改,则失败掉此次事务。
乐观锁,在此处的体现就是,利用watch监控一下事务执行期间,a_money的值是否被改动。
unwatch 使用
终端1
127.0.0.1:6379> set a a
OK
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a a1终端2,模拟并发过来的用户请求
127.0.0.1:6379> set a a2
OK终端1,执行unwatch后,取消了对所有key的监控,执行exec时,就不是nil了。
127.0.0.1:6379> exec
1) OK
127.0.0.1:6379> get a
"a1
watch部分key,其余key的反应
终端1
127.0.0.1:6379> set a a
OK
127.0.0.1:6379> set b b
OK
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a a1
QUEUED
127.0.0.1:6379> set b b1
QUEUED终端2
127.0.0.1:6379> set a a2
OK
127.0.0.1:6379> set b b2
OK终端1,watch a,没有watch b,事务提交时,被watch的key,可以影响没有被watch的key。
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get a
"a2"
127.0.0.1:6379> get b
"b2"
管道
- 官方文档:https://redis.io/docs/latest/develop/use/pipelining/
- 极简概括:将多个指令的操作,一次性发送给Redis,进行批量处理。
- 解决问题:减少网络开销,减少频繁接收命令的开销(10轮request->exec->response,精简为1次request->10次exec->1次response),避免多条Redis指令通信往返时间。避免Redis服务器频繁的从用户态到内核态的调用,减少上下文通信时间。
- 与事务对比:批量处理指令的行为,类似事务。
- 注意:redis-cli会话内部并未提供管道命令,(但是使用Linux Shell端支持STDIN标准输入到redis-cli实现管道,例如
echo -e "set a aa \n set b bb" | redis-cli --pipe
),但redis-server提供了这个机制,管道机制最好用编程语言的客户端演示。
若在redis-cli会话内部实现管道,会有如下提示:
127.0.0.1:6379> pipe
(error) ERR unknown command `pipe`, with args beginning with:
127.0.0.1:6379> pipeline
(error) ERR unknown command `pipeline`, with args beginning with:
- PHP实现:
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);$pipe = $redis->pipeline();$pipe->set('key1', 'value1');
$pipe->set('key2', 'value2');
$pipe->get('key1');
$pipe->get('key2');$responses = $pipe->exec();var_dump($responses);$redis->close();返回执行的结果
array(4) {[0]=>bool(true)[1]=>bool(true)[2]=>string(6) "value1"[3]=>string(6) "value2"
}
管道异常情况(Redis语法错误)
以PHP为例,经实际测试(set函数缺少参数2),Redis调用语法错误(非PHP语法错误),会升级为PHP出现致命错误,管道流程走不下去。
Fatal error: Uncaught ArgumentCountError: Redis::set() expects at least 2 arguments, 1 given in E:\Host\test\t1.php:7
Stack trace:
#0 E:\Host\test\t1.php(7): Redis->set('a')
#1 {main}thrown in E:\Host\test\t1.php on line 7
管道异常情况(Redis执行异常)
经过实测,对字符串进行递增操作,除了incr返回false外,其余上下文代码执行不受影响。
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);$pipe = $redis->pipeline();$pipe->set('a', 'a');
$pipe->incr('a');
$pipe->set('b', 'b');
$pipe->get('a');
$pipe->get('b');$responses = $pipe->exec();var_dump($responses);$redis->close();array(5) { [0]=> bool(true)[1]=>bool(false)[2]=>bool(true)[3]=>string(1) "a"[4]=>string(1) "b"
}
相关文章:

深入理解Redis事务、事务异常、乐观锁、管道
Redis事务与MySQL事务 不一样。原子性:MySQL有Undo Log机制,支持强原子性,和回滚。Redis只能保证事务内指令可以不被干扰的在同一批次执行,且没有机制保证全部成功则提交,部分失败则回滚。隔离性:MySQL的隔…...

17、Spring系列-SpringMVC-请求源码流程
前言 Spring官网的MVC模块介绍: Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。正式名称“ Spring Web MVC”来自其源模块的名称(spring-webmvc),但它通常被称为“ Spring MVC…...

对简单工厂模式、工厂方法模式、抽象工厂模式的简单理解
简单工厂模式 三部分组成 抽象类一些抽象类的具体实现类工厂类 把创建对象的任务交给一个工厂类来实现,对业务进行封装。 优点:实现了任务分离,客户端不用关心业务的具体实现,交由工厂来“生产”。 缺点:违背开闭原…...

PostgreSQL常用插件
PostgreSQL 拥有许多常用插件,这些插件可以大大增强其功能和性能。以下是一些常用的 PostgreSQL 插件: 性能监控和优化 pg_stat_statements 1.提供对所有 SQL 语句执行情况的统计信息。对调优和监控非常有用。 2.安装和使用: pg_stat_k…...

mysql表字段超过多少影响性能 mysql表多少效率会下降
一直有传言说,MySQL 表的数据只要超过 2000 万行,其性能就会下降。而本文作者用实验分析证明:至少在 2023 年,这已不再是 MySQL 表的有效软限制。 传言 互联网上有一则传言说,我们应该避免单个 MySQL 表中的数据超过 …...

Vue进阶之Vue无代码可视化项目(一)
Vue无代码可视化项目 项目搭建初始步骤拓展:工程项目从0-1项目规范化package.jsoncpell.jsoncustom-words.txtts-eslint规则.eslintrc.cjsgit钩子检查有没有问题type-checkspellchecklint:stylehusky操作安装pre-commitpnpm的commit规范package.json:commitlint.config.cjs安装…...

初识C++ · 模拟实现list
目录 前言 1 push_back pop_back 2 迭代器类 2.1 ! 2.2 -- 2.3 * 3 Print_List 4 有关自定义类型 5 有关const迭代器 6 拷贝构造 赋值 析构 Insert erase 前言 有了string,vector的基础,我们模拟实现list还是比较容易的,这里同…...

电商运营-2024年6月1日
作为一名电商运营,针对淘工厂平台,需要具备以下核心技能和素质: 核心技能 新店入驻与产品管理 熟练掌握淘工厂平台的新店入驻流程,包括资质准备、资料提交、审核跟进等。精通产品上架技巧,确保产品信息准确、图片清晰…...

Go跨平台编译
1.编译windows平台运行程序 # windows env GOOSwindows GOARCHamd64 go build main.go2.编译linux平台运行程序 # linux env GOOSlinux GOARCHamd64 go build main.go 3.编译macos平台运行程序 # macos env GOOSdarwin GOARCHamd64 go build main.go 编译结果:...

生产计划排产,制定每小时计划产量(“查表法”SQL计算)
根据日生产计划产量排产,制定每2小时理论计划生产产量。 每2小时计划产量 每2小时工作时间(秒)/生产计划节拍(秒)。 假设,生产计划节拍 : 25.0(秒)/台 工厂以每天8点00分钟作为当日工作日的…...

视频汇聚管理安防监控平台EasyCVR程序报错“create jwtSecret del server class:0xf98b6040”的原因排查与解决
国标GB28181协议EasyCVR安防视频监控平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力,平台支持7*24小时实时高清视频监控,能同时播放多路监控视频流…...

头歌页面置换算法第2关:计算OPT算法缺页率
2 任务:OPT算法 2.1 任务描述 设计OPT页面置换算法模拟程序:从键盘输入访问串。计算OPT算法在不同内存页框数时的缺页数和缺页率。要求程序模拟驻留集变化过程,即能模拟页框装入与释放过程。 2.2任务要求 输入串长度作为总页框数目,补充程序完成OPT算法。 2.3算法思路 OPT算…...

vscode怎么拷贝插件到另一台电脑
说明 vscode插件默认存放在 C:\Users\用户名\.vscode 目录下的 extensions 文件夹中 方法 拷贝 C:\Users\用户名\.vscode 目录下的 extensions 文件夹到另一台电脑的C:\Users\用户名\.vscode 目录下 C:\Users\用户名\.vscode...

网络协议分析
网络协议分析 网络协议分析概述用IP实现异构网络互联网络协议的分层TCP/IP的分层模型协议分析协议分析应用协议分析任务 常见网络协议PPP协议报文选项IPCP认证协议PAP安全缺陷认证协议CHAPPPPoE协议流程 地址解析协议ARPARP的思想和步骤ARP报文格式及封装 移动IP移动IP的工作机…...

GAMIT目录配置
1打开home,显示隐藏文件,CTRH 2修改目录 #set gamitpath gamitpath/opt/gamit10.7 export PATH$PATH:${gamitpath}/com/:${gamitpath}/gamit/bin:${gamitpath}/kf/bin HELP_DIR${gamitpath}/help export HELP_DIR #set GMT path gmtpath/usr/lib/gmt P…...

基于JSP的九宫格日志网站
你好呀,我是学长猫哥!如果有需求可以文末加我。 开发语言:Java 数据库:MySQL 技术:JSP技术 工具:浏览器/服务器(B/S)结构 系统展示 首页 管理员功能模块 用户功能模块 摘要 本…...

C#中结构struct能否继承于一个类class,类class能否继承于一个struct
C#中结构struct能否继承于一个类class,类class能否继承于一个struct 答案是:都不能。 第一种情行,尝试结构继承类 报错:接口列表中的类型"XX"不是接口interface。 一般来说,都是结构只能实现接口&#x…...

【Vulhub】Fastjson 1.2.24_rce复现
文章目录 一,Fastjson是什么?二,fastjson漏洞原理三,判断是否有fastjson反序列化四,复现Fastjson 1.2.24_rce(vulhub)环境配置1.判断是否存在Fastjson反序列化2.反弹shell3.启动RMI服务器4.构造恶意POST请求 一&#x…...

【iconv】UTF-8字符串转换为UTF-16字符串
使用<iconv.h>来进行字符串编码的转换 #include <iconv.h> #include <iostream> #include <string.h> #include <unistd.h> #include <memory> #include <fcntl.h>// 需要链接iconv库// iconv -l 命令可列出所有支持的格式 // exam…...

AI技术的未来展望:重塑人类社会的智能革命
一、引言 随着技术的飞速发展,人工智能(AI)已经不再是科幻小说中的概念,而是成为了我们生活中不可或缺的一部分。从简单的智能助手到复杂的自动化生产线,AI技术正在以前所未有的速度改变着世界。本文将对AI技术的未来…...

掘金AI 商战宝典-系统班:2024掘金AIGC课程(30节视频课)
课程目录 1-第一讲学会向Al提问:万能提问公式_1.mp4 2-第二讲用AI写视频脚本_1.mp4 3-第三讲用AI写视频口播文案_1.mp4 4-第四讲用AI自动做视频(上)_1.mp4 5-第五讲用AI自动做视频(中)_1.mp4 6-第六讲用AI自动做视…...

C# WinForm —— 26 ImageList 介绍
1. 简介 图片集合,用于存储图像的资源,并在关联控件中显示出来 可以通过 索引、键名 访问每张图片 没有事件 2. 属性 属性解释(Name)控件ID,在代码里引用的时候会用到,一般以 imgList 开头ClolorDepth用于呈现图像的颜色数,默…...

Vue:现代前端开发的首选框架-【声明周期钩子详解】
引言 Vue.js 是一个流行的前端框架,它通过组件化的开发方式,让开发者能够构建出高效且可维护的应用程序。在Vue中,生命周期钩子(Lifecycle Hooks)是理解组件行为的关键概念。本文将深入探讨Vue生命周期钩子࿰…...

【因果推断python】8_线性回归模型2
目录 回归理论 非随机数据的回归 回归理论 我不打算深入研究线性回归是如何构建和估计的。然而,一点点理论将有助于解释它在因果推断中的力量。首先,回归解决了理论上的最佳线性预测问题。令 是一个参数向量: 线性回归找到最小化均方误差 (…...

MySQL目录和文件
MySQL目录和文件 bin目录 存储一些mysql脚本比如mysqld、mysqld-self等等,用于执行mysql一些操作 数据目录 show variables like datadir;--查看数据目录位置每一个数据库都有一个和数据库名相同的文件夹;MySQL5.7开始每创建一个表,在Innod…...

0基础学习Elasticsearch-Quick start
文章目录 1 背景2 前言3 快速部署ES4 快速部署Kibana5 发送请求给ES5.1 打开Kibana控制台5.2 通过REST API发送请求5.3 通过curl发送请求5.4 添加数据5.4.1 添加单个document5.4.2 添加多个document 5.5 搜索数据5.5.1 搜索所有documents5.5.2 match查询 6 总结 1 背景 因电商项…...

Centos给普通用户添加sudo命令权限
打开sudoers文件 sudo visudo 修改sudoers文件 找到root ALL(ALL) ALL这一行,即如下图标出红线的一行 在此行下新增如下内容: lbs为用给予sudo执行权限的用户名 # 执行sudo命令,需要输入命令 lbs ALL(ALL) ALL 或 # 执行sudo命令,…...

编写备份MySQL 脚本
目录 环境准备 增量备份 增量备份和差异备份 完整代码如下 测试脚本是否正常 星期天运行脚本(完全备份) 星期一运备份脚本(增量备份) 星期二备份数据(其他天--增量备份) 星期三备份数据(差异备…...

C语言中的数据类型转换:隐式类型转换与显示类型转换
一. 简介 本文简单学习一下,C语言中的数据类型转换。重点学习一下隐式类型转换。 二. C语言中的数据类型转换:隐式类型转换与显示类型转换 类型转换(TypeCasting):在C语言中是将一种数据类型值转换为另一种数据类型…...

Android 安卓通过bindService ServiceConnection 没有响应的问题
bindService 失败且 ServiceConnection 没有响应可能由多种原因造成。以下是一些可能的原因和相应的解决方法: Service未正确声明或注册: 如果Service没有在AndroidManifest.xml文件中正确声明或注册,bindService将会失败。解决方法…...