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

news/2024/5/19 21:56:22/文章来源:https://blog.csdn.net/m0_53142956/article/details/131928405

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.luyixian.cn/news_show_335098.aspx

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

相关文章

uniapp 即时通讯开发流程详解

今天我将为您详细介绍UniApp开发中的即时通讯流程。本文将向您展示如何在UniApp中实现即时通讯功能,为您的应用程序增添交互性和实时性。 1. 准备工作 在开始开发之前,确保您已完成以下准备工作: 确保您已经安装好UniApp开发环境&#xff…

实战攻防Demo|如何轻松形成自动响应的安全闭环?

从威胁阻断角度来说,拦住黑客的第一步攻击尤为重要。同样,对于攻击者来说,第一步攻击的成本也往往是最高的。日常工作中人们会遇到很多类型的攻击,但暴力破解或者撞库攻击往往被作为黑客的第一步攻击。这主要源于其技术含量低&…

chrome解决http自动跳转https问题

1.地址栏输入: chrome://net-internals/#hsts 2.找到底部Delete domain security policies一栏,输入想处理的域名,点击delete。 3.再次访问http域名不再自动跳转https了。

基于Java+SpringBoot+vue前后端分离海滨体育馆管理系统设计实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

Acwing.282 石子合并(动态规划)

题目 设有N堆沙子排成一排,其编号为1,2,3,…,N。 每堆沙子有一定的质量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆沙子的…

云计算需求激增带来的基础设施挑战及解决方案

云计算的指数级增长迅速改变了我们消费和存储数字信息的方式。随着企业和个人越来越依赖基于云的服务和数据存储,对支持这些服务的强大且可扩展的基础设施的需求已达到前所未有的水平。 云计算需求的快速增长 我们的日常生活越来越多地被新技术所渗透。流媒体服务、…

Visual Studio 2022 cmake配置opencv开发环境

1. 环境与说明 这里我用的是 widnows 10 64位,Visual Studio 用的 Visual Studio Community 2022 (社区版) 对于Android开发工程师来说,为什么要使用Visual Studio 呢 ? 因为在Visual Studio中开发调试OpenCV方便,可以开发调试好后&#xf…

vue脚手架文件说明

vue脚手架文件说明 1、文件介绍2、脚手架里面主要文件和作用 1、文件介绍 2、脚手架里面主要文件和作用 node_modules 都是下载的第三方包public/index.html 浏览器运行的网页src/main.js webpack打包的入口src/APP.vue Vue页面入口package.json 依赖包列表文件

【三维点云处理】顶点、面片、邻接矩阵、邻接距离矩阵以及稀疏存储概念

文章目录 vts和faces基础知识vertices-节点(3是点的三维坐标)faces-面片(3是构成三角形面片的3个点) 邻接矩阵邻接距离矩阵(NN500)稀疏矩阵 vts和faces基础知识 vertices-节点(3是点的三维坐标…

Rust vs Go:常用语法对比(六)

题图来自[1] 101. Load from HTTP GET request into a string Make an HTTP request with method GET to URL u, then store the body of the response in string s. 发起http请求 package mainimport ( "fmt" "io/ioutil" "net" "net/http…

Jmeter(二十三):快速生成测试报告

一、jmeter配置 首先要保证jmeter命令是ok的,如果你在cmd中输入jmeter -v,有出现如下截图所示的信息,那就说明jmeter环境ok; 二、jmeter执行结合命令 生成HTML测试报告 1.完成脚本的调试、参数化、断言等操作。然后在聚合报告中指定日志文件存储路径,路径中最好不要包含有…

Fatdog64 Linux 814发布

导读Fatdog64 Linux是一个小型、桌面、64位的Linux发行版。 最初是作为Puppy Linux的衍生品,并增加了一些应用程序。该项目最新的版本,Fatdog64 814,是8xx系列的最后一个版本,未来的版本将转向9xx基础。 尽管它是该系列的最后一个…

【C#】微软的Roslyn 是个啥?

一、说明 Roslyn 是微软重写的C#编译器并开源。 Roslyn 是 C# 和 Visual Basic.NET 开源编译器的代号。以下是它如何在过去十年企业Microsoft的最黑暗中开始,并成为所有C#(和VB)的开源,跨平台,公共语言引擎&#xff0c…

Training-Time-Friendly Network for Real-Time Object Detection 论文学习

1. 解决了什么问题? 目前的目标检测器很少能做到快速训练、快速推理,并同时保持准确率。直觉上,推理越快的检测器应该训练也很快,但大多数的实时检测器反而需要更长的训练时间。准确率高的检测器大致可分为两类:推理时…

STM32MP157驱动开发——按键驱动(工作队列)

文章目录 “工作队列”机制:内核函数work_struct 结构体定义 work使用 work :schedule_workworkqueue 其他函数 工作队列方式的按键驱动程序(stm32mp157)编程思路button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “工作队列”机制&#xff1…

【stable diffusion】保姆级入门课程05-Stable diffusion(SD)图生图-涂鸦重绘的用法

1.什么是涂鸦重绘 涂鸦重绘又称手涂蒙版。 简单来说,局部重绘手涂蒙版 就是涂鸦局部重绘的结合体,这个功能的出现是为了解决用户不想改变整张图片的情况下,对多个元素进行修改。 功能支持: 1.支持蒙版功能 2.笔刷决定绘制的元素…

DB-GPT:强强联合Langchain-Vicuna的应用实战开源项目,彻底改变与数据库的交互方式

今天看到 蚂蚁科技 Magic 开源的DB-GPT项目,觉得创意很好,集成了当前LLM的主流技术,主要如下 Langchain: 构建在LLM之上的应用开发框架HuggingFace: 模型标准,提供大模型管理功能Vicuna: 一个令GPT-4惊艳的开源聊天机…

如何在3ds max中创建可用于真人场景的巨型机器人:第 3 部分

推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建腿部装备 步骤 1 打开 3ds Max。 打开在本教程最后一部分中保存的文件。 打开 3ds Max 步骤 2 转到创建> 系统并单击骨骼。 创建>系统 步骤 3 为的 侧视口中的腿,如下图所示…

更省更快更安全的云服务器,一站式集中管理,随时随地远程——站斧云桌面

随着全球化和数字化经济的发展,越来越多的企业开始海外扩张和拓展国际市场。而云服务器作为一种高效、灵活且可靠的IT基础设施方案,已成为出海企业不可或缺的重要工具。这里就为大家介绍云服务器在出海企业中的几个使用场景。 1.全球范围内协同办公 对…

开源大模型LLaMA 2会扮演类似Android的角色么?

在AI大模型没有商业模式?等文章中,我多次表达过这样一个观点:不要把大模型的未来应用方式比喻成公有云,大模型最终会是云端操作系统的核心(新通用计算平台),而它的落地形式会很像过去的沃森&…