Redis如何应对并发访问(redis控制并发量)
mhr18 2024-10-25 12:37 309 浏览 0 评论
Redis如何应对并发访问
Redis如果在业务中运用那么肯定需要考虑并发问题,如多个用户对同一个商品进行扣减,这时并发执行很可能导致商品数量不对,那么Redis如何来避免这些问题呢?一般分为两种解决方案分布式锁以及原子性操作。
对于分布式锁显然是可以解决上述问题的,但这不是最优因为分布式锁降低了系统的效率,还需要额外加锁解锁的操作,这里暂不讨论加锁这种方案。
那么对于原子性操作Redis如何实现呢?我们可以先思考并发访问中是对什么进行加锁呢?
以商品库存扣减为例,一般分为三个步骤
- 读取商品库存数量
- 将商品库存数量减一
- 将扣减后的数量写回Redis中
这个流程简称为RMW也就是读取-修改-写回,当进行这三个中的任何一步时我们都需要保证互斥,不允许其它用户读取到商品库存的中间状态,这样才能避免并发导致的库存不正确性。
Redis事务处理
那Redis如何保证RMW的互斥性呢?大多数人这里的第一想法应该是事务,事务去保证要不都成功要不都失败,Redis中的事务怎么玩呢?
redis中提供下面四种命令来保证事务执行
- MULTI:开启一个事务,执行后客户端可以向服务器发送任意多条命令,这些命令并不会立即执行,而是放到一个队列中,当执行EXEC命令才开始执行,当客户端处于事务状态,执行所有的命令会返回QUEUED。
- EXEC :执行事务。
- DISCARD :清空事务队列,并放弃事务。
- WATCH:类似于乐观锁,当监控的值在WATCH执行后,EXEC命令执行前,修改了该值,那么redis执行当前事务将失败。
简单演示事务操作
-- 开启事务
127.0.0.1:6379> MULTI
OK
-- 会将命令加入到事务队列中,后续exec统一执行
127.0.0.1:6379(TX)> set name zhangsan
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> set age 12
QUEUED
127.0.0.1:6379(TX)> DECR age
QUEUED
-- 执行事务
127.0.0.1:6379(TX)> EXEC
1) OK
2) "zhangsan"
3) OK
4) (integer) 11
丢弃事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set name zhangsan
QUEUED
-- 丢弃事务
127.0.0.1:6379(TX)> DISCARD
OK
-- get事务中添加的值,为空
127.0.0.1:6379> get name
(nil)
WATCH乐观锁
-- 客户端1,中添加age值为12
127.0.0.1:6379> set age 12
OK
-- 开启监控
127.0.0.1:6379> WATCH age
OK
127.0.0.1:6379> MULTI
127.0.0.1:6379(TX)> set name zhagnsan
QUEUED
-- 同时开启客户端2,将监控的age值改为11
127.0.0.1:6379(TX)> DECR age
QUEUED
-- 事务执行失败
127.0.0.1:6379(TX)> EXEC
(nil)
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> get age
"11"
事务能保证原子性吗?这是不确定的,一般我们熟知的事务遇到错误就会回滚,但Redis是分情况来讨论。
事务中的错误
Redis事务中的错误分为两种
- 事务在执行EXEC命令之前,入队命令出错,比如命令产生语法错误(参数错误、参数名错误等等),或者一些更加严重的错误如内存不足(Redis设置maxmemory最大内存限制的前提)。
- 命令在EXEC调用失败后,举个例子,事务命令可能处理了错误类型的键,列表命令用在字符串键上。
模拟EXEC命令执行之前出现错误
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set name zhangsan
QUEUED
127.0.0.1:6379(TX)> seta age 1
(error) ERR unknown command `seta`, with args beginning with: `age`, `1`,
-- EXEC执行前出现异常,放弃事务
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get name
(nil)
模拟EXEC命令执行之后出现错误
-- 开启事务前先设置一个String类型的键值
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> MULTI
OK
-- 采用hash方式获取string类型的键值,语法没错
127.0.0.1:6379(TX)> hget name 1
QUEUED
127.0.0.1:6379(TX)> set sex 2
QUEUED
127.0.0.1:6379(TX)> EXEC
-- 执行报错
1) (error) WRONGTYPE Operation against a key holding the wrong kind of value
2) OK
-- 事务没有放弃,执行部分事务
127.0.0.1:6379> get sex
"2"
所以,在EXEC执行之前的错误,Redis会将事务回滚,但是EXEC执行后产生的错误,Redis不会丢弃事务。因为Redis认为在EXEC执行后发生错误一般是语法错误,这个失败是编程造成的,这些错误在开发时就应该被发现,而不是应该出现在生产环境,而且不需要对回滚进行支持所以Redis能保持简单而且快速。
综上严格意义上来讲Redis的事务不支持原子性
既然Redis事务不能支持原子性,那么还能如何保证并发一致性呢?这里还有两种方案。
Redis两种原子操作方法
单命令操作
简单来讲就是将多个操作集成到一个命令上,因为Redis单命令操作是会阻塞主线程的,也就是说是互斥的,在命令执行过程中其它命令不会执行,这种命令形如INCR、DECR,例如库存扣减有三个步骤简称RMW,可以通过单命令DECR一次执行。
Lua脚本
Redis从2.6.0版本开始支持Lua脚本,Lua脚本的多样性一般是实现原子操作的最佳选择,Redis会把整个Lua脚本作为一个整体执行,在执行过程中不会被其他命令所打断,从而保证Lua脚本的原子性,如果我们存在多个操作要执行,又无法使用单命令操作实现,那么就可以试试Lua脚本。
Lua脚本简单演示
-- 形如命令 set name zhangsan
127.0.0.1:6379> EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
-- 形如命令 get name
127.0.0.1:6379> EVAL "return redis.call('get',KEYS[1])" 1 name
"zhangsan"
-- 清空预加载lua脚本
127.0.0.1:6379> SCRIPT FLUSH
OK
-- 加载脚本,可以自动生成一个字符串,不需要每次传输脚本,提升传输速度
127.0.0.1:6379> SCRIPT LOAD "return redis.call('set',KEYS[1],ARGV[1])"
"c686f316aaf1eb01d5a4de1b0b63cd233010e63d"
-- 执行预加载脚本
127.0.0.1:6379> EVALSHA "c686f316aaf1eb01d5a4de1b0b63cd233010e63d" 1 age 1
OK
127.0.0.1:6379> get age
"1"
-- 判断预加载脚本是否存在
127.0.0.1:6379> SCRIPT EXISTS "c686f316aaf1eb01d5a4de1b0b63cd233010e63d"
1) (integer) 1
相关推荐
- 订单超时自动取消业务的 N 种实现方案,从原理到落地全解析
-
在分布式系统架构中,订单超时自动取消机制是保障业务一致性的关键组件。某电商平台曾因超时处理机制缺陷导致日均3000+订单库存锁定异常,直接损失超50万元/天。本文将从技术原理、实现细节、...
- 使用Spring Boot 3开发时,如何选择合适的分布式技术?
-
作为互联网大厂的后端开发人员,当你满怀期待地用上SpringBoot3,准备在项目中大显身手时,却发现一个棘手的问题摆在面前:面对众多分布式技术,究竟该如何选择,才能让SpringBoot...
- 数据库内存爆满怎么办?99%的程序员都踩过这个坑!
-
你的数据库是不是又双叒叕内存爆满了?!服务器监控一片红色警告,老板在群里@所有人,运维同事的电话打爆了手机...这种场景是不是特别熟悉?别慌!作为一个在数据库优化这条路上摸爬滚打了10年的老司机,今天...
- springboot利用Redisson 实现缓存与数据库双写不一致问题
-
使用了Redisson来操作Redis分布式锁,主要功能是从缓存和数据库中获取商品信息,以下是针对并发时更新缓存和数据库带来不一致问题的解决方案1.基于读写锁和删除缓存策略在并发更新场景下,...
- 外贸独立站数据库炸了?对象缓存让你起死回生
-
上周黑五,一个客户眼睁睁看着服务器CPU飙到100%——每次页面加载要查87次数据库。这让我想起2024年Pantheon的测试:Redis缓存能把WooCommerce查询速度提升20倍。跨境电商最...
- 手把手教你在 Spring Boot3 里纯编码实现自定义分布式锁
-
为什么要自己实现分布式锁?你是不是早就受够了引入各种第三方依赖时的繁琐?尤其是分布式锁这块,每次集成Redisson或者Zookeeper,都得额外维护一堆配置,有时候还会因为版本兼容问题头疼半...
- 如何设计一个支持百万级实时数据推送的WebSocket集群架构?
-
面试解答:要设计一个支持百万级实时数据推送的WebSocket集群架构,需从**连接管理、负载均衡、水平扩展、容灾恢复**四个维度切入:连接层设计-**长连接优化**:采用Netty或Und...
- Redis数据结构总结——面试最常问到的知识点
-
Redis作为主流的nosql存储,面试时经常会问到。其主要场景是用作缓存,分布式锁,分布式session,消息队列,发布订阅等等。其存储结构主要有String,List,Set,Hash,Sort...
- skynet服务的缺陷 lua死循环
-
服务端高级架构—云风的skynet这边有一个关于云风skynet的视频推荐给大家观看点击就可以观看了!skynet是一套多人在线游戏的轻量级服务端框架,使用C+Lua开发。skynet的显著优点是,...
- 七年Java开发的一路辛酸史:分享面试京东、阿里、美团后的心得
-
前言我觉得有一个能够找一份大厂的offer的想法,这是很正常的,这并不是我们的饭后谈资而是每个技术人的追求。像阿里、腾讯、美团、字节跳动、京东等等的技术氛围与技术规范度还是要明显优于一些创业型公司...
- mysql mogodb es redis数据库之间的区别
-
1.MySQL应用场景概念:关系型数据库,基于关系模型,使用表和行存储数据。优点:支持ACID事务,数据具有很高的一致性和完整性。缺点:垂直扩展能力有限,需要分库分表等方式扩展。对于复杂的查询和大量的...
- redis,memcached,nginx网络组件
-
1.理解阻塞io,非阻塞io,同步io,异步io的区别2.理解BIO和AIO的区别io多路复用只负责io检测,不负责io操作阻塞io中的write,能写多少是多少,只要写成功就返回,譬如准备写500字...
- SpringBoot+Vue+Redis实现验证码功能
-
一个小时只允许发三次验证码。一次验证码有效期二分钟。SpringBoot整合Redis...
- AWS MemoryDB 可观测最佳实践
-
AWSMemoryDB介绍AmazonMemoryDB是一种完全托管的、内存中数据存储服务,专为需要极低延迟和高吞吐量的应用程序而设计。它与Redis和Memcached相似,但具有更...
- 从0构建大型AI推荐系统:实时化引擎从工具到生态的演进
-
在AI浪潮席卷各行各业的今天,推荐系统正从幕后走向前台,成为用户体验的核心驱动力。本文将带你深入探索一个大型AI推荐系统从零起步的全过程,揭示实时化引擎如何从单一工具演进为复杂生态的关键路径。无论你是...
你 发表评论:
欢迎- 一周热门
-
-
Redis客户端 Jedis 与 Lettuce
-
高并发架构系列:Redis并发竞争key的解决方案详解
-
redis如何防止并发(redis如何防止高并发)
-
Java SE Development Kit 8u441下载地址【windows版本】
-
开源推荐:如何实现的一个高性能 Redis 服务器
-
redis安装与调优部署文档(WinServer)
-
Redis 入门 - 安装最全讲解(Windows、Linux、Docker)
-
一文带你了解 Redis 的发布与订阅的底层原理
-
Redis如何应对并发访问(redis控制并发量)
-
Oracle如何创建用户,表空间(oracle19c创建表空间用户)
-
- 最近发表
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle主从同步 (56)
- oracle 乐观锁 (53)
- redis 命令 (83)
- php redis (97)
- redis 存储 (67)
- redis 锁 (74)
- 启动 redis (73)
- redis 时间 (60)
- redis 删除 (69)
- redis内存 (64)
- redis并发 (53)
- redis 主从 (71)
- redis同步 (53)
- redis结构 (53)
- redis 订阅 (54)
- redis 登录 (62)
- redis 面试 (58)
- redis问题 (54)
- 阿里 redis (67)
- redis的缓存 (57)
- lua redis (59)
- redis 连接池 (64)