redis学习笔记29——无锁的原子操作:redis如何应对并发访问(代码片段)

qq_34132502 qq_34132502     2022-12-12     465

关键词:

为了保证并发访问的正确性,Redis 提供了两种方法,分别是加锁和原子操作。

但是加锁会遇到两个问题:

  • 首先是加锁操作过多会降低系统的并发访问性能
  • 其次,Redis客户端要加锁时,需要使用分布式锁,而分布式锁实现复杂

原子操作是另一种提供并发访问控制的方法,实现了无锁操作。既可以保证并发控制,还能减少系统对并发性能的影响。

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

当客户端需要修改数据时,基本流程分成两步:

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

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

Redis的两种原子操作方法

为了实现并发控制要求的临界区代码互斥执行,Redis 的原子操作采用了两种方法:

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

单命令操作

Redis 提供了INCR/DECR命令,把RMW这三个操作转变为一个原子操作了。

INCR/DECR 命令可以对数据进行增值 / 减值操作,而且它们本身就是单个命令操作,Redis 在执行它们时,本身就具有互斥性。

比如,下面的伪代码显示了使用锁来控制临界区代码,对库存值做了一次扣减

LOCK()
current = GET(id)
current--
SET(id, current)
UNLOCK()

而使用单命令操作则如下:

DECR id

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

Lua脚本

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

Redis 会把整个 Lua 脚本作为一个整体执行,在执行的过程中不会被其他命令打断,从而保证了 Lua 脚本中操作的原子性。

比如,我们有时需要限制某个客户端在一定时间范围内的访问次数,在一分钟内只能访问一次。所以,这个例子中的操作无法用 Redis 单个命令来实现,此时,我们就可以使用 Lua 脚本来保证并发控制。我们可以把访问次数加 1、判断访问次数是否为 1,以及设置过期时间这三个操作写入一个 Lua 脚本,如下所示:

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

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

redis-cli  --eval lua.script  keys , args

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

Redis 的 Lua 脚本可以包含多个操作,这些操作都会以原子性的方式执行,绕开了单命令操作的限制。不过,如果把很多操作都放在 Lua 脚本中原子执行,会导致 Redis 执行脚本的时间增加,同样也会降低 Redis 的并发性能。所以,在编写 Lua 脚本时,你要避免把不需要做并发控制的操作写入脚本中

无锁的原子函数改变两个独立的内存位置

】无锁的原子函数改变两个独立的内存位置【英文标题】:Atomicfunctionwithoutlockstochangetwoindependentmemorylocations【发布时间】:2017-05-0109:02:28【问题描述】:我有一个名为updateEntry的函数,它将一个值写入查找表。我想创建这个函数... 查看详情

并发编程(学习笔记-共享模型之无锁)-part5(代码片段)

文章目录并发编程-5-共享模型之无锁1.无锁解决线程安全问题2.CAS与volatile2-1CAS2-2volatile2-3为什么无锁效率高2-4CAS特点3.原子整数4.原子引用4-1原子引用的使用4-2ABA问题及解决5.原子数组6.字段更新器7.原子累加器8.LongAdder详解8-1cas锁8... 查看详情

redis学习笔记--redis的认识和使用(代码片段)

什么是redis?    redis是基于key-value的内存存储系统,具有高性能,高可靠的特点。支持存储String、list、hash、set、zset等数据类型,这些数据都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且... 查看详情

redis学习笔记4:redis事务(代码片段)

事务:要么同时成功,要么同时失败。--->原子性Redis事务:单条命令保证原子性,但其事务不保证。(即在整一个事务中,正确的命令依然执行,错误的不执行。)1.正常开启Redis事务步骤:... 查看详情

redis学习笔记jedis(jediscluster)操作redis集群redis-cluster

...用spring-data-redis实现incr自增Redis利用Hash存储节约内存Redis学习笔记(九)redis实现时时直播列表缓存,支持分页[热点数据存储]Redis学习笔记(八)redis之lua脚本学习Redis学习笔记(七)jedis超时重试机制注意事... 查看详情

学习笔记——redis事务乐观锁悲观锁

2023-01-29一、redis事务与乐观锁相关命令1、redis事务(1)redis事务的含义redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序执行。事务在执行过程中,不会被其他客户端送来的命令请求所打断。(2)redis事务... 查看详情

redis的io多路复用和多线程特性会破坏分布式锁的原子性吗?(代码片段)

...冲突,可将所有客户端从Redis获取分布式锁,拿到锁的客户端才能操作共享资源。分布式锁实现的关键就是保证加锁、解锁都是原子操作,才能保证多个客户端访问时锁的正确性。而Redis能通过事件驱动框架同时捕获... 查看详情

redis的io多路复用和多线程特性会破坏分布式锁的原子性吗?(代码片段)

...冲突,可将所有客户端从Redis获取分布式锁,拿到锁的客户端才能操作共享资源。分布式锁实现的关键就是保证加锁、解锁都是原子操作,才能保证多个客户端访问时锁的正确性。而Redis能通过事件驱动框架同时捕获... 查看详情

day755.redis的原子操作-redis核心技术与实战(代码片段)

Redis的原子操作Hi,我是阿昌,今天学习记录的是关于Redis的原子操作的内容,感谢您。在使用Redis时,不可避免地会遇到并发访问的问题,比如说如果多个用户同时下单,就会对缓存在Redis中的商品库存并... 查看详情

redis学习笔记--redis的认识和使用(代码片段)

什么是redis?    redis是基于key-value的内存存储系统,具有高性能,高可靠的特点。支持存储String、list、hash、set、zset等数据类型,这些数据都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且... 查看详情

stackexchange.redis学习笔记事务控制和batch批量操作

Redis事物Redis命令实现事务Redis的事物包含在multi和exec(执行)或者discard(回滚)命令中和sql事务不同的是,Redis调用Exec只是将所有的命令变成一个单元一起执行,期间不会插入其他的命令。这种方式不保证事务的一致性,即使... 查看详情

尚硅谷redis学习笔记--redis持久化操作

Redis提供了2个不同形式的持久化方式。RDB(RedisDataBase)AOF(AppendOfFile)1.RDB(RedisDataBase)1.1RDB是什么在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是... 查看详情

redis学习基础概念摘抄(代码片段)

redis个人笔记学习redis学习redis是什么Redis简介redis的下载和配置redis的数据类型redis学习redis是什么REmoteDIctionaryServer(Redis)是一个由SalvatoreSanfilippo写的key-value存储系统,是跨平台的非关系型数据库。Redis是一个开源的使用ANSIC语... 查看详情

cas原子锁高效自旋无锁的正确用法

1#pragmaonce2#ifndef_atomic_lock_h_include_3#define_atomic_lock_h_include_45#include<windows.h>67#definecpu_pause()__asm{pause}8#definethread_yield()Yield()910#definespin_num(2048)1112typedefstr 查看详情

redis学习笔记—redis与lua(代码片段)

使用Lua的好处Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这些命令常驻在Redis内存中,实现复用的效果Lua脚本可以将多条命令一次... 查看详情

cas原子操作实现无锁及性能分析

Author:EchoChen(陈斌)Email:chenb19870707@gmail.comBlog:Blog.csdn.net/chen19870707Date:Nov13th,2014最近在研究nginx的自旋锁的时候,又见到了GCCCAS原子操作,于是决定动手分析下CA 查看详情

redis笔记redis中的事务

...:http://blog.csdn.net/Xiejingfa/article/details/50675302在【Redis学习笔记】前面几篇文章中,我们简单介绍了Redis中的五种数据结构相关的命令,今天,我们来介绍一下Redis中的事务Transaction。一、什么是事务如果你之前接触... 查看详情

redis笔记redis中的事务

...:http://blog.csdn.net/Xiejingfa/article/details/50675302在【Redis学习笔记】前面几篇文章中,我们简单介绍了Redis中的五种数据结构相关的命令,今天,我们来介绍一下Redis中的事务Transaction。一、什么是事务如果你之前接触... 查看详情