Redis核心技术与实战【学习笔记】 - 20.Redis原子操作及并发访问

概述

使用 Redis 时,不可避免地会遇到并发访问的问题,比如说如果多个用户同时下单,就会对缓存在 Redis 中的商品库存并发更新。一旦有了并发写操作,数据就会被修改,如果我们没有对并发写请求做好控制,就可能导致数据被改错,影响业务的正常使用(例如,库存数据错误,导致下单异常)。

为了保证并发访问的正确性,Redis 提供了两种方法,分别是加锁和原子操作。当一个客户端获得锁后,就会一直持有这把锁,直到客户端完成数据更新,才释放这把锁。

但是用锁会有两个问题:

  • 一个是,如果加锁操作多,会降低系统的并发访问性能。
  • 第二个是,Redis 客户端需要加锁时,需要用到分布式锁,而分布式锁实现复杂,需要用额外的存储系统来提供枷锁解锁操作。

原子操作是另一种提供并发访问控制的方法。原子操作是指执行过程保持原子性的操作,而且原子操作执行时并不需要加锁,实现了无锁操作。这样一来,既能保证并发控制,还能减少对系统并发性能的影响。


1.并发访问中需要对什么进行控制?

并发访问控制是指对多个客户端访问操作同一份数据的过程进行控制,以保证任何一个客户端发送的操作在 Redis 实例上执行具有互斥性。

并发访问控制对应的操作主要是数据修改操作。当客户端需要修改数据时,基本流程分成两步:

  1. 客户端先把数据读取到本地,在本地进行修改
  2. 客户端修改完数据后,再写回 Redis。

这个流程叫做“读取 - 修改 - 写回”操作(Read-Modify-Write,简称为 RMW 操作)。当有多个客户端对同一份数据执行 RMW 操作的话,我们就需要让 RMW 操作 涉及的代码以原子性方式执行。访问同一份数据的 RMW 操作 的代码,就叫做临界区代码。

不过,当有多个客户端并发执行临界区代码时,就会存在一些潜在问题。例如,客户端要对商品库存执行扣减 1 的操作,伪代码如下:

current = GET(id)
current--
SET(id, current)
  1. 可以看到,客户先根据商品 id,从 Redis 中读取商品当前的库存值 current(对应 Read)
  2. 然后,客户端对库存值减 1(对应 Modify)
  3. 再把库存值写回 Redis (对应 Write)

如果,我们对临界区代码的执行没有控制机制,就会出现数据更新错误。在刚才的例子中,假设现在有两个客户端 A 和 B,同时执行刚才的临界区代码,就会出现错误。
在这里插入图片描述

  1. 在客户端 A 在 t1 时读取库存值 10 并扣减 1
  2. 在 t2 时,客户端 A 还没有把扣减值后的库存值 9 写回 Redis。此时,客户端 B 读取到库存值 10,也扣减了 1, B 记录的库存值也为 9 了。
  3. 等到 T3 时刻, A 往 Redis 写回了库存值 9。
  4. T4 时刻,B 也写回了库存值 9。

如果按正确的逻辑处理,客户端 A 和 B 对库存值各做了一次扣减,库存值应该为 8.所以,这里的库存值明显更新错了。

出现这个现象的原因是,临界区代码中的客户端读取、更新、再写回涉及了三个操作,而这三个操作执行时并不具有互斥性,多个客户端基于相同的初始值进行修改,而不是基于前一个客户端修改后的值再修改。

前面,我们已经解释过,虽然加锁保证了互斥性,但是加锁也会导致系统并发性能降低。接下来,我们了解下 Redis 中的原子操作。

2.Redis 的两种原子操作方法

Redis 的原子操作采用了两种方法:

  1. 把多操作在 Redis 中实现成一个操作,也就是单命令操作。
  2. 把多个操作写到 Lua 脚本中,以原子性方式执行单个脚本。

单命令操作

Redis 使用单线程来串行处理客户端的请求命令,所以,当 Redis 执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的。

当然,Redis 的快照生成、AOF 重写这些操作可以使用后台线程或子进程执行,也就是和主线程的操作并行执行。不过他们都是只读操作,不会修改数据,所以,不需要对它们进行并发控制。

虽然 Redis 的单个命令可以原子性地执行,但是在实际应用中,数据修改时可能包含多个操作,至少包括读数据、数据增减、写回数据三个操作,这显然不是单个命令。

Redis 提供了 INCR/DECR 命令,把这三个操作变为一个原子操作了。INCR/DECR 命令可以对数据进行增值 / 减值操作,而它们本身就是单个命令操作,Redis 在执行它们时,本身就具有互斥性。

比如说,在刚才的库存扣减例子中,客户端可以使用下面的代码,也不用担心出现库存值扣减错误的问题。

DECR id

所以,如果我们执行的 RMW 操作对数据增减值的话, Redis 提供的原子操作 INCR 和 DECR 可以直接绑我们进行并发控制。

Lua 脚本

如果,我们要执行的操作不是简单地增减数据,而是有更新复杂的判断逻辑或者其他操作,那么 Redis 的单命令操作已经无法保证多个操作的互斥执行了。这个时候,就需要用到 Lua 脚本

Redis 会把整个 Lua 脚本 作为一个整体执行,在执行过程中不会被其他命令打断,从而保证了 Lua 脚本 中操作的原子性。如果,我们有多个操作要执行,但是又无法使用 INCR/DECR 这种命令操作来实现,就可以把这些要执行的命令编写到一个 Lua 脚本 中。然后,我们可以使用 Redis 的 EVAL 命令来执行脚本。这样一来,这些操作在执行的时就具有了互斥性。

我们可以把 访问次数加 1、判断访问次数是否为 1,以及设置过期时间这三个操作写入一个 Lua 脚本,如下所示:

local current
current = redis.call("incr", KEYS[1])
if tonumber(current) == 1 thenredis.call("expire", KEYS[1], 60)
end
return current

假设编写的脚本的名称为 lua.script,我们就接着使用 Redis 客户端,带上 eval 选项,来执行该脚本。脚本所需要的参数通过以下命令中的 key 和 arg 进行传递。

redis-cli --eval lua.script [key key2 ...] , [arg arg2 ...]

另外还可以在 Redis 客户端内,执行脚本:eval lua.script key-num [key key2 ...] [arg arg2 ...]

  • [key key2 ...]: 表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • [arg arg2 ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

例如上面的 Lua 脚本的实际执行如下(其中, KEYS[1] 为 lua1):

chenjian@DESKTOP-Q24SEP3:~$ vim lua.script
chenjian@DESKTOP-Q24SEP3:~$ ./redis-6.2.12/src/redis-cli --eval lua.script lua1 ,
(integer) 1
chenjian@DESKTOP-Q24SEP3:~$ ./redis-6.2.12/src/redis-cli --eval lua.script lua1 ,
(integer) 2

这样一来,访问次数加 1、判断访问次数是否为 1,以及设置过期时间这三个操作就可以原子性的执行了。即使客户端有多个线程同时执行这个脚本,Redis 也会依次串行的执行脚本代码,避免并发操作带来的数据错误。

不过需要注意的是,如果把很多操作都放在 Lua 脚本中原子执行,会导致 Redis 执行脚本的时间增加,同样也会降低 Redis 。所以,给你一个小建议:在编写 Lua 脚本时,你要避免把不需要做并发控制的操作写入 Lua 脚本中。

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

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

相关文章

MySQL-----DML基础操作

DML语句 DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进行增删改操作。 ▶ 添加数据(INSERT) 【语法】 1. 给指定字段添加数据 INSERTO 表名 (字段名1,字段名2,...) VALUES (值1,值2,...); 2.给全…

蓝桥杯---生日蜡烛

某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛,现在算起来,他一共吹熄了236根蜡烛。请问,他从多少岁开始过生日party的? 请填写他开始过生日 party的年龄数。 注意:你提交的应该是一个整数,不要…

区间时间检索

前端 <el-col :md"6" v-if"advanced"><el-form-item :label"$t(inRecord.column.createTime)"><el-date-pickerstyle"width: 100%;"v-model"daterangeCreateTime"value-format"yyyy-MM-dd"type&qu…

JavaScript鼠标拖放(Drag and Drop)

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 拖放是现代界面不可或缺的交互方式之一。本文将介绍如何用JavaScript…

界面组件DevExpress中文教程 - 如何使用UI本地化客户端工具本地化应用

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。 获取DevExpress v23.2正式版下载(Q技术交流&#xff1a;909157416&#xff09; 在2023年12月…

django+flask网上购物商城系统的设计与实现python-vue

全球经济在快速的发展&#xff0c;中国更是进步飞速&#xff0c;这使得国内的互联网技术进入了发展的高峰时期&#xff0c;这让中外资本不断转向互联网这个大市场[3]。在这个信息高度发达的现在&#xff0c;利用网络进行信息管理改革已经成为了人们追捧的一种趋势。“网上购物系…

华西建筑智能化团队助力建筑行业转型升级

华西建筑智能化团队全力推进建筑数字化转行。华西建筑智能化团队作为专业的机电安装及弱电智能化项目施工管理团队&#xff0c;先后实施了多个大型机电、智能化工程项目&#xff0c;包括&#xff1a;智慧医院项目、智能楼宇项目、机场/体育场馆/展馆等大型公共建筑及科研单位园…

sqli-labs-master靶场训练笔记(21-38|精英级)

2024.1.30 level-21 (cookie 注入数据加密) 从页面上就可以看出这次的数据被 baes64 加密了 中国有句古话&#xff1a;师夷长技以制夷 &#xff0c;用base64加密后的数据即可爆出数据 加密前&#xff1a; admin and updatexml(1,concat(~,(select database()),~),1) and …

搜索专项---Flood Fill

文章目录 池塘计数城堡问题山峰与山谷 一、池塘计数OJ链接 1.BFS做法 #include <bits/stdc.h>#define x first #define y secondtypedef std::pair<int,int> PII;constexpr int N1010;int n,m; char g[N][N]; bool st[N][N];//用来表示已经记录过的 std::queue&…

javaEE - 21( 15000字 Tomcat 和 HTTP 协议入门 -2)

一&#xff1a; HTTP 响应 1.1 认识 “状态码” (status code) 状态码表示访问一个页面的结果. (是访问成功, 还是失败, 还是其他的一些情况…)&#xff0c;以下为常见的状态码. 1.1.1 200 OK 这是一个最常见的状态码, 表示访问成功. 抓包抓到的大部分结果都是 200 HTTP/…

Unity引擎学习笔记之【混合动画操作】

混合动画Hybrid Animation Unity中的Blend Tree是一种动画混合技术&#xff0c;它允许开发者通过添加多个动画片段&#xff08;例如奔跑、行走、跳跃等&#xff09;来创建复杂的角色动画。Blend Tree允许在不同的状态下平滑地过渡并混合不同的动画。例如&#xff0c;在奔跑和行…

目标检测:2如何生成自己的数据集

目录 1. 数据采集 2. 图像标注 3. 开源已标记数据集 4. 数据集划分 参考&#xff1a; 1. 数据采集 数据采集是深度学习和人工智能任务中至关重要的一步&#xff0c;它为模型提供了必要的训练样本和测试数据。在实际应用中&#xff0c;数据采集的方法多种多样&#xff0c;每…

3.0 Hadoop 概念

本章着重介绍 Hadoop 中的概念和组成部分&#xff0c;属于理论章节。如果你比较着急可以跳过。但作者不建议跳过&#xff0c;因为它与后面的章节息息相关。 Hadoop 整体设计 Hadoop 框架是用于计算机集群大数据处理的框架&#xff0c;所以它必须是一个可以部署在多台计算机上…

Multisim14.0仿真(五十)基于CD4518的计数器设计

一、CD4518简介: CD4518是二、十进制(8421编码)同步加计数器,内含两个单元的加计数器。每单个单元有两个时钟输入端CLK和EN,可用时钟脉冲的上升沿或下降沿触发。可知,若用ENABLE信号下降沿触发,触发信号由EN端输入,CLK端置“0”;若用CL℃K信号上升沿触发,触发信号由C…

《Python 网络爬虫简易速速上手小册》第1章:Python 网络爬虫基础(2024 最新版)

文章目录 1.1 网络爬虫简介1.1.1 重点基础知识讲解1.1.2 重点案例&#xff1a;社交媒体数据分析1.1.3 拓展案例1&#xff1a;电商网站价格监控1.1.4 拓展案例2&#xff1a;新闻聚合服务 1.2 网络爬虫的工作原理1.2.1 重点基础知识讲解1.2.2 重点案例&#xff1a;股票市场数据采…

生物素 PEG4 甲基四嗪,Biotin-PEG4-methyltetrazine,用于标记、追踪和分离特定的分子或细胞

生物素四聚乙二醇甲基四嗪&#xff0c;生物素 PEG4 甲基四嗪&#xff0c;Biotin-PEG4-methyltetrazine&#xff0c;用于标记、追踪和分离特定的分子或细胞 您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;生物素四聚乙二醇甲基四嗪&#xff0c;生物素 PEG4 甲基四嗪…

2024三掌柜赠书活动第八期:Web3与DAO:下一代互联网演进逻辑

目录 前言关于Web3和DAO关于《Web3与DAO&#xff1a;下一代互联网演进逻辑》编辑推荐内容简介作者简介精彩书评图书目录书中前言/序言《Web3与DAO&#xff1a;下一代互联网演进逻辑》全书速览结束语 前言 随着区块链技术的崛起&#xff0c;Web3和DAO成为了当前互联网领域炙手…

QXlsx Qt操作excel

QXlsx 是一个用于处理Excel文件的开源C库。它允许你在你的C应用程序中读取和写入Microsoft Excel文件&#xff08;.xlsx格式&#xff09;。该库支持多种操作&#xff0c;包括创建新的工作簿、读取和写入单元格数据、格式化单元格、以及其他与Excel文件相关的功能。 支持跨平台…

云服务器也能挂游戏 安卓模拟器

安卓模拟器云服务器 什么是BlueStacks模拟器主机&#xff1f; 特网科技基于Windows操作系统预装了BlueStacks Android模拟器您能够通过Android模拟器安装Android应用程序、如APP游戏、安卓APP、APP游戏等。 我可以在主机上安装应用程序吗&#xff1f; 你可以在BlueStacks模…

斗破年番:七星斗宗地魔老鬼,首战吊打萧炎,毁灭莲逼出千百二老

Hello,小伙伴们&#xff0c;我是拾荒君。 国漫《斗破苍穹年番》第82期超前爆料&#xff0c;在万众瞩目之下&#xff0c;卡点帝再次展现了他的卡点救场技巧。此次&#xff0c;韩枫为了除掉萧炎&#xff0c;以他击杀魔炎谷四位长老为借口&#xff0c;请来了七品斗宗地魔老鬼。更…