问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Redis进阶 - Redis的原子性操作

创作时间:
作者:
@小白创作中心

Redis进阶 - Redis的原子性操作

引用
1
来源
1.
https://juejin.cn/post/7384347818385915944

一、概述

1.1 原子性

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

加锁是一种常用的方法,在读取数据前,客户端需要先获得锁,否则就无法进行操作。当一个客户端获得锁后,就会一直持有这把锁,直到客户端完成数据更新,才释放这把锁。看上去好像是一种很好的方案,但是,其实这里会有两个问题:一个是如果加锁操作多,会降低系统的并发访问性能;第二个是 Redis 客户端要加锁时,需要用到分布式锁,而分布式锁实现复杂,需要用额外的存储系统来提供加解锁操作。

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

1.2 Redis 的原子操作

Redis的原子性操作指的是在执行过程中不会被其他操作打断,即操作要么完全执行成功,要么完全不执行,从而保证数据的一致性和可靠性。

二、Redis实现原子操作

Redis通过其内部机制和一些特定的命令来实现原子性操作,以下是Redis中一些常见的原子性操作及其描述。

2.1 单命令操作

Redis 是使用单线程来串行处理客户端的请求操作命令的,所以,当 Redis 执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的。以下这些操作在Redis中是原子性的,因为它们都是由Redis的单个命令执行的,不会被其他操作打断。

命令
说明
set
设置指定key的值。
get
获取指定key的值。
incr
对指定key的值进行增加操作。
decr
对指定key的值进行减少操作。
lpush
将一个或多个值插入到列表的头部。
rpush
将一个或多个值插入到列表的尾部。
lpop
从列表的头部移除并返回一个元素。
rpop
从列表的尾部移除并返回一个元素。
sadd
向集合中添加一个或多个元素。
srem
从集合中移除一个或多个元素。
hset
设置哈希表中指定字段的值。
hdel
删除哈希表中的一个或多个字段。
zadd
将一个或多个成员元素及其分数值添加到有序集合中。
zrem
从有序集合中移除一个或多个成员。

2.2 使用事务

Redis 支持将多个命令组合成一个事务来执行,通过MULTI、EXEC、WATCH和UNWATCH等命令来实现事务。其中,使用 MULTI 命令开始事务,然后依次执行多个命令,接着使用 EXEC 命令来按照顺序依次执行事务中的所有命令,WATCH 用于监视一个或多个键,当有其他客户端对被监视的键进行修改时,事务会被中断,UNWATCH 则用于取消监视。Redis事务的具体的实现步骤如下:

  1. 客户端向Redis发送MULTI命令,表示事务的开始。
  2. 服务器收到MULTI命令后,会将客户端的命令请求添加到一个队列中,而不是立即执行。
  3. 客户端继续发送多个命令请求,这些命令请求都会被添加到同一个队列中。
  4. 客户端发送EXEC命令,表示事务的执行。
  5. 服务器收到EXEC命令后,会依次执行队列中的命令。
  6. 服务器执行完所有命令后,将执行结果返回给客户端。

Redis 保证事务的原子性是通过将事务中的所有命令作为一个整体来执行,即在EXEC命令执行期间,不会处理其他客户端的命令请求。这样可以确保事务中的所有操作都是原子性的,如果事务中的任何一条命令失败,那么整个事务都会被回滚,即所有命令都不会被执行。

2.3 使用Lua脚本

Lua 本身并没有提供对于原子性的直接支持,它只是一种脚本语言,通常是嵌入到其他宿主程序中运行。Redis 2.6及更高版本支持使用Lua脚本来执行多个命令,并且这些命令在Lua脚本中是原子性执行的。这意味着在Lua脚本中执行的所有Redis命令都会作为一个整体来执行,不会被其他操作打断。Redis会在执行脚本期间锁住数据库,确保其他客户端的操作不会干扰。

2.4 特定命令

你可能也注意到了,虽然 Redis 的单个命令操作可以原子性地执行,但是在实际应用中,数据修改时可能包含多个操作,至少包括读数据、数据增减、写回数据三个操作,这显然就不是单个命令操作了,那该怎么办呢?别担心,Redis 提供了几种命令

命令
说明
INCR
对数据进行增值操作
DECR
对数据进行减值操作
SETNX
用于设置一个键的值,但仅在该键不存在时才执行。这是一个原子性操作,它会检查键是否存在,并在不存在时设置值。
GETSET
用于设置一个键的新值,并返回该键的旧值。这是一个原子性操作,它在设置新值的同时返回旧值,且这个过程不会被其他操作打断。

三、结语

总之,Redis通过其内部机制、事务、Lua脚本和特定命令等方式来支持原子性操作,从而确保数据的一致性和可靠性。需要注意的是,虽然Redis提供了一些原子性操作的机制,但并不是所有的操作都是原子的。例如,修改一个字符串类型的值时,如果使用GET和SET命令分别获取和修改,可能会导致中间发生了其他操作,从而导致不是原子性的。因此,在实现原子操作时,需要根据具体情况选择合适的方式。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号