redis的并发安全问题:redis的事务VSLua脚本

redis为什么会发生并发安全问题?

在redis中,处理的数据都在内存中,数据操作效率极高,单线程的情况下,qps轻松破10w。反而在使用多线程时,为了保证线程安全,采用了一些同步机制,以及多线程的上下文切换,却对性能造成了一定的影响。
如此看来,在单线程模式下,redis的性能比较高,且可以避免多线程情况下的线程安全问题。但是在redis使用过程中,线程安全问题依旧存在,此话怎讲呢?
线程安全,是站在reids的角度来说的,redis使用单线程模型,是不存在线程安全问题的,以为他只有一个线程,不存在多线程间数据的共享,俗话说没有共享就没有伤害。
而线程不安全,是站在客户端的角度说的,redis是只有一个线程在工作,但是客户单端却是有成千上万个的,对于客户端来说,redis是被共享的资源,所以对于客户端来说依旧存在线程安全问题,
举例:
1.商品库存开始为10,此时客户端A和客户端B,同时下单扣减库存。
2.两个客户端从redis获取到当前库存数为10。
3.两个客户端在本地将库存数减1,然后写回redis。
4,此时redis中存库数为9,正常情况下应该是8,造成超卖问题。
在这里插入图片描述
————————————————
版权声明:本文为CSDN博主「wind_huise」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45701550/article/details/126324924

读库存和写库存操作,在redis中是单线程执行的,是原子性的,但是整个扣减库存的操作却不是原子性的,这也是出现线程不安全的根本原因

对于解决这个问题,redis提供了一些复合命令,将多个操作合并成一个操作命令,此时这个复合命令就变成一个原子操作,也就不会再出现上述的线程安全问题了。如果需要实现简单的原子性操作,则可以使用Redis的单个指令,例如SET、GET、DEL等来实现;如果需要实现复杂数据结构的操作,则可以考虑使用Redis事务、Lua脚本来实现。

Redis的事务模型

在Redis中,事务是通过MULTI、EXEC、WATCH、UNWATCH等指令来实现的。在MULTI指令开始执行事务之前,客户端发送的每个指令都不会立即执行,而是被缓存到一个队列中,直到EXEC指令被执行时,所有缓存的指令才一起被执行。这种模型被称为乐观锁,因为在执行事务期间,Redis并不会对被监控的数据进行加锁,而是在执行EXEC指令时进行条件检查,如果检查失败,则事务执行失败。这种设计可以减少锁的竞争,提高Redis的并发性能。

流程如下:
1、开启事务:MULTI
客户端要使用一个命令显式地表示一个事务的开启。在Redis中,这个命令就是MULTI。
2、指令入队(暂时不执行)
客户端把事务中本身要执行的具体操作(例如增删改数据)发送给服务器端。这些操作就是Redis本身提供的数据读写命令,例如GET、SET等。不过,这些命令虽然被客户端发送到了服务器端,但Redis实例只是把这些命令暂存到一个命令队列中,并不会立即执行。
3、执行指令:EXEC
客户端向服务器端发送提交事务的命令,让数据库实际执行第二步中发送的具体操作。Redis 提供的EXEC命令就是执行事务提交的。当服务器端收到EXEC命令后,才会实际执行命令队列中的所有命令

Redis对于事务操作的支持比较局限,最大的不足是不支持回滚。执行事务时,Redis将这些命令依次执行,如果有一条命令执行失败,就会导致整个事务的失败,但不能回滚已经执行成功的命令

在Redis的事务模型中,由于不会对被监控的数据进行加锁,因此如果事务执行失败,只能够通过执行一系列的逆操作来恢复数据状态,而无法直接回滚事务。这是因为对于一些操作,例如INCRBY、RPUSH、SUNION、ZUNIONSTORE等,Redis并没有提供对应的逆操作。因此,如果使用Redis来实现复杂的事务操作,可能需要自己实现回滚逻辑,这会增加开发和维护的复杂度。

Lua脚本

基本原理为使脚本相当于一个redis命令,可以结合redis原有命令,自定义脚本逻辑

如何在redis中使用lua脚本,可以参考https://redis.io/commands/eval/

Lua脚本之所以可以保证线程安全,是因为我们可以把多个操作写成一个 lua 脚本,使其具备原子性,作为一个整体执行。再由于 redis 是单线程模型,不同线程的 lua 脚本是依次执行的。也就是说,只有一个线程原子性的多个操作执行完,下一个线程才可以执行。实际上也是保证了在 redis 内部不同线程操作的串行执行,从而能够解决并发安全问题。
【简单来说,就是lua脚本包括业务的多个操作,使得整个业务成为一个整体,执行时相当于把整个lua脚本当成一个redis指令】

Lua脚本的优点

1.lua脚本是作为一个整体执行的,所以中间不会被其他命令插入,无需担心并发;

2.lua脚本把多条命令一次性打包,而代码实现的事务需要向Redis发送多次请求,所以可以有效减少网络开销;

3.lua脚本可以常驻在redis内存中,所以在使用的时候,可以直接拿来复用。

redis事务和Lua两者相同点

很好的实现了一致性、隔离性和持久性,但没有实现原子性,无论是redis事务,还是lua脚本,如果执行期间出现运行错误,之前的执行过的命令是不会回滚的。

lua 脚本实现的原子性是假的原子性

因为当多个指令执行时,lua脚本中的一条指令报错时,后面的指令执行失败了,但是前面的指令已经成功。且不会回滚

其中报错原因包括:
1、指令语法错误
2、语法是正确的,但是类型不对,比如对已经存在的string类型的key,执行hset等
3、服务器挂掉了,比如lua脚本执行了一半,但是服务器挂掉了

前面两者,我们可以通过仔细检查脚本逻辑,确保脚本中的所有命令都是正确的,并且按照预期的顺序执行、使用redis.pcall处理潜在的错误以及适时使用事务,可以编写出高效、可靠的Lua脚本,确保脚本的逻辑正确性和健壮性,以避免潜在的问题。

redis.pcall在命令执行失败时不会引发错误,而是返回一个包含错误信息的表。通过检查redis.pcall的返回值,可以在脚本中处理错误情况,从而避免脚本执行失败

Lua脚本回滚?
在Lua脚本中,我们可以使用Redis的WATCH指令,它允许我们监视一个或多个键的变化。当我们监视的键发生变化时,Redis会立刻中断正在执行的Lua脚本,使得整个Lua脚本操作回滚,这种方式可以实现Redis事务的回滚效果。

Lua脚本相对于Redis事务更实用的原因有以下几点:

1、原子性保证:使用Lua脚本可以将多个命令封装成一个原子操作,确保这些命令在执行期间不会被其他命令插入,从而保证操作的原子性。
2、减少网络开销:在Lua脚本中,多个命令可以一次性发送到Redis服务器,并由Redis执行,减少了网络开销。而Redis事务需要通过MULTI和EXEC命令来开启和提交事务,增加了网络往返的次数。
3、高性能:由于Lua脚本在Redis服务器端执行,避免了客户端与服务器之间的通信。这样可以减少通信延迟,并在服务器端以原生代码的方式执行,提高了执行效率。相比之下,Redis事务在客户端和服务器之间进行多次通信,可能降低执行效率。
4、复杂逻辑支持:Lua脚本提供了强大的编程能力,可以实现复杂的业务逻辑。通过脚本的编写,可以实现数据库操作的灵活性,从而适应更多的场景需求。Redis事务对于复杂逻辑的支持相对较弱,更适合简单的操作序列。

参考文章:
使用redis,怎么解决并发问题?
redis为什么不支持事务(redis不支持的数据结构)
Redis实例发生故障,而Redis使用的RDB机制,事务的原子性还能否得到保证?
Redis不支持事务的解决方案(redis事务为什么不支持回滚)
Redis实例发生故障,而Redis使用的RDB机制,事务的原子性还能否得到保证?

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

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

相关文章

windows中注册redis服务启动时报1067错误

注册完redis服务,打开计算机 服务时确实有redis服务存在,但是点击启动时却报1067错误,而命令行用redis-server.exe redis.windows.conf 命令却也可以启动 查看6379的端口也没有被占用(netstat -ano | findstr :6379) …

Mac 定时重启 TouchBar 脚本(缓解闪烁问题)

背景 Mac 笔记本 TouchBar 是真的脆啊,合盖使用一段时间就废了,右侧一直闪烁简直亮瞎眼 😂 经过观察,总结出闪烁规律如下: 工作状态:不断操作电脑时,触控栏处于工作状态,几乎不闪…

第14届蓝桥杯国赛题解

T1. 八进制回文数 #include <iostream> typedef long long LL; using namespace std; int a[30]; //转八进制 LL f(LL m) {LL sum 0, n 0;while(m) {a[ n] m % 8;m / 8;}for(int i n; i > 1; i --) sum sum * 10 a[i];return sum; } //数字反转 LL r(LL m) {LL…

Apipost教程?一篇文章玩转Apipost

你是否经常遇到接口开发过程中的各种问题&#xff1f;或许你曾为接口测试与调试的繁琐流程而烦恼。不要担心&#xff01;今天我将向大家介绍一款功能强大、易于上手的接口测试工具——Apipost&#xff0c;并带你深入了解如何玩转它&#xff0c;轻松实现接口测试与调试。 什么是…

思科认证 | CCIE考过了,证书编号怎么查?

考CCIE证书是一个很辛苦的过程&#xff0c;你努力考证的最终目的就是为了拿证&#xff0c;有了CCIE证书你才能证明你自己的技术能力。 那么如何查询CCIE证书呢&#xff1f;看这里。 01 如何查询CCIE证书 1. Cisco官方认证查询系统 Cisco官方网站提供了一个在线认证查询系统&a…

深入浅出Pytorch函数——torch.no_grad

分类目录&#xff1a;《深入浅出Pytorch函数》总目录 禁用梯度计算的上下文管理器。当我们确信不会调用Tensor.backward()时&#xff0c;禁用梯度计算对推理很有用。它将减少计算的内存消耗&#xff0c;否则我们需要设置requires_gradTrue。在这种模式下&#xff0c;即使输入的…

1992-2021年全国及31省对外开放度测算数据含原始数据和计算过程(无缺失)

1992-2021年全国及31省对外开放度测算数据含原始数据和计算过程&#xff08;无缺失&#xff09; 1、时间&#xff1a;1992-2021年 2、范围&#xff1a;全国及31省 3、指标&#xff1a;进出口总额、国内生产总值、年均汇率 4、计算方法&#xff1a;对外开放度进出口总额/GDP…

【Linux 网络】 HTTPS协议原理 对称加密 非对称加密 数字证书

HTTPS协议 HTTPS协议和HTTP协议的区别什么是“加密” 和“解密”加密和解密的小故事 为什么要进行加密&#xff1f;臭名昭著的“运营商劫持”事件 常见加密方式对称加密非对称加密 数据摘要数字签名 HTTPS工作过程探究方案 1 &#xff1a; 只使用对称加密方案2 &#xff1a; 只…

【开发问题记录】01—大量数据同时插入数据库导致的时间戳重复问题

需求背景 用户登录之后将其云端收藏的内容同步到本地数据库且保持和原收藏顺序一致, 比如在电脑上登录之后显示的收藏顺序A->B->C 切换到手机登录之后的顺序预期也是A->B->C, 之后的查询就可以不依赖云端接口而是直接查本地数据库 代码抽象 针对这个场景我们抽象出…

rsync 远程同步

1.简介 rsync&#xff08;Remote Sync&#xff0c;远程同步&#xff09; 是一个开源的快速备份工具&#xff0c;可以在不同主机之间镜像同步整个目录树&#xff0c;支持增量备份&#xff0c;并保持链接和权限&#xff0c;且采用优化的同步算法&#xff0c;传输前执行压缩&#…

从0到1开发go-tcp框架【2-实现Message模块、解决TCP粘包问题、实现多路由机制】

从0到1开发go-tcp框架【2-实现Message模块、解决TCP粘包问题、实现多路由机制】 1 实现\封装Message模块 zinx/ziface/imessage.go package zifacetype IMessage interface {GetMsdId() uint32GetMsgLen() uint32GetMsgData() []byteSetMsgId(uint32)SetData([]byte)SetData…

淘宝10年架构演进

目录 1. 概述 2. 基本概念 3. 架构演进 3.1 单机架构 3.2 第一次演进&#xff1a;Tomcat与数据库分开部署 3.3 第二次演进&#xff1a;引入本地缓存和分布式缓存 3.4 第三次演进&#xff1a;引入反向代理实现负载均衡 3.5 第四次演进&#xff1a;数据库读写分离 3.6 第…

C语言第十三课--------初阶指针的认识--------重要部分

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; &#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382;…

CTF:信息泄露.(CTFHub靶场环境)

CTF&#xff1a;信息泄露.&#xff08;CTFHub靶场环境&#xff09; “ 信息泄露 ” 是指网站无意间向用户泄露敏感信息&#xff0c;泄露了有关于其他用户的数据&#xff0c;例如&#xff1a;另一个用户名的财务信息&#xff0c;敏感的商业 或 商业数据 &#xff0c;还有一些有…

无涯教程-jQuery - Ajax Tutorial函数

AJAX是用于创建交互式Web应用程序的Web开发技术。如果您了解JavaScript,HTML,CSS和XML,则只需花费一个小时即可开始使用AJAX。 为什么要学习Ajax? AJAX代表 A 同步 Ja vaScript和 X ML。 AJAX是一项新技术,可借助XML,HTML,CSS和Java Script创建更好,更快,更具交互性的Web应用…

x264低时延低码率高质量调参

h264解码延迟优化_H264编码参数优化&#xff1a; 参考文章&#xff1a;Video Codecs Analysis and Tuning &#xff0c;文档下载地址&#xff1a;http://www.yuvsoft.com/pdf/x264_parameters_comparison.pdf 文章中详细比较了48种不同x264参数组合&#xff0c;比较得到如下6类…

QT数据库编程

ui界面 mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include <QButtonGroup> #include <QFileDialog> #include <QMessageBox> MainWindow::MainWindow(QWidget* parent): QMainWindow(parent), ui(new Ui::M…

FFmpeg 音视频开发工具

目录 FFmpeg 下载与安装 ffmpeg 使用快速入门 ffplay 使用快速入门 FFmpeg 全套下载与安装 1、FFmpeg 是处理音频、视频、字幕和相关元数据等多媒体内容的库和工具的集合。一个完整的跨平台解决方案&#xff0c;用于录制、转换和流式传输音频和视频。 官网&#xff1a;http…

uni-ajax网络请求库使用

uni-ajax网络请求库使用 uni-ajax是什么 uni-ajax是基于 Promise 的轻量级 uni-app 网络请求库,具有开箱即用、轻量高效、灵活开发 特点。 下面是安装和使用教程 安装该请求库到项目中 npm install uni-ajax编辑工具类request.js // ajax.js// 引入 uni-ajax 模块 import ajax…

微信小程序测试要点

一、什么是小程序&#xff1f; 可以将小程序理解为轻便的APP&#xff0c;不用安装就可以使用的应用。用户通过扫一扫或者搜索的方式&#xff0c;就可以打开应用。 小程序最主要的特点是内嵌于微信之中&#xff0c;而使用小程序的目的是为了能够方便用户不在受下载多个APP的烦…