深入理解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技术的未来…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
